mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 18:24:16 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c8826a337 | |||
| 9fccc27741 | |||
| b0021d42b5 | |||
| 5598d75fd5 | |||
| 48bb1373e1 | |||
| 8fce2fc67f | |||
| f7acd1e819 | |||
| b46dc664c6 | |||
| 18b4c698fc | |||
| 62c300f285 | |||
| d8e974429d | |||
| 634955660d | |||
| 1e9e068aec | |||
| 87ecfdbc72 | |||
| 2f1881bbe7 | |||
| 1e60ac5ba2 | |||
| cd2752a525 | |||
| 66b10f19c2 |
@@ -7,72 +7,38 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linux-build:
|
linux-build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: "recursive"
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
env:
|
env:
|
||||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
run: |
|
run: |
|
||||||
echo ${#beammp_sentry_url}
|
echo ${#beammp_sentry_url}
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev cmake g++-10
|
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
run: cmake -E make_directory ${{github.workspace}}/build-linux
|
run: cmake -E make_directory ${{github.workspace}}/build-linux
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
env:
|
env:
|
||||||
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||||
|
|
||||||
- name: Build Server
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server --parallel
|
run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server --parallel
|
||||||
|
|
||||||
- name: Build Tests
|
- name: Archive artifacts
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
uses: actions/upload-artifact@v2
|
||||||
shell: bash
|
with:
|
||||||
run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server-tests --parallel
|
name: BeamMP-Server-linux
|
||||||
|
path: ${{github.workspace}}/build-linux/BeamMP-Server
|
||||||
- name: Archive server artifact
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: BeamMP-Server-linux
|
|
||||||
path: ${{github.workspace}}/build-linux/BeamMP-Server
|
|
||||||
|
|
||||||
- name: Archive test artifact
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: BeamMP-Server-linux-tests
|
|
||||||
path: ${{github.workspace}}/build-linux/BeamMP-Server-tests
|
|
||||||
|
|
||||||
run-tests:
|
|
||||||
needs: linux-build
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/download-artifact@master
|
|
||||||
with:
|
|
||||||
name: BeamMP-Server-linux-tests
|
|
||||||
path: ${{github.workspace}}
|
|
||||||
|
|
||||||
- name: Install Runtime Dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y liblua5.3 openssl
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
working-directory: ${{github.workspace}}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
chmod +x ./BeamMP-Server-tests
|
|
||||||
./BeamMP-Server-tests
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cmake --build . --config Debug
|
cmake --build . --config Debug -t BeamMP-Server --parallel
|
||||||
|
|
||||||
- name: Archive debug artifacts
|
- name: Archive debug artifacts
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake --build . --config $BUILD_TYPE
|
run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server --parallel
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload Release Asset
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake --build . --config $BUILD_TYPE
|
run: cmake --build . --config $BUILD_TYPE -t BeamMP-Server --parallel
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload Release Asset
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
|
|||||||
+3
-6
@@ -1,6 +1,9 @@
|
|||||||
[submodule "deps/commandline"]
|
[submodule "deps/commandline"]
|
||||||
path = deps/commandline
|
path = deps/commandline
|
||||||
url = https://github.com/lionkor/commandline
|
url = https://github.com/lionkor/commandline
|
||||||
|
[submodule "deps/fmt"]
|
||||||
|
path = deps/fmt
|
||||||
|
url = https://github.com/fmtlib/fmt
|
||||||
[submodule "deps/asio"]
|
[submodule "deps/asio"]
|
||||||
path = deps/asio
|
path = deps/asio
|
||||||
url = https://github.com/chriskohlhoff/asio
|
url = https://github.com/chriskohlhoff/asio
|
||||||
@@ -25,9 +28,3 @@
|
|||||||
[submodule "deps/json"]
|
[submodule "deps/json"]
|
||||||
path = deps/json
|
path = deps/json
|
||||||
url = https://github.com/nlohmann/json
|
url = https://github.com/nlohmann/json
|
||||||
[submodule "deps/fmt"]
|
|
||||||
path = deps/fmt
|
|
||||||
url = https://github.com/fmtlib/fmt
|
|
||||||
[submodule "deps/doctest"]
|
|
||||||
path = deps/doctest
|
|
||||||
url = https://github.com/doctest/doctest
|
|
||||||
|
|||||||
+56
-101
@@ -26,16 +26,17 @@ endif()
|
|||||||
set(HTTPLIB_REQUIRE_OPENSSL ON)
|
set(HTTPLIB_REQUIRE_OPENSSL ON)
|
||||||
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/asio/asio/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/rapidjson/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/websocketpp")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/commandline")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/sol2/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/cpp-httplib")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps/json/single_include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/toml11")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/deps")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/json/single_include")
|
||||||
|
include_directories("${PROJECT_SOURCE_DIR}/deps")
|
||||||
|
|
||||||
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1)
|
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
|
|
||||||
# ------------------------ APPLE ---------------------------------
|
# ------------------------ APPLE ---------------------------------
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
@@ -50,12 +51,18 @@ elseif (WIN32)
|
|||||||
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
||||||
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
|
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
|
||||||
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
|
||||||
|
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||||
|
endif ()
|
||||||
|
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
||||||
|
include_directories(${VcpkgRoot}/include)
|
||||||
|
link_directories(${VcpkgRoot}/lib)
|
||||||
# ------------------------ LINUX ---------------------------------
|
# ------------------------ LINUX ---------------------------------
|
||||||
elseif (UNIX)
|
elseif (UNIX)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++ -static-libgcc")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++ -static-libgcc")
|
||||||
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 -fno-builtin")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fno-builtin")
|
||||||
option(SANITIZE "Turns on thread and UB sanitizers" OFF)
|
|
||||||
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_FLAGS} -fsanitize=undefined,thread")
|
||||||
@@ -63,7 +70,7 @@ elseif (UNIX)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
include_directories("include/sentry-native/include")
|
include_directories("include/sentry-native/include")
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
|
||||||
# ------------------------ SENTRY ---------------------------------
|
# ------------------------ SENTRY ---------------------------------
|
||||||
message(STATUS "Checking for Sentry URL")
|
message(STATUS "Checking for Sentry URL")
|
||||||
# this is set by the build system.
|
# this is set by the build system.
|
||||||
@@ -80,62 +87,57 @@ add_subdirectory("deps/sentry-native")
|
|||||||
|
|
||||||
# ------------------------ C++ SETUP ---------------------------------
|
# ------------------------ C++ SETUP ---------------------------------
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
if (MSVC)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# ------------------------ DEPENDENCIES ------------------------------
|
# ------------------------ DEPENDENCIES ------------------------------
|
||||||
message(STATUS "Adding local source dependencies")
|
message(STATUS "Adding local source dependencies")
|
||||||
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
||||||
add_subdirectory(deps)
|
add_subdirectory(deps)
|
||||||
|
|
||||||
# ------------------------ VARIABLES ---------------------------------
|
# ------------------------ BEAMMP SERVER -----------------------------
|
||||||
|
|
||||||
|
add_executable(BeamMP-Server
|
||||||
|
src/main.cpp
|
||||||
|
include/TConsole.h src/TConsole.cpp
|
||||||
|
include/TServer.h src/TServer.cpp
|
||||||
|
include/Compat.h src/Compat.cpp
|
||||||
|
include/Common.h src/Common.cpp
|
||||||
|
include/Client.h src/Client.cpp
|
||||||
|
include/VehicleData.h src/VehicleData.cpp
|
||||||
|
include/TConfig.h src/TConfig.cpp
|
||||||
|
include/TLuaEngine.h src/TLuaEngine.cpp
|
||||||
|
include/TLuaPlugin.h src/TLuaPlugin.cpp
|
||||||
|
include/TResourceManager.h src/TResourceManager.cpp
|
||||||
|
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
||||||
|
include/Http.h src/Http.cpp
|
||||||
|
include/TSentry.h src/TSentry.cpp
|
||||||
|
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
||||||
|
include/TNetwork.h src/TNetwork.cpp
|
||||||
|
include/LuaAPI.h src/LuaAPI.cpp
|
||||||
|
include/TScopedTimer.h src/TScopedTimer.cpp
|
||||||
|
include/SignalHandling.h src/SignalHandling.cpp
|
||||||
|
include/ArgsParser.h src/ArgsParser.cpp
|
||||||
|
include/Environment.h)
|
||||||
|
|
||||||
|
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||||
|
include_directories(BeamMP-Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
target_include_directories(BeamMP-Server PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||||
|
|
||||||
include(FindLua)
|
include(FindLua)
|
||||||
include(FindOpenSSL)
|
include(FindOpenSSL)
|
||||||
include(FindThreads)
|
include(FindThreads)
|
||||||
include(FindZLIB)
|
include(FindZLIB)
|
||||||
|
|
||||||
set(BeamMP_Sources
|
target_include_directories(BeamMP-Server PUBLIC
|
||||||
include/TConsole.h src/TConsole.cpp
|
${LUA_INCLUDE_DIR}
|
||||||
include/TServer.h src/TServer.cpp
|
|
||||||
include/Compat.h src/Compat.cpp
|
|
||||||
include/Common.h src/Common.cpp
|
|
||||||
include/Client.h src/Client.cpp
|
|
||||||
include/VehicleData.h src/VehicleData.cpp
|
|
||||||
include/TConfig.h src/TConfig.cpp
|
|
||||||
include/TLuaEngine.h src/TLuaEngine.cpp
|
|
||||||
include/TLuaPlugin.h src/TLuaPlugin.cpp
|
|
||||||
include/TResourceManager.h src/TResourceManager.cpp
|
|
||||||
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
|
||||||
include/Http.h src/Http.cpp
|
|
||||||
include/TSentry.h src/TSentry.cpp
|
|
||||||
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
|
||||||
include/TNetwork.h src/TNetwork.cpp
|
|
||||||
include/LuaAPI.h src/LuaAPI.cpp
|
|
||||||
include/TScopedTimer.h src/TScopedTimer.cpp
|
|
||||||
include/SignalHandling.h src/SignalHandling.cpp
|
|
||||||
include/ArgsParser.h src/ArgsParser.cpp
|
|
||||||
include/TPluginMonitor.h src/TPluginMonitor.cpp
|
|
||||||
include/Environment.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(BeamMP_Includes
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/commandline"
|
|
||||||
${LUA_INCLUDE_DIR}
|
|
||||||
${CURL_INCLUDE_DIRS}
|
${CURL_INCLUDE_DIRS}
|
||||||
"include/tomlplusplus"
|
"include/tomlplusplus"
|
||||||
"include/sentry-native/include"
|
"include/sentry-native/include"
|
||||||
"include/curl/include"
|
"include/curl/include")
|
||||||
)
|
|
||||||
|
|
||||||
set(BeamMP_Definitions
|
target_link_libraries(BeamMP-Server
|
||||||
SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(BeamMP_Libraries
|
|
||||||
doctest::doctest
|
|
||||||
OpenSSL::SSL
|
OpenSSL::SSL
|
||||||
OpenSSL::Crypto
|
OpenSSL::Crypto
|
||||||
sol2::sol2
|
sol2::sol2
|
||||||
@@ -144,55 +146,8 @@ set(BeamMP_Libraries
|
|||||||
ZLIB::ZLIB
|
ZLIB::ZLIB
|
||||||
${LUA_LIBRARIES}
|
${LUA_LIBRARIES}
|
||||||
commandline
|
commandline
|
||||||
sentry
|
sentry)
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(BeamMP_PlatformLibs wsock32 ws2_32)
|
target_link_libraries(BeamMP-Server wsock32 ws2_32)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# ------------------------ BEAMMP SERVER -----------------------------
|
|
||||||
|
|
||||||
add_executable(BeamMP-Server
|
|
||||||
src/main.cpp
|
|
||||||
${BeamMP_Sources}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(BeamMP-Server PRIVATE
|
|
||||||
${BeamMP_Definitions}
|
|
||||||
DOCTEST_CONFIG_DISABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(BeamMP-Server PUBLIC
|
|
||||||
${BeamMP_Includes}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(BeamMP-Server
|
|
||||||
${BeamMP_Libraries}
|
|
||||||
${BeamMP_PlatformLibs}
|
|
||||||
)
|
|
||||||
|
|
||||||
# ------------------------ BEAMMP SERVER TESTS -----------------------
|
|
||||||
|
|
||||||
option(BUILD_TESTS "Build BeamMP-Server tests" ON)
|
|
||||||
|
|
||||||
if(BUILD_TESTS)
|
|
||||||
add_executable(BeamMP-Server-tests
|
|
||||||
test/test_main.cpp
|
|
||||||
${BeamMP_Sources}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(BeamMP-Server-tests PRIVATE
|
|
||||||
${BeamMP_Definitions}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(BeamMP-Server-tests PUBLIC
|
|
||||||
${BeamMP_Includes}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(BeamMP-Server-tests
|
|
||||||
${BeamMP_Libraries}
|
|
||||||
${BeamMP_PlatformLibs}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
|
|
||||||
# v3.1.0
|
# v3.1.0
|
||||||
|
|
||||||
- ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console
|
- ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console
|
||||||
- ADDED lua debug facilities (type `:help` when attached to lua via `lua`)
|
- ADDED lua debug facilities (type `:help` when attached to lua via `lua`)
|
||||||
- ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa
|
- ADDED MP.JsonEncode() and MP.JsonDecode(), which turn lua tables into json and vice-versa
|
||||||
- ADDED FS.ListFiles and FS.ListDirectories
|
- ADDED FS.ListFiles and FS.ListDirectories
|
||||||
- ADDED onFileChanged event, triggered when a server plugin file changes
|
|
||||||
- FIXED `ip` in MP.GetIdentifiers
|
|
||||||
- FIXED issue with client->server events which contain ':'
|
- FIXED issue with client->server events which contain ':'
|
||||||
- FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink
|
|
||||||
- FIXED onInit not being called on hot-reload
|
|
||||||
- FIXED incorrect timing calculation of Lua EventTimer loop
|
|
||||||
- FIXED bug which caused hot-reload not to report syntax errors
|
|
||||||
- FIXED missing error messages on some event handler calls
|
|
||||||
|
|
||||||
# v3.0.2
|
# v3.0.2
|
||||||
|
|
||||||
@@ -23,7 +15,6 @@
|
|||||||
- FIXED `MP.CreateEventTimer` filling up the queue (see <https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302>)
|
- FIXED `MP.CreateEventTimer` filling up the queue (see <https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302>)
|
||||||
- FIXED `MP.TriggerClientEvent` not kicking the client if it failed
|
- FIXED `MP.TriggerClientEvent` not kicking the client if it failed
|
||||||
- FIXED Lua result queue handling not checking all results
|
- FIXED Lua result queue handling not checking all results
|
||||||
- FIXED bug which caused ServerConfig.toml to generate incorrectly
|
|
||||||
|
|
||||||
# v3.0.1
|
# v3.0.1
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ On windows, use git-bash for these commands. On Linux, these should work in your
|
|||||||
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`.
|
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/v3.0.1` for version 3.0.1. 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/v2.3.3` for version 2.3.3. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
|
||||||
5. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`)
|
5. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`)
|
||||||
6. Run `make`
|
6. Run `make`
|
||||||
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps")
|
include_directories("${PROJECT_SOURCE_DIR}/deps")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
|
||||||
|
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/commandline")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/commandline")
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/fmt")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/fmt")
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/sol2")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/sol2")
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/deps/doctest")
|
|
||||||
|
|||||||
Vendored
+1
-1
Submodule deps/cpp-httplib updated: 47044c05a8...b324921c1a
Vendored
-1
Submodule deps/doctest deleted from 7b98851331
Vendored
+1
-1
Submodule deps/fmt updated: ce246aaf74...17dda58391
Vendored
+1
-1
Submodule deps/json updated: ede6667858...eb21824147
Vendored
+1
-1
Submodule deps/toml11 updated: 1400dd223f...fda0a2b9ab
+79
-121
@@ -12,14 +12,9 @@ extern TSentry Sentry;
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
#include <doctest/doctest.h>
|
|
||||||
#include <filesystem>
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
#include "TConsole.h"
|
#include "TConsole.h"
|
||||||
@@ -47,8 +42,6 @@ public:
|
|||||||
std::string Resource { "Resources" };
|
std::string Resource { "Resources" };
|
||||||
std::string MapName { "/levels/gridmap_v2/info.json" };
|
std::string MapName { "/levels/gridmap_v2/info.json" };
|
||||||
std::string Key {};
|
std::string Key {};
|
||||||
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
|
||||||
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
|
||||||
bool HTTPServerEnabled { false };
|
bool HTTPServerEnabled { false };
|
||||||
int MaxPlayers { 8 };
|
int MaxPlayers { 8 };
|
||||||
bool Private { true };
|
bool Private { true };
|
||||||
@@ -97,8 +90,6 @@ public:
|
|||||||
static void CheckForUpdates();
|
static void CheckForUpdates();
|
||||||
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||||
static bool IsOutdated(const Version& Current, const Version& Newest);
|
static bool IsOutdated(const Version& Current, const Version& Newest);
|
||||||
static bool IsShuttingDown();
|
|
||||||
static void SleepSafeSeconds(size_t Seconds);
|
|
||||||
|
|
||||||
static void InitializeConsole() {
|
static void InitializeConsole() {
|
||||||
if (!mConsole) {
|
if (!mConsole) {
|
||||||
@@ -124,14 +115,10 @@ public:
|
|||||||
static void SetSubsystemStatus(const std::string& Subsystem, Status status);
|
static void SetSubsystemStatus(const std::string& Subsystem, Status status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void SetShutdown(bool Val);
|
|
||||||
|
|
||||||
static inline SystemStatusMap mSystemStatusMap {};
|
static inline SystemStatusMap mSystemStatusMap {};
|
||||||
static inline std::mutex mSystemStatusMapMutex {};
|
static inline std::mutex mSystemStatusMapMutex {};
|
||||||
static inline std::string mPPS;
|
static inline std::string mPPS;
|
||||||
static inline std::unique_ptr<TConsole> mConsole;
|
static inline std::unique_ptr<TConsole> mConsole;
|
||||||
static inline std::shared_mutex mShutdownMtx {};
|
|
||||||
static inline bool mShutdown { false };
|
|
||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
@@ -142,126 +129,97 @@ std::string ThreadName(bool DebugModeOverride = false);
|
|||||||
void RegisterThread(const std::string& str);
|
void RegisterThread(const std::string& str);
|
||||||
#define RegisterThreadAuto() RegisterThread(__func__)
|
#define RegisterThreadAuto() RegisterThread(__func__)
|
||||||
|
|
||||||
#define KB 1024llu
|
#define KB 1024
|
||||||
#define MB (KB * 1024llu)
|
#define MB (KB * 1024)
|
||||||
#define GB (MB * 1024llu)
|
|
||||||
#define SSU_UNRAW SECRET_SENTRY_URL
|
#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__)
|
||||||
#define _in_lambda (std::string(__func__) == "operator()")
|
#define _in_lambda (std::string(__func__) == "operator()")
|
||||||
|
|
||||||
|
// we would like the full function signature 'void a::foo() const'
|
||||||
|
// on windows this is __FUNCSIG__, on GCC it's __PRETTY_FUNCTION__,
|
||||||
|
// feel free to add more
|
||||||
|
#if defined(WIN32)
|
||||||
|
#define _function_name std::string(__FUNCSIG__)
|
||||||
|
#elif defined(__unix) || defined(__unix__)
|
||||||
|
#define _function_name std::string(__PRETTY_FUNCTION__)
|
||||||
|
#else
|
||||||
|
#define _function_name std::string(__func__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG)
|
||||||
|
|
||||||
|
// if this is defined, we will show the full function signature infront of
|
||||||
|
// each info/debug/warn... call instead of the 'filename:line' format.
|
||||||
|
#if defined(BMP_FULL_FUNCTION_NAMES)
|
||||||
|
#define _this_location (ThreadName() + _function_name + " ")
|
||||||
|
#else
|
||||||
|
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
|
||||||
|
#endif
|
||||||
|
#define SU_RAW SSU_UNRAW
|
||||||
|
|
||||||
|
#else // !defined(DEBUG)
|
||||||
|
|
||||||
|
#define SU_RAW RAWIFY(SSU_UNRAW)
|
||||||
|
#define _this_location (ThreadName())
|
||||||
|
|
||||||
|
#endif // defined(DEBUG)
|
||||||
|
|
||||||
|
#define beammp_warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
||||||
|
#define beammp_info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
||||||
|
#define beammp_error(x) \
|
||||||
|
do { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \
|
||||||
|
Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \
|
||||||
|
} while (false)
|
||||||
|
#define beammp_lua_error(x) \
|
||||||
|
do { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[LUA ERROR] ") + (x)); \
|
||||||
|
} while (false)
|
||||||
|
#define beammp_lua_warn(x) \
|
||||||
|
do { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[LUA WARN] ") + (x)); \
|
||||||
|
} while (false)
|
||||||
|
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
|
||||||
|
#define beammp_debug(x) \
|
||||||
|
do { \
|
||||||
|
if (Application::Settings.DebugModeEnabled) { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
#define beammp_event(x) \
|
||||||
|
do { \
|
||||||
|
if (Application::Settings.DebugModeEnabled) { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
// for those times when you just need to ignore something :^)
|
// for those times when you just need to ignore something :^)
|
||||||
// explicity disables a [[nodiscard]] warning
|
// explicity disables a [[nodiscard]] warning
|
||||||
#define beammp_ignore(x) (void)x
|
#define beammp_ignore(x) (void)x
|
||||||
|
// trace() is a debug-build debug()
|
||||||
// clang-format off
|
|
||||||
#ifdef DOCTEST_CONFIG_DISABLE
|
|
||||||
|
|
||||||
// we would like the full function signature 'void a::foo() const'
|
|
||||||
// on windows this is __FUNCSIG__, on GCC it's __PRETTY_FUNCTION__,
|
|
||||||
// feel free to add more
|
|
||||||
#if defined(WIN32)
|
|
||||||
#define _function_name std::string(__FUNCSIG__)
|
|
||||||
#elif defined(__unix) || defined(__unix__)
|
|
||||||
#define _function_name std::string(__PRETTY_FUNCTION__)
|
|
||||||
#else
|
|
||||||
#define _function_name std::string(__func__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define DEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(DEBUG)
|
|
||||||
|
|
||||||
// if this is defined, we will show the full function signature infront of
|
|
||||||
// each info/debug/warn... call instead of the 'filename:line' format.
|
|
||||||
#if defined(BMP_FULL_FUNCTION_NAMES)
|
|
||||||
#define _this_location (ThreadName() + _function_name + " ")
|
|
||||||
#else
|
|
||||||
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // defined(DEBUG)
|
|
||||||
|
|
||||||
#define beammp_warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
|
||||||
#define beammp_info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
|
||||||
#define beammp_error(x) \
|
|
||||||
do { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \
|
|
||||||
Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \
|
|
||||||
} while (false)
|
|
||||||
#define beammp_lua_error(x) \
|
|
||||||
do { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[LUA ERROR] ") + (x)); \
|
|
||||||
} while (false)
|
|
||||||
#define beammp_lua_warn(x) \
|
|
||||||
do { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[LUA WARN] ") + (x)); \
|
|
||||||
} while (false)
|
|
||||||
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
|
|
||||||
#define beammp_debug(x) \
|
|
||||||
do { \
|
|
||||||
if (Application::Settings.DebugModeEnabled) { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
|
|
||||||
} \
|
|
||||||
} while (false)
|
|
||||||
#define beammp_event(x) \
|
|
||||||
do { \
|
|
||||||
if (Application::Settings.DebugModeEnabled) { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[EVENT] ") + (x)); \
|
|
||||||
} \
|
|
||||||
} while (false)
|
|
||||||
// trace() is a debug-build debug()
|
|
||||||
#if defined(DEBUG)
|
|
||||||
#define beammp_trace(x) \
|
|
||||||
do { \
|
|
||||||
if (Application::Settings.DebugModeEnabled) { \
|
|
||||||
Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \
|
|
||||||
} \
|
|
||||||
} while (false)
|
|
||||||
#else
|
|
||||||
#define beammp_trace(x)
|
|
||||||
#endif // defined(DEBUG)
|
|
||||||
|
|
||||||
#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_warnf(...) beammp_warn(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_tracef(...) beammp_trace(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_lua_errorf(...) beammp_lua_error(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_lua_warnf(...) beammp_lua_warn(fmt::format(__VA_ARGS__))
|
|
||||||
|
|
||||||
#else // DOCTEST_CONFIG_DISABLE
|
|
||||||
|
|
||||||
#define beammp_error(x) /* x */
|
|
||||||
#define beammp_lua_error(x) /* x */
|
|
||||||
#define beammp_warn(x) /* x */
|
|
||||||
#define beammp_lua_warn(x) /* x */
|
|
||||||
#define beammp_info(x) /* x */
|
|
||||||
#define beammp_event(x) /* x */
|
|
||||||
#define beammp_debug(x) /* x */
|
|
||||||
#define beammp_trace(x) /* x */
|
|
||||||
#define luaprint(x) /* x */
|
|
||||||
#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_warnf(...) beammp_warn(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_tracef(...) beammp_trace(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_lua_errorf(...) beammp_lua_error(fmt::format(__VA_ARGS__))
|
|
||||||
#define beammp_lua_warnf(...) beammp_lua_warn(fmt::format(__VA_ARGS__))
|
|
||||||
|
|
||||||
#endif // DOCTEST_CONFIG_DISABLE
|
|
||||||
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
#define SU_RAW SSU_UNRAW
|
#define beammp_trace(x) \
|
||||||
|
do { \
|
||||||
|
if (Application::Settings.DebugModeEnabled) { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
#else
|
#else
|
||||||
#define SU_RAW RAWIFY(SSU_UNRAW)
|
#define beammp_trace(x)
|
||||||
#define _this_location (ThreadName())
|
#endif // defined(DEBUG)
|
||||||
#endif
|
|
||||||
|
|
||||||
// clang-format on
|
#define beammp_errorf(...) beammp_error(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_infof(...) beammp_info(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_warnf(...) beammp_warn(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_debugf(...) beammp_debug(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_tracef(...) beammp_trace(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_lua_errorf(...) beammp_lua_error(fmt::format(__VA_ARGS__))
|
||||||
|
#define beammp_lua_warnf(...) beammp_lua_warn(fmt::format(__VA_ARGS__))
|
||||||
|
|
||||||
void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
class TServer;
|
||||||
|
class TNetwork;
|
||||||
|
class TResourceManager;
|
||||||
|
|
||||||
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, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
|
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
|
||||||
@@ -28,13 +32,19 @@ const std::string ErrorString = "-1";
|
|||||||
namespace Server {
|
namespace Server {
|
||||||
class THttpServerInstance {
|
class THttpServerInstance {
|
||||||
public:
|
public:
|
||||||
THttpServerInstance();
|
THttpServerInstance(TServer&, TNetwork&, TResourceManager&);
|
||||||
|
~THttpServerInstance();
|
||||||
|
static fs::path KeyFilePath;
|
||||||
|
static fs::path CertFilePath;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void operator()();
|
void operator()();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
|
TServer& mServer;
|
||||||
|
TNetwork& mNetwork;
|
||||||
|
TResourceManager& mResourceManager;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
||||||
#include <toml11/toml.hpp> // header-only version of TOML++
|
#include <toml.hpp> // header-only version of TOML++
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
void FlushToFile();
|
void FlushToFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateConfigFile();
|
void CreateConfigFile(std::string_view name);
|
||||||
void ParseFromFile(std::string_view name);
|
void ParseFromFile(std::string_view name);
|
||||||
void PrintDebug();
|
void PrintDebug();
|
||||||
void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue);
|
void TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue);
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ private:
|
|||||||
void Command_List(const std::string& cmd, const std::vector<std::string>& args);
|
void Command_List(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
void Command_Status(const std::string& cmd, const std::vector<std::string>& args);
|
void Command_Status(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
|
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
void Command_Clear(const std::string&, const std::vector<std::string>& args);
|
|
||||||
|
|
||||||
void Command_Say(const std::string& FullCommand);
|
void Command_Say(const std::string& FullCommand);
|
||||||
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
|
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
|
||||||
@@ -51,7 +50,6 @@ private:
|
|||||||
{ "list", [this](const auto& a, const auto& b) { Command_List(a, b); } },
|
{ "list", [this](const auto& a, const auto& b) { Command_List(a, b); } },
|
||||||
{ "status", [this](const auto& a, const auto& b) { Command_Status(a, b); } },
|
{ "status", [this](const auto& a, const auto& b) { Command_Status(a, b); } },
|
||||||
{ "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } },
|
{ "settings", [this](const auto& a, const auto& b) { Command_Settings(a, b); } },
|
||||||
{ "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } },
|
|
||||||
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
|
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ private:
|
|||||||
std::string GenerateCall();
|
std::string GenerateCall();
|
||||||
std::string GetPlayers();
|
std::string GetPlayers();
|
||||||
|
|
||||||
|
bool mShutdown = false;
|
||||||
TResourceManager& mResourceManager;
|
TResourceManager& mResourceManager;
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
};
|
};
|
||||||
|
|||||||
+21
-21
@@ -13,7 +13,7 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <toml11/toml.hpp>
|
#include <toml.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ struct TLuaPluginConfig {
|
|||||||
static inline const std::string FileName = "PluginConfig.toml";
|
static inline const std::string FileName = "PluginConfig.toml";
|
||||||
TLuaStateId StateId;
|
TLuaStateId StateId;
|
||||||
// TODO: Add execute list
|
// TODO: Add execute list
|
||||||
// TODO: Build a better toml serializer, or some way to do this in an easier way
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TLuaChunk {
|
struct TLuaChunk {
|
||||||
@@ -60,7 +59,20 @@ struct TLuaChunk {
|
|||||||
std::string PluginPath;
|
std::string PluginPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TLuaEngine : public std::enable_shared_from_this<TLuaEngine>, IThreaded {
|
class TPluginMonitor : IThreaded {
|
||||||
|
public:
|
||||||
|
TPluginMonitor(const fs::path& Path, TLuaEngine& Engine, std::atomic_bool& Shutdown);
|
||||||
|
|
||||||
|
void operator()();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TLuaEngine& mEngine;
|
||||||
|
fs::path mPath;
|
||||||
|
std::atomic_bool& mShutdown;
|
||||||
|
std::unordered_map<std::string, fs::file_time_type> mFileTimes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TLuaEngine : IThreaded {
|
||||||
public:
|
public:
|
||||||
enum CallStrategy : int {
|
enum CallStrategy : int {
|
||||||
BestEffort,
|
BestEffort,
|
||||||
@@ -119,6 +131,7 @@ public:
|
|||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
||||||
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
|
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
|
||||||
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
|
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
|
||||||
|
template <typename... ArgsT>
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means
|
* @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means
|
||||||
@@ -127,7 +140,6 @@ public:
|
|||||||
* @param Args
|
* @param Args
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
template <typename... ArgsT>
|
|
||||||
[[nodiscard]] std::vector<std::shared_ptr<TLuaResult>> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) {
|
[[nodiscard]] std::vector<std::shared_ptr<TLuaResult>> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) {
|
||||||
std::unique_lock Lock(mLuaEventsMutex);
|
std::unique_lock Lock(mLuaEventsMutex);
|
||||||
beammp_event(EventName);
|
beammp_event(EventName);
|
||||||
@@ -147,21 +159,6 @@ public:
|
|||||||
}
|
}
|
||||||
return Results; //
|
return Results; //
|
||||||
}
|
}
|
||||||
template <typename... ArgsT>
|
|
||||||
[[nodiscard]] std::vector<std::shared_ptr<TLuaResult>> TriggerLocalEvent(const TLuaStateId& StateId, const std::string& EventName, ArgsT&&... Args) {
|
|
||||||
std::unique_lock Lock(mLuaEventsMutex);
|
|
||||||
beammp_event(EventName + " in '" + StateId + "'");
|
|
||||||
if (mLuaEvents.find(EventName) == mLuaEvents.end()) { // if no event handler is defined for 'EventName', return immediately
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::vector<std::shared_ptr<TLuaResult>> Results;
|
|
||||||
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... };
|
|
||||||
const auto Handlers = GetEventHandlersForState(EventName, StateId);
|
|
||||||
for (const auto& Handler : Handlers) {
|
|
||||||
Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments));
|
|
||||||
}
|
|
||||||
return Results;
|
|
||||||
}
|
|
||||||
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
||||||
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy);
|
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy);
|
||||||
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
||||||
@@ -187,7 +184,7 @@ private:
|
|||||||
|
|
||||||
class StateThreadData : IThreaded {
|
class StateThreadData : IThreaded {
|
||||||
public:
|
public:
|
||||||
StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine);
|
StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine);
|
||||||
StateThreadData(const StateThreadData&) = delete;
|
StateThreadData(const StateThreadData&) = delete;
|
||||||
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
||||||
@@ -218,6 +215,7 @@ private:
|
|||||||
sol::table Lua_FS_ListDirectories(const std::string& Path);
|
sol::table Lua_FS_ListDirectories(const std::string& Path);
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
std::atomic_bool& mShutdown;
|
||||||
TLuaStateId mStateId;
|
TLuaStateId mStateId;
|
||||||
lua_State* mState;
|
lua_State* mState;
|
||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
@@ -246,7 +244,9 @@ private:
|
|||||||
|
|
||||||
TNetwork* mNetwork;
|
TNetwork* mNetwork;
|
||||||
TServer* mServer;
|
TServer* mServer;
|
||||||
const fs::path mResourceServerPath;
|
TPluginMonitor mPluginMonitor;
|
||||||
|
std::atomic_bool mShutdown { false };
|
||||||
|
fs::path mResourceServerPath;
|
||||||
std::vector<std::shared_ptr<TLuaPlugin>> mLuaPlugins;
|
std::vector<std::shared_ptr<TLuaPlugin>> mLuaPlugins;
|
||||||
std::unordered_map<TLuaStateId, std::unique_ptr<StateThreadData>> mLuaStates;
|
std::unordered_map<TLuaStateId, std::unique_ptr<StateThreadData>> mLuaStates;
|
||||||
std::recursive_mutex mLuaStatesMutex;
|
std::recursive_mutex mLuaStatesMutex;
|
||||||
|
|||||||
+1
-1
@@ -32,6 +32,7 @@ private:
|
|||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
TPPSMonitor& mPPSMonitor;
|
TPPSMonitor& mPPSMonitor;
|
||||||
SOCKET mUDPSock {};
|
SOCKET mUDPSock {};
|
||||||
|
bool mShutdown { false };
|
||||||
TResourceManager& mResourceManager;
|
TResourceManager& mResourceManager;
|
||||||
std::thread mUDPThread;
|
std::thread mUDPThread;
|
||||||
std::thread mTCPThread;
|
std::thread mTCPThread;
|
||||||
@@ -47,5 +48,4 @@ private:
|
|||||||
void SendFile(TClient& c, const std::string& Name);
|
void SendFile(TClient& c, const std::string& Name);
|
||||||
static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size);
|
static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size);
|
||||||
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
|
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
|
||||||
static uint8_t* SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ private:
|
|||||||
|
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
std::optional<std::reference_wrapper<TNetwork>> mNetwork { std::nullopt };
|
std::optional<std::reference_wrapper<TNetwork>> mNetwork { std::nullopt };
|
||||||
|
bool mShutdown { false };
|
||||||
int mInternalPPS { 0 };
|
int mInternalPPS { 0 };
|
||||||
};
|
};
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Common.h"
|
|
||||||
#include "IThreaded.h"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class TLuaEngine;
|
|
||||||
|
|
||||||
class TPluginMonitor : IThreaded, public std::enable_shared_from_this<TPluginMonitor> {
|
|
||||||
public:
|
|
||||||
TPluginMonitor(const fs::path& Path, std::shared_ptr<TLuaEngine> Engine);
|
|
||||||
|
|
||||||
void operator()();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<TLuaEngine> mEngine;
|
|
||||||
fs::path mPath;
|
|
||||||
std::unordered_map<std::string, fs::file_time_type> mFileTimes;
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
class TVehicleData final {
|
class TVehicleData final {
|
||||||
public:
|
public:
|
||||||
TVehicleData(int ID, std::string Data);
|
TVehicleData(int ID, const std::string& Data);
|
||||||
~TVehicleData();
|
~TVehicleData();
|
||||||
// We cannot delete this, since vector needs to be able to copy when it resizes.
|
// We cannot delete this, since vector needs to be able to copy when it resizes.
|
||||||
// Deleting this causes some wacky template errors which are hard to decipher,
|
// Deleting this causes some wacky template errors which are hard to decipher,
|
||||||
@@ -19,9 +22,12 @@ public:
|
|||||||
|
|
||||||
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
||||||
|
|
||||||
|
const json& Json() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mID { -1 };
|
int mID { -1 };
|
||||||
std::string mData;
|
std::string mData;
|
||||||
|
json mJson;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: unused now, remove?
|
// TODO: unused now, remove?
|
||||||
|
|||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
openapi: "3.0.2"
|
||||||
|
info:
|
||||||
|
title: BeamMP-Server
|
||||||
|
version: "api-v1"
|
||||||
|
description: |
|
||||||
|
The BeamMP-Server optionally runs an HTTP server
|
||||||
|
which can be used to query information about the server's
|
||||||
|
status, health, players, etc.
|
||||||
|
servers:
|
||||||
|
- url: http://localhost:{port}/api/v1
|
||||||
|
description: BeamMP-Server API v1
|
||||||
|
variables:
|
||||||
|
port:
|
||||||
|
default: "8000"
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/player/{name}/vehicles:
|
||||||
|
parameters:
|
||||||
|
- name: "name"
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: "LionKor"
|
||||||
|
get:
|
||||||
|
summary: vehicles this player has spawend
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"application/json":
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
jbm:
|
||||||
|
type: string
|
||||||
|
example: "bolide"
|
||||||
|
/version:
|
||||||
|
get:
|
||||||
|
summary: server version
|
||||||
|
description: |
|
||||||
|
Server version in 'major.minor.patch' semver format
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"text/plain":
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: "3.1.0"
|
||||||
|
/players:
|
||||||
|
get:
|
||||||
|
summary: list of players on this server
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"application/json":
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
example:
|
||||||
|
- "LionKor"
|
||||||
|
- "Titch"
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
/config:
|
||||||
|
get:
|
||||||
|
summary: describes server configuration
|
||||||
|
description: |
|
||||||
|
An overview over the configuration currently
|
||||||
|
used by this server.
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"application/json":
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: "my awesome beammp server"
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
example: "join!"
|
||||||
|
max_players:
|
||||||
|
type: integer
|
||||||
|
example: 7
|
||||||
|
max_cars:
|
||||||
|
type: integer
|
||||||
|
example: 2
|
||||||
|
map:
|
||||||
|
type: string
|
||||||
|
example: "/levels/gridmap_v2/info.json"
|
||||||
|
private:
|
||||||
|
type: boolean
|
||||||
|
/ready:
|
||||||
|
get:
|
||||||
|
summary: whether the server has started fully
|
||||||
|
description: |
|
||||||
|
True once all subsystems have started up.
|
||||||
|
This doesn't tell you whether they're healthy,
|
||||||
|
check /health for that information.
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"text/plain":
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
/health:
|
||||||
|
get:
|
||||||
|
summary: health of the server's systems
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
"application/json":
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
healthy:
|
||||||
|
type: boolean
|
||||||
|
good:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "Heartbeat"
|
||||||
|
bad:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "ResourceManager"
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "ArgsParser.h"
|
#include "ArgsParser.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <doctest/doctest.h>
|
|
||||||
|
|
||||||
void ArgsParser::Parse(const std::vector<std::string_view>& ArgList) {
|
void ArgsParser::Parse(const std::vector<std::string_view>& ArgList) {
|
||||||
for (const auto& Arg : ArgList) {
|
for (const auto& Arg : ArgList) {
|
||||||
@@ -93,78 +92,3 @@ void ArgsParser::ConsumeLongFlag(const std::string& Arg) {
|
|||||||
beammp_warn("Argument '" + Name + "' was supplied but isn't a known argument, so it is likely being ignored.");
|
beammp_warn("Argument '" + Name + "' was supplied but isn't a known argument, so it is likely being ignored.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ArgsParser") {
|
|
||||||
ArgsParser parser;
|
|
||||||
|
|
||||||
SUBCASE("Simple args") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::NONE);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE);
|
|
||||||
parser.Parse({ "--a", "--hello" });
|
|
||||||
CHECK(parser.Verify());
|
|
||||||
CHECK(parser.FoundArgument({ "a" }));
|
|
||||||
CHECK(parser.FoundArgument({ "hello" }));
|
|
||||||
CHECK(parser.FoundArgument({ "a", "hello" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "b" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "goodbye" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("No args") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::NONE);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE);
|
|
||||||
parser.Parse({});
|
|
||||||
CHECK(parser.Verify());
|
|
||||||
CHECK(!parser.FoundArgument({ "a" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "hello" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "a", "hello" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "b" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "goodbye" }));
|
|
||||||
CHECK(!parser.FoundArgument({ "" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Value args") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::HAS_VALUE);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::HAS_VALUE);
|
|
||||||
parser.Parse({ "--a=5", "--hello=world" });
|
|
||||||
CHECK(parser.Verify());
|
|
||||||
REQUIRE(parser.FoundArgument({ "a" }));
|
|
||||||
REQUIRE(parser.FoundArgument({ "hello" }));
|
|
||||||
CHECK(parser.GetValueOfArgument({ "a" }).has_value());
|
|
||||||
CHECK(parser.GetValueOfArgument({ "a" }).value() == "5");
|
|
||||||
CHECK(parser.GetValueOfArgument({ "hello" }).has_value());
|
|
||||||
CHECK(parser.GetValueOfArgument({ "hello" }).value() == "world");
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Mixed value & no-value args") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::HAS_VALUE);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::NONE);
|
|
||||||
parser.Parse({ "--a=5", "--hello" });
|
|
||||||
CHECK(parser.Verify());
|
|
||||||
REQUIRE(parser.FoundArgument({ "a" }));
|
|
||||||
REQUIRE(parser.FoundArgument({ "hello" }));
|
|
||||||
CHECK(parser.GetValueOfArgument({ "a" }).has_value());
|
|
||||||
CHECK(parser.GetValueOfArgument({ "a" }).value() == "5");
|
|
||||||
CHECK(!parser.GetValueOfArgument({ "hello" }).has_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
SUBCASE("Required args") {
|
|
||||||
SUBCASE("Two required, two present") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.Parse({ "--a", "--hello" });
|
|
||||||
CHECK(parser.Verify());
|
|
||||||
}
|
|
||||||
SUBCASE("Two required, one present") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.Parse({ "--a" });
|
|
||||||
CHECK(!parser.Verify());
|
|
||||||
}
|
|
||||||
SUBCASE("Two required, none present") {
|
|
||||||
parser.RegisterArgument({ "a" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.RegisterArgument({ "hello" }, ArgsParser::Flags::REQUIRED);
|
|
||||||
parser.Parse({ "--b" });
|
|
||||||
CHECK(!parser.Verify());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
-126
@@ -12,9 +12,6 @@
|
|||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
|
|
||||||
// global, yes, this is ugly, no, it cant be done another way
|
|
||||||
TSentry Sentry {};
|
|
||||||
|
|
||||||
Application::TSettings Application::Settings = {};
|
Application::TSettings Application::Settings = {};
|
||||||
|
|
||||||
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||||
@@ -25,7 +22,6 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::GracefullyShutdown() {
|
void Application::GracefullyShutdown() {
|
||||||
SetShutdown(true);
|
|
||||||
static bool AlreadyShuttingDown = false;
|
static bool AlreadyShuttingDown = false;
|
||||||
static uint8_t ShutdownAttempts = 0;
|
static uint8_t ShutdownAttempts = 0;
|
||||||
if (AlreadyShuttingDown) {
|
if (AlreadyShuttingDown) {
|
||||||
@@ -65,23 +61,6 @@ std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
|
|||||||
return Version;
|
return Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Application::VersionStrToInts") {
|
|
||||||
auto v = Application::VersionStrToInts("1.2.3");
|
|
||||||
CHECK(v[0] == 1);
|
|
||||||
CHECK(v[1] == 2);
|
|
||||||
CHECK(v[2] == 3);
|
|
||||||
|
|
||||||
v = Application::VersionStrToInts("10.20.30");
|
|
||||||
CHECK(v[0] == 10);
|
|
||||||
CHECK(v[1] == 20);
|
|
||||||
CHECK(v[2] == 30);
|
|
||||||
|
|
||||||
v = Application::VersionStrToInts("100.200.255");
|
|
||||||
CHECK(v[0] == 100);
|
|
||||||
CHECK(v[1] == 200);
|
|
||||||
CHECK(v[2] == 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Application::IsOutdated(const Version& Current, const Version& Newest) {
|
bool Application::IsOutdated(const Version& Current, const Version& Newest) {
|
||||||
if (Newest.major > Current.major) {
|
if (Newest.major > Current.major) {
|
||||||
return true;
|
return true;
|
||||||
@@ -94,65 +73,6 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::IsShuttingDown() {
|
|
||||||
std::shared_lock Lock(mShutdownMtx);
|
|
||||||
return mShutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::SleepSafeSeconds(size_t Seconds) {
|
|
||||||
// Sleeps for 500 ms, checks if a shutdown occurred, and so forth
|
|
||||||
for (size_t i = 0; i < Seconds * 2; ++i) {
|
|
||||||
if (Application::IsShuttingDown()) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Application::IsOutdated (version check)") {
|
|
||||||
SUBCASE("Same version") {
|
|
||||||
CHECK(!Application::IsOutdated({ 1, 2, 3 }, { 1, 2, 3 }));
|
|
||||||
}
|
|
||||||
// we need to use over 1-2 digits to test against lexical comparisons
|
|
||||||
SUBCASE("Patch outdated") {
|
|
||||||
for (uint8_t Patch = 0; Patch < 10; ++Patch) {
|
|
||||||
for (uint8_t Minor = 0; Minor < 10; ++Minor) {
|
|
||||||
for (uint8_t Major = 0; Major < 10; ++Major) {
|
|
||||||
CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major), uint8_t(Minor), uint8_t(Patch + 1) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SUBCASE("Minor outdated") {
|
|
||||||
for (uint8_t Patch = 0; Patch < 10; ++Patch) {
|
|
||||||
for (uint8_t Minor = 0; Minor < 10; ++Minor) {
|
|
||||||
for (uint8_t Major = 0; Major < 10; ++Major) {
|
|
||||||
CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major), uint8_t(Minor + 1), uint8_t(Patch) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SUBCASE("Major outdated") {
|
|
||||||
for (uint8_t Patch = 0; Patch < 10; ++Patch) {
|
|
||||||
for (uint8_t Minor = 0; Minor < 10; ++Minor) {
|
|
||||||
for (uint8_t Major = 0; Major < 10; ++Major) {
|
|
||||||
CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major + 1), uint8_t(Minor), uint8_t(Patch) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SUBCASE("All outdated") {
|
|
||||||
for (uint8_t Patch = 0; Patch < 10; ++Patch) {
|
|
||||||
for (uint8_t Minor = 0; Minor < 10; ++Minor) {
|
|
||||||
for (uint8_t Major = 0; Major < 10; ++Major) {
|
|
||||||
CHECK(Application::IsOutdated({ uint8_t(Major), uint8_t(Minor), uint8_t(Patch) }, { uint8_t(Major + 1), uint8_t(Minor + 1), uint8_t(Patch + 1) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::SetSubsystemStatus(const std::string& Subsystem, Status status) {
|
void Application::SetSubsystemStatus(const std::string& Subsystem, Status status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case Status::Good:
|
case Status::Good:
|
||||||
@@ -175,20 +95,6 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status
|
|||||||
mSystemStatusMap[Subsystem] = status;
|
mSystemStatusMap[Subsystem] = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::SetShutdown(bool Val) {
|
|
||||||
std::unique_lock Lock(mShutdownMtx);
|
|
||||||
mShutdown = Val;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Application::SetSubsystemStatus") {
|
|
||||||
Application::SetSubsystemStatus("Test", Application::Status::Good);
|
|
||||||
auto Map = Application::GetSubsystemStatuses();
|
|
||||||
CHECK(Map.at("Test") == Application::Status::Good);
|
|
||||||
Application::SetSubsystemStatus("Test", Application::Status::Bad);
|
|
||||||
Map = Application::GetSubsystemStatuses();
|
|
||||||
CHECK(Map.at("Test") == Application::Status::Bad);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::CheckForUpdates() {
|
void Application::CheckForUpdates() {
|
||||||
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
|
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
|
||||||
static bool FirstTime = true;
|
static bool FirstTime = true;
|
||||||
@@ -246,25 +152,6 @@ std::string ThreadName(bool DebugModeOverride) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ThreadName") {
|
|
||||||
RegisterThread("MyThread");
|
|
||||||
auto OrigDebug = Application::Settings.DebugModeEnabled;
|
|
||||||
|
|
||||||
// ThreadName adds a space at the end, legacy but we need it still
|
|
||||||
SUBCASE("Debug mode enabled") {
|
|
||||||
Application::Settings.DebugModeEnabled = true;
|
|
||||||
CHECK(ThreadName(true) == "MyThread ");
|
|
||||||
CHECK(ThreadName(false) == "MyThread ");
|
|
||||||
}
|
|
||||||
SUBCASE("Debug mode disabled") {
|
|
||||||
Application::Settings.DebugModeEnabled = false;
|
|
||||||
CHECK(ThreadName(true) == "MyThread ");
|
|
||||||
CHECK(ThreadName(false) == "");
|
|
||||||
}
|
|
||||||
// cleanup
|
|
||||||
Application::Settings.DebugModeEnabled = OrigDebug;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegisterThread(const std::string& str) {
|
void RegisterThread(const std::string& str) {
|
||||||
std::string ThreadId;
|
std::string ThreadId;
|
||||||
#ifdef BEAMMP_WINDOWS
|
#ifdef BEAMMP_WINDOWS
|
||||||
@@ -282,11 +169,6 @@ void RegisterThread(const std::string& str) {
|
|||||||
threadNameMap[std::this_thread::get_id()] = str;
|
threadNameMap[std::this_thread::get_id()] = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("RegisterThread") {
|
|
||||||
RegisterThread("MyThread");
|
|
||||||
CHECK(threadNameMap.at(std::this_thread::get_id()) == "MyThread");
|
|
||||||
}
|
|
||||||
|
|
||||||
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
|
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
|
||||||
: major(major)
|
: major(major)
|
||||||
, minor(minor)
|
, minor(minor)
|
||||||
@@ -300,12 +182,6 @@ std::string Version::AsString() {
|
|||||||
return fmt::format("{:d}.{:d}.{:d}", major, minor, patch);
|
return fmt::format("{:d}.{:d}.{:d}", major, minor, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Version::AsString") {
|
|
||||||
CHECK(Version { 0, 0, 0 }.AsString() == "0.0.0");
|
|
||||||
CHECK(Version { 1, 2, 3 }.AsString() == "1.2.3");
|
|
||||||
CHECK(Version { 255, 255, 255 }.AsString() == "255.255.255");
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||||
if (Application::Settings.LogChat) {
|
if (Application::Settings.LogChat) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@@ -317,9 +193,7 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
|||||||
ss << name << "";
|
ss << name << "";
|
||||||
}
|
}
|
||||||
ss << msg;
|
ss << msg;
|
||||||
#ifdef DOCTEST_CONFIG_DISABLE
|
|
||||||
Application::Console().Write(ss.str());
|
Application::Console().Write(ss.str());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
#include "Compat.h"
|
#include "Compat.h"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <doctest/doctest.h>
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
|
|
||||||
static struct termios old, current;
|
static struct termios old, current;
|
||||||
@@ -23,23 +20,6 @@ void resetTermios(void) {
|
|||||||
tcsetattr(0, TCSANOW, &old);
|
tcsetattr(0, TCSANOW, &old);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("init and reset termios") {
|
|
||||||
if (isatty(STDIN_FILENO)) {
|
|
||||||
struct termios original;
|
|
||||||
tcgetattr(0, &original);
|
|
||||||
SUBCASE("no echo") {
|
|
||||||
initTermios(false);
|
|
||||||
}
|
|
||||||
SUBCASE("yes echo") {
|
|
||||||
initTermios(true);
|
|
||||||
}
|
|
||||||
resetTermios();
|
|
||||||
struct termios current;
|
|
||||||
tcgetattr(0, ¤t);
|
|
||||||
CHECK(std::memcmp(&original, ¤t, sizeof(struct termios)) == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char getch_(int echo) {
|
char getch_(int echo) {
|
||||||
char ch;
|
char ch;
|
||||||
initTermios(echo);
|
initTermios(echo);
|
||||||
|
|||||||
+120
-17
@@ -4,6 +4,9 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
|
#include "TNetwork.h"
|
||||||
|
#include "TResourceManager.h"
|
||||||
|
#include "TServer.h"
|
||||||
#include "httplib.h"
|
#include "httplib.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -142,29 +145,115 @@ std::string Http::Status::ToString(int Code) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Http::Status::ToString") {
|
#define API_V1 "/api/v1"
|
||||||
CHECK(Http::Status::ToString(200) == "OK");
|
|
||||||
CHECK(Http::Status::ToString(696969) == "696969");
|
|
||||||
CHECK(Http::Status::ToString(-1) == "Invalid Response Code");
|
|
||||||
}
|
|
||||||
|
|
||||||
Http::Server::THttpServerInstance::THttpServerInstance() {
|
Http::Server::THttpServerInstance::THttpServerInstance(TServer& Server, TNetwork& Network, TResourceManager& ResMan)
|
||||||
|
: mServer(Server)
|
||||||
|
, mNetwork(Network)
|
||||||
|
, mResourceManager(ResMan) {
|
||||||
Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting);
|
Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting);
|
||||||
mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this);
|
mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::Server::THttpServerInstance::~THttpServerInstance() {
|
||||||
mThread.detach();
|
mThread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::Server::THttpServerInstance::operator()() try {
|
void Http::Server::THttpServerInstance::operator()() try {
|
||||||
beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort));
|
beammp_infof("HTTP Server starting on [{}]:{}", Application::Settings.HTTPServerIP, Application::Settings.HTTPServerPort);
|
||||||
std::unique_ptr<httplib::Server> HttpLibServerInstance;
|
if (Application::Settings.Private
|
||||||
HttpLibServerInstance = std::make_unique<httplib::Server>();
|
&& Application::Settings.HTTPServerIP != "::"
|
||||||
// todo: make this IP agnostic so people can set their own IP
|
&& Application::Settings.HTTPServerIP != "localhost"
|
||||||
HttpLibServerInstance->Get("/", [](const httplib::Request&, httplib::Response& res) {
|
&& Application::Settings.HTTPServerIP != "127.0.0.1") {
|
||||||
res.set_content("<!DOCTYPE html><article><h1>Hello World!</h1><section><p>BeamMP Server can now serve HTTP requests!</p></section></article></html>", "text/html");
|
beammp_warnf("HTTP Server is running on a possibly public IP ({}), but the server is private. The server's HTTP API supports viewing config, players, and other information, even though the server is private. (this is not an error)", Application::Settings.HTTPServerIP);
|
||||||
|
}
|
||||||
|
std::unique_ptr<httplib::Server> Server = std::make_unique<httplib::Server>();
|
||||||
|
|
||||||
|
Server->Get(API_V1 R"(/player/(.+)/vehicles)", [this](const httplib::Request& Req, httplib::Response& Res) {
|
||||||
|
if (Req.matches.empty()) {
|
||||||
|
Res.status = 404;
|
||||||
|
} else {
|
||||||
|
const std::string PlayerName = Req.matches[1];
|
||||||
|
bool Found = false;
|
||||||
|
json Result = json::array();
|
||||||
|
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) {
|
||||||
|
if (!ClientPtr.expired()) {
|
||||||
|
const auto Client = ClientPtr.lock();
|
||||||
|
if (Client->GetName() == PlayerName) {
|
||||||
|
const auto [Cars, Lock] = Client->GetAllCars();
|
||||||
|
for (const auto& Car : *Cars) {
|
||||||
|
Result.emplace_back().at("jbm") = Car.Json().at("jbm");
|
||||||
|
}
|
||||||
|
Found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (!Found) {
|
||||||
|
Res.status = 404;
|
||||||
|
} else {
|
||||||
|
Res.status = 200;
|
||||||
|
Res.set_content(Result.dump(), "application/json");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
HttpLibServerInstance->Get("/health", [](const httplib::Request&, httplib::Response& res) {
|
|
||||||
|
Server->Get(API_V1 "/players", [this](const httplib::Request&, httplib::Response& res) {
|
||||||
|
res.status = 200;
|
||||||
|
json Players = json::array();
|
||||||
|
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) {
|
||||||
|
if (!ClientPtr.expired()) {
|
||||||
|
Players.push_back(ClientPtr.lock()->GetName());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
res.set_content(Players.dump(),
|
||||||
|
"application/json");
|
||||||
|
});
|
||||||
|
|
||||||
|
Server->Get(API_V1 "/version", [](const httplib::Request&, httplib::Response& res) {
|
||||||
|
res.status = 200;
|
||||||
|
res.set_content(Application::ServerVersionString(), "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
Server->Get(API_V1 "/config", [](const httplib::Request&, httplib::Response& res) {
|
||||||
|
res.status = 200;
|
||||||
|
res.set_content(json {
|
||||||
|
{ "name", Application::Settings.ServerName },
|
||||||
|
{ "description", Application::Settings.ServerDesc },
|
||||||
|
{ "max_players", Application::Settings.MaxPlayers },
|
||||||
|
{ "max_cars", Application::Settings.MaxCars },
|
||||||
|
{ "map", Application::Settings.MapName },
|
||||||
|
{ "private", Application::Settings.Private },
|
||||||
|
}
|
||||||
|
.dump(),
|
||||||
|
"application/json");
|
||||||
|
});
|
||||||
|
|
||||||
|
Server->Get(API_V1 "/ready", [](const httplib::Request&, httplib::Response& res) {
|
||||||
|
auto Statuses = Application::GetSubsystemStatuses();
|
||||||
|
bool Started = true;
|
||||||
|
for (const auto& NameStatusPair : Statuses) {
|
||||||
|
switch (NameStatusPair.second) {
|
||||||
|
case Application::Status::Starting:
|
||||||
|
case Application::Status::ShuttingDown:
|
||||||
|
case Application::Status::Shutdown:
|
||||||
|
Started = false;
|
||||||
|
break;
|
||||||
|
case Application::Status::Good:
|
||||||
|
case Application::Status::Bad:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.status = 200;
|
||||||
|
res.set_content(Started ? "true" : "false", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
Server->Get(API_V1 "/health", [](const httplib::Request&, httplib::Response& res) {
|
||||||
size_t SystemsGood = 0;
|
size_t SystemsGood = 0;
|
||||||
size_t SystemsBad = 0;
|
size_t SystemsBad = 0;
|
||||||
|
json Good = json::array();
|
||||||
|
json Bad = json::array();
|
||||||
auto Statuses = Application::GetSubsystemStatuses();
|
auto Statuses = Application::GetSubsystemStatuses();
|
||||||
for (const auto& NameStatusPair : Statuses) {
|
for (const auto& NameStatusPair : Statuses) {
|
||||||
switch (NameStatusPair.second) {
|
switch (NameStatusPair.second) {
|
||||||
@@ -172,30 +261,44 @@ void Http::Server::THttpServerInstance::operator()() try {
|
|||||||
case Application::Status::ShuttingDown:
|
case Application::Status::ShuttingDown:
|
||||||
case Application::Status::Shutdown:
|
case Application::Status::Shutdown:
|
||||||
case Application::Status::Good:
|
case Application::Status::Good:
|
||||||
|
Good.push_back(NameStatusPair.first);
|
||||||
SystemsGood++;
|
SystemsGood++;
|
||||||
break;
|
break;
|
||||||
case Application::Status::Bad:
|
case Application::Status::Bad:
|
||||||
|
Bad.push_back(NameStatusPair.first);
|
||||||
SystemsBad++;
|
SystemsBad++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.set_content(
|
res.set_content(
|
||||||
json {
|
json {
|
||||||
{ "ok", SystemsBad == 0 },
|
{ "healthy", SystemsBad == 0 },
|
||||||
|
{ "good", Good },
|
||||||
|
{ "bad", Bad },
|
||||||
}
|
}
|
||||||
.dump(),
|
.dump(),
|
||||||
"application/json");
|
"application/json");
|
||||||
res.status = 200;
|
res.status = 200;
|
||||||
});
|
});
|
||||||
// magic endpoint
|
// magic endpoint
|
||||||
HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) {
|
Server->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) {
|
||||||
res.set_content(std::string(Magic), "text/plain");
|
res.set_content(std::string(Magic), "text/plain");
|
||||||
});
|
});
|
||||||
HttpLibServerInstance->set_logger([](const httplib::Request& Req, const httplib::Response& Res) {
|
Server->set_logger([](const httplib::Request& Req, const httplib::Response& Res) {
|
||||||
beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status));
|
beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status));
|
||||||
});
|
});
|
||||||
|
Server->set_error_handler([](const httplib::Request&, httplib::Response& Res) {
|
||||||
|
if (Res.status >= 400 && Res.body.empty()) {
|
||||||
|
Res.set_content("Error: " + std::to_string(Res.status) + " " + Map.at(Res.status), "text/plain");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Server->set_exception_handler([](const httplib::Request& Req, httplib::Response& Res, std::exception& e) {
|
||||||
|
Res.status = 500;
|
||||||
|
Res.set_content("Internal Server Error", "text/plain");
|
||||||
|
beammp_errorf("Exception in http server serving '{}': {}", Req.target, e.what());
|
||||||
|
});
|
||||||
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
|
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
|
||||||
auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort);
|
auto ret = Server->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it.");
|
beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it.");
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-146
@@ -102,14 +102,6 @@ void LuaAPI::Print(sol::variadic_args Args) {
|
|||||||
luaprint(ToPrint);
|
luaprint(ToPrint);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::MP::GetServerVersion") {
|
|
||||||
const auto [ma, mi, pa] = LuaAPI::MP::GetServerVersion();
|
|
||||||
const auto real = Application::ServerVersion();
|
|
||||||
CHECK(ma == real.major);
|
|
||||||
CHECK(mi == real.minor);
|
|
||||||
CHECK(pa == real.patch);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) {
|
static inline bool InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) {
|
||||||
std::string Packet = "E:" + EventName + ":" + Data;
|
std::string Packet = "E:" + EventName + ":" + Data;
|
||||||
if (PlayerID == -1)
|
if (PlayerID == -1)
|
||||||
@@ -184,57 +176,50 @@ void LuaAPI::MP::Set(int ConfigID, sol::object NewValue) {
|
|||||||
if (NewValue.is<bool>()) {
|
if (NewValue.is<bool>()) {
|
||||||
Application::Settings.DebugModeEnabled = NewValue.as<bool>();
|
Application::Settings.DebugModeEnabled = NewValue.as<bool>();
|
||||||
beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
beammp_info(std::string("Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected boolean");
|
beammp_lua_error("set invalid argument [2] expected boolean");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1: // private
|
case 1: // private
|
||||||
if (NewValue.is<bool>()) {
|
if (NewValue.is<bool>()) {
|
||||||
Application::Settings.Private = NewValue.as<bool>();
|
Application::Settings.Private = NewValue.as<bool>();
|
||||||
beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
|
beammp_info(std::string("Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected boolean");
|
beammp_lua_error("set invalid argument [2] expected boolean");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 2: // max cars
|
case 2: // max cars
|
||||||
if (NewValue.is<int>()) {
|
if (NewValue.is<int>()) {
|
||||||
Application::Settings.MaxCars = NewValue.as<int>();
|
Application::Settings.MaxCars = NewValue.as<int>();
|
||||||
beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
|
beammp_info(std::string("Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected integer");
|
beammp_lua_error("set invalid argument [2] expected integer");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 3: // max players
|
case 3: // max players
|
||||||
if (NewValue.is<int>()) {
|
if (NewValue.is<int>()) {
|
||||||
Application::Settings.MaxPlayers = NewValue.as<int>();
|
Application::Settings.MaxPlayers = NewValue.as<int>();
|
||||||
beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
|
beammp_info(std::string("Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected integer");
|
beammp_lua_error("set invalid argument [2] expected integer");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 4: // Map
|
case 4: // Map
|
||||||
if (NewValue.is<std::string>()) {
|
if (NewValue.is<std::string>()) {
|
||||||
Application::Settings.MapName = NewValue.as<std::string>();
|
Application::Settings.MapName = NewValue.as<std::string>();
|
||||||
beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName);
|
beammp_info(std::string("Set `Map` to ") + Application::Settings.MapName);
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected string");
|
beammp_lua_error("set invalid argument [2] expected string");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 5: // Name
|
case 5: // Name
|
||||||
if (NewValue.is<std::string>()) {
|
if (NewValue.is<std::string>()) {
|
||||||
Application::Settings.ServerName = NewValue.as<std::string>();
|
Application::Settings.ServerName = NewValue.as<std::string>();
|
||||||
beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName);
|
beammp_info(std::string("Set `Name` to ") + Application::Settings.ServerName);
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected string");
|
beammp_lua_error("set invalid argument [2] expected string");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 6: // Desc
|
case 6: // Desc
|
||||||
if (NewValue.is<std::string>()) {
|
if (NewValue.is<std::string>()) {
|
||||||
Application::Settings.ServerDesc = NewValue.as<std::string>();
|
Application::Settings.ServerDesc = NewValue.as<std::string>();
|
||||||
beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc);
|
beammp_info(std::string("Set `Description` to ") + Application::Settings.ServerDesc);
|
||||||
} else {
|
} else
|
||||||
beammp_lua_error("set invalid argument [2] expected string");
|
beammp_lua_error("set invalid argument [2] expected string");
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this.");
|
beammp_warn("Invalid config ID \"" + std::to_string(ConfigID) + "\". Use `MP.Settings.*` enum for this.");
|
||||||
@@ -270,9 +255,7 @@ void LuaAPI::MP::PrintRaw(sol::variadic_args Args) {
|
|||||||
ToPrint += LuaToString(static_cast<const sol::object>(Arg));
|
ToPrint += LuaToString(static_cast<const sol::object>(Arg));
|
||||||
ToPrint += "\t";
|
ToPrint += "\t";
|
||||||
}
|
}
|
||||||
#ifdef DOCTEST_CONFIG_DISABLE
|
|
||||||
Application::Console().WriteRaw(ToPrint);
|
Application::Console().WriteRaw(ToPrint);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int LuaAPI::PanicHandler(lua_State* State) {
|
int LuaAPI::PanicHandler(lua_State* State) {
|
||||||
@@ -295,7 +278,7 @@ static std::pair<bool, std::string> FSWrapper(FnT Fn, ArgsT&&... Args) {
|
|||||||
std::pair<bool, std::string> LuaAPI::FS::CreateDirectory(const std::string& Path) {
|
std::pair<bool, std::string> LuaAPI::FS::CreateDirectory(const std::string& Path) {
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
fs::create_directories(Path, errc);
|
fs::create_directories(fs::relative(Path), errc);
|
||||||
Result.first = errc == std::error_code {};
|
Result.first = errc == std::error_code {};
|
||||||
if (!Result.first) {
|
if (!Result.first) {
|
||||||
Result.second = errc.message();
|
Result.second = errc.message();
|
||||||
@@ -303,33 +286,6 @@ std::pair<bool, std::string> LuaAPI::FS::CreateDirectory(const std::string& Path
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::CreateDirectory") {
|
|
||||||
std::string TestDir = "beammp_test_dir";
|
|
||||||
fs::remove_all(TestDir);
|
|
||||||
SUBCASE("Single level dir") {
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK(Err == "");
|
|
||||||
CHECK(fs::exists(TestDir));
|
|
||||||
}
|
|
||||||
SUBCASE("Multi level dir") {
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir + "/a/b/c");
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK(Err == "");
|
|
||||||
CHECK(fs::exists(TestDir + "/a/b/c"));
|
|
||||||
}
|
|
||||||
SUBCASE("Already exists") {
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::CreateDirectory(TestDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK(Err == "");
|
|
||||||
CHECK(fs::exists(TestDir));
|
|
||||||
const auto [Ok2, Err2] = LuaAPI::FS::CreateDirectory(TestDir);
|
|
||||||
CHECK(Ok2);
|
|
||||||
CHECK(Err2 == "");
|
|
||||||
}
|
|
||||||
fs::remove_all(TestDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> LuaAPI::FS::Remove(const std::string& Path) {
|
std::pair<bool, std::string> LuaAPI::FS::Remove(const std::string& Path) {
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
@@ -341,30 +297,10 @@ std::pair<bool, std::string> LuaAPI::FS::Remove(const std::string& Path) {
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::Remove") {
|
|
||||||
const std::string TestFileOrDir = "beammp_test_thing";
|
|
||||||
SUBCASE("Remove existing directory") {
|
|
||||||
fs::create_directory(TestFileOrDir);
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::Remove(TestFileOrDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK_EQ(Err, "");
|
|
||||||
CHECK(!fs::exists(TestFileOrDir));
|
|
||||||
}
|
|
||||||
SUBCASE("Remove non-existing directory") {
|
|
||||||
fs::remove_all(TestFileOrDir);
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::Remove(TestFileOrDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK_EQ(Err, "");
|
|
||||||
CHECK(!fs::exists(TestFileOrDir));
|
|
||||||
}
|
|
||||||
// TODO: add tests for files
|
|
||||||
// TODO: add tests for files and folders without access permissions (failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> LuaAPI::FS::Rename(const std::string& Path, const std::string& NewPath) {
|
std::pair<bool, std::string> LuaAPI::FS::Rename(const std::string& Path, const std::string& NewPath) {
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
fs::rename(Path, NewPath, errc);
|
fs::rename(fs::relative(Path), fs::relative(NewPath), errc);
|
||||||
Result.first = errc == std::error_code {};
|
Result.first = errc == std::error_code {};
|
||||||
if (!Result.first) {
|
if (!Result.first) {
|
||||||
Result.second = errc.message();
|
Result.second = errc.message();
|
||||||
@@ -372,25 +308,10 @@ std::pair<bool, std::string> LuaAPI::FS::Rename(const std::string& Path, const s
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::Rename") {
|
|
||||||
const auto TestDir = "beammp_test_dir";
|
|
||||||
const auto OtherTestDir = "beammp_test_dir_2";
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::create_directory(TestDir);
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::Rename(TestDir, OtherTestDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK_EQ(Err, "");
|
|
||||||
CHECK(!fs::exists(TestDir));
|
|
||||||
CHECK(fs::exists(OtherTestDir));
|
|
||||||
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::remove_all(TestDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> LuaAPI::FS::Copy(const std::string& Path, const std::string& NewPath) {
|
std::pair<bool, std::string> LuaAPI::FS::Copy(const std::string& Path, const std::string& NewPath) {
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
fs::copy(Path, NewPath, fs::copy_options::recursive, errc);
|
fs::copy(fs::relative(Path), fs::relative(NewPath), fs::copy_options::recursive, errc);
|
||||||
Result.first = errc == std::error_code {};
|
Result.first = errc == std::error_code {};
|
||||||
if (!Result.first) {
|
if (!Result.first) {
|
||||||
Result.second = errc.message();
|
Result.second = errc.message();
|
||||||
@@ -398,86 +319,30 @@ std::pair<bool, std::string> LuaAPI::FS::Copy(const std::string& Path, const std
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::Copy") {
|
|
||||||
const auto TestDir = "beammp_test_dir";
|
|
||||||
const auto OtherTestDir = "beammp_test_dir_2";
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::create_directory(TestDir);
|
|
||||||
const auto [Ok, Err] = LuaAPI::FS::Copy(TestDir, OtherTestDir);
|
|
||||||
CHECK(Ok);
|
|
||||||
CHECK_EQ(Err, "");
|
|
||||||
CHECK(fs::exists(TestDir));
|
|
||||||
CHECK(fs::exists(OtherTestDir));
|
|
||||||
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::remove_all(TestDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LuaAPI::FS::Exists(const std::string& Path) {
|
bool LuaAPI::FS::Exists(const std::string& Path) {
|
||||||
return fs::exists(Path);
|
return fs::exists(fs::relative(Path));
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::Exists") {
|
|
||||||
const auto TestDir = "beammp_test_dir";
|
|
||||||
const auto OtherTestDir = "beammp_test_dir_2";
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::create_directory(TestDir);
|
|
||||||
|
|
||||||
CHECK(LuaAPI::FS::Exists(TestDir));
|
|
||||||
CHECK(!LuaAPI::FS::Exists(OtherTestDir));
|
|
||||||
|
|
||||||
fs::remove_all(OtherTestDir);
|
|
||||||
fs::remove_all(TestDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LuaAPI::FS::GetFilename(const std::string& Path) {
|
std::string LuaAPI::FS::GetFilename(const std::string& Path) {
|
||||||
return fs::path(Path).filename().string();
|
return fs::path(Path).filename().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::GetFilename") {
|
|
||||||
CHECK(LuaAPI::FS::GetFilename("test.txt") == "test.txt");
|
|
||||||
CHECK(LuaAPI::FS::GetFilename("/test.txt") == "test.txt");
|
|
||||||
CHECK(LuaAPI::FS::GetFilename("place/test.txt") == "test.txt");
|
|
||||||
CHECK(LuaAPI::FS::GetFilename("/some/../place/test.txt") == "test.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LuaAPI::FS::GetExtension(const std::string& Path) {
|
std::string LuaAPI::FS::GetExtension(const std::string& Path) {
|
||||||
return fs::path(Path).extension().string();
|
return fs::path(Path).extension().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::GetExtension") {
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("test.txt") == ".txt");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/test.txt") == ".txt");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("place/test.txt") == ".txt");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/some/../place/test.txt") == ".txt");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/some/../place/test") == "");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/some/../place/test.a.b.c") == ".c");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/some/../place/test.") == ".");
|
|
||||||
CHECK(LuaAPI::FS::GetExtension("/some/../place/test.a.b.") == ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string LuaAPI::FS::GetParentFolder(const std::string& Path) {
|
std::string LuaAPI::FS::GetParentFolder(const std::string& Path) {
|
||||||
return fs::path(Path).parent_path().string();
|
return fs::path(Path).parent_path().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("LuaAPI::FS::GetParentFolder") {
|
|
||||||
CHECK(LuaAPI::FS::GetParentFolder("test.txt") == "");
|
|
||||||
CHECK(LuaAPI::FS::GetParentFolder("/test.txt") == "/");
|
|
||||||
CHECK(LuaAPI::FS::GetParentFolder("place/test.txt") == "place");
|
|
||||||
CHECK(LuaAPI::FS::GetParentFolder("/some/../place/test.txt") == "/some/../place");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add tests
|
|
||||||
bool LuaAPI::FS::IsDirectory(const std::string& Path) {
|
bool LuaAPI::FS::IsDirectory(const std::string& Path) {
|
||||||
return fs::is_directory(Path);
|
return fs::is_directory(Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add tests
|
|
||||||
bool LuaAPI::FS::IsFile(const std::string& Path) {
|
bool LuaAPI::FS::IsFile(const std::string& Path) {
|
||||||
return fs::is_regular_file(Path);
|
return fs::is_regular_file(Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add tests
|
|
||||||
std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) {
|
std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) {
|
||||||
fs::path Path;
|
fs::path Path;
|
||||||
for (size_t i = 0; i < Args.size(); ++i) {
|
for (size_t i = 0; i < Args.size(); ++i) {
|
||||||
|
|||||||
+30
-56
@@ -27,42 +27,15 @@ static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
|||||||
// HTTP
|
// HTTP
|
||||||
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
||||||
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
||||||
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
|
|
||||||
static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
|
|
||||||
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
||||||
static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP";
|
static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP";
|
||||||
|
|
||||||
TEST_CASE("TConfig::TConfig") {
|
|
||||||
const std::string CfgFile = "beammp_server_testconfig.toml";
|
|
||||||
fs::remove(CfgFile);
|
|
||||||
|
|
||||||
TConfig Cfg(CfgFile);
|
|
||||||
|
|
||||||
CHECK(fs::file_size(CfgFile) != 0);
|
|
||||||
|
|
||||||
std::string buf;
|
|
||||||
{
|
|
||||||
buf.resize(fs::file_size(CfgFile));
|
|
||||||
auto fp = std::fopen(CfgFile.c_str(), "r");
|
|
||||||
std::fread(buf.data(), 1, buf.size(), fp);
|
|
||||||
std::fclose(fp);
|
|
||||||
}
|
|
||||||
INFO("file contents are:", buf);
|
|
||||||
|
|
||||||
const auto table = toml::parse(CfgFile);
|
|
||||||
CHECK(table.at("General").is_table());
|
|
||||||
CHECK(table.at("Misc").is_table());
|
|
||||||
CHECK(table.at("HTTP").is_table());
|
|
||||||
|
|
||||||
fs::remove(CfgFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
TConfig::TConfig(const std::string& ConfigFileName)
|
TConfig::TConfig(const std::string& ConfigFileName)
|
||||||
: mConfigFileName(ConfigFileName) {
|
: mConfigFileName(ConfigFileName) {
|
||||||
Application::SetSubsystemStatus("Config", Application::Status::Starting);
|
Application::SetSubsystemStatus("Config", Application::Status::Starting);
|
||||||
if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) {
|
if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) {
|
||||||
beammp_info("No config file found! Generating one...");
|
beammp_info("No config file found! Generating one...");
|
||||||
CreateConfigFile();
|
CreateConfigFile(mConfigFileName);
|
||||||
}
|
}
|
||||||
if (!mFailed) {
|
if (!mFailed) {
|
||||||
if (fs::exists("Server.cfg")) {
|
if (fs::exists("Server.cfg")) {
|
||||||
@@ -88,8 +61,7 @@ void SetComment(CommentsT& Comments, const std::string& Comment) {
|
|||||||
* whether it is in TConfig.cpp or the configuration file.
|
* whether it is in TConfig.cpp or the configuration file.
|
||||||
*/
|
*/
|
||||||
void TConfig::FlushToFile() {
|
void TConfig::FlushToFile() {
|
||||||
// auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
||||||
auto data = toml::value {};
|
|
||||||
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
||||||
data["General"][StrLogChat.data()] = Application::Settings.LogChat;
|
data["General"][StrLogChat.data()] = Application::Settings.LogChat;
|
||||||
@@ -111,34 +83,16 @@ void TConfig::FlushToFile() {
|
|||||||
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
||||||
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " 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`");
|
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " 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`");
|
||||||
// HTTP
|
// HTTP
|
||||||
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
|
|
||||||
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
|
|
||||||
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
||||||
SetComment(data["HTTP"][StrHTTPServerIP.data()].comments(), " Which IP to listen on. Pick 0.0.0.0 for a public-facing server with no specific IP, and 127.0.0.1 or 'localhost' for a local server.");
|
SetComment(data["HTTP"][StrHTTPServerIP.data()].comments(), " Which IP to listen on. Pick 0.0.0.0 for a public-facing server with no specific IP, and 127.0.0.1 or 'localhost' for a local server.");
|
||||||
data["HTTP"][StrHTTPServerIP.data()] = Application::Settings.HTTPServerIP;
|
data["HTTP"][StrHTTPServerIP.data()] = Application::Settings.HTTPServerIP;
|
||||||
data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL;
|
|
||||||
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to have enabled for servers which face the internet. With SSL the server will serve https and requires valid key and cert files");
|
|
||||||
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
||||||
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
||||||
std::stringstream Ss;
|
std::ofstream Stream(mConfigFileName, std::ios::trunc | std::ios::out);
|
||||||
Ss << "# This is the BeamMP-Server config file.\n"
|
Stream << data << std::flush;
|
||||||
"# 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"
|
|
||||||
<< data;
|
|
||||||
auto File = std::fopen(mConfigFileName.c_str(), "w+");
|
|
||||||
if (!File) {
|
|
||||||
beammp_error("Failed to create/write to config file: " + GetPlatformAgnosticErrorString());
|
|
||||||
throw std::runtime_error("Failed to create/write to config file");
|
|
||||||
}
|
|
||||||
auto Str = Ss.str();
|
|
||||||
auto N = std::fwrite(Str.data(), sizeof(char), Str.size(), File);
|
|
||||||
if (N != Str.size()) {
|
|
||||||
beammp_error("Failed to write to config file properly, config file might be misshapen");
|
|
||||||
}
|
|
||||||
std::fclose(File);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConfig::CreateConfigFile() {
|
void TConfig::CreateConfigFile(std::string_view name) {
|
||||||
// build from old config Server.cfg
|
// build from old config Server.cfg
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -150,7 +104,32 @@ void TConfig::CreateConfigFile() {
|
|||||||
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // create file context
|
||||||
|
std::ofstream ofs(name.data());
|
||||||
|
}
|
||||||
|
|
||||||
FlushToFile();
|
FlushToFile();
|
||||||
|
|
||||||
|
size_t FileSize = fs::file_size(name);
|
||||||
|
std::fstream ofs { std::string(name), std::ios::in | std::ios::out };
|
||||||
|
if (ofs.good()) {
|
||||||
|
std::string Contents {};
|
||||||
|
Contents.resize(FileSize);
|
||||||
|
ofs.readsome(Contents.data(), FileSize);
|
||||||
|
ofs.seekp(0);
|
||||||
|
ofs << "# This is the BeamMP-Server config file.\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"
|
||||||
|
<< '\n'
|
||||||
|
<< Contents;
|
||||||
|
beammp_error("There was no \"" + std::string(mConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
|
||||||
|
mFailed = true;
|
||||||
|
ofs.close();
|
||||||
|
} else {
|
||||||
|
beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
||||||
|
Application::SetSubsystemStatus("Config", Application::Status::Bad);
|
||||||
|
mFailed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) {
|
void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) {
|
||||||
@@ -191,12 +170,9 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "Misc", StrHideUpdateMessages, Application::Settings.HideUpdateMessages);
|
TryReadValue(data, "Misc", StrHideUpdateMessages, Application::Settings.HideUpdateMessages);
|
||||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
||||||
// HTTP
|
// HTTP
|
||||||
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
|
|
||||||
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
|
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort);
|
TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerIP, Application::Settings.HTTPServerIP);
|
TryReadValue(data, "HTTP", StrHTTPServerIP, Application::Settings.HTTPServerIP);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled);
|
TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL);
|
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
||||||
mFailed = true;
|
mFailed = true;
|
||||||
@@ -231,8 +207,6 @@ void TConfig::PrintDebug() {
|
|||||||
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
beammp_debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
||||||
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\"");
|
beammp_debug(std::string(StrLogChat) + ": \"" + (Application::Settings.LogChat ? "true" : "false") + "\"");
|
||||||
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
beammp_debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
||||||
beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\"");
|
|
||||||
beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\"");
|
|
||||||
beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\"");
|
beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\"");
|
||||||
beammp_debug(std::string(StrHTTPServerIP) + ": \"" + Application::Settings.HTTPServerIP + "\"");
|
beammp_debug(std::string(StrHTTPServerIP) + ": \"" + Application::Settings.HTTPServerIP + "\"");
|
||||||
// special!
|
// special!
|
||||||
|
|||||||
+1
-35
@@ -14,17 +14,6 @@ static inline bool StringStartsWith(const std::string& What, const std::string&
|
|||||||
return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith;
|
return What.size() >= StartsWith.size() && What.substr(0, StartsWith.size()) == StartsWith;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("StringStartsWith") {
|
|
||||||
CHECK(StringStartsWith("Hello, World", "Hello"));
|
|
||||||
CHECK(StringStartsWith("Hello, World", "H"));
|
|
||||||
CHECK(StringStartsWith("Hello, World", ""));
|
|
||||||
CHECK(!StringStartsWith("Hello, World", "ello"));
|
|
||||||
CHECK(!StringStartsWith("Hello, World", "World"));
|
|
||||||
CHECK(StringStartsWith("", ""));
|
|
||||||
CHECK(!StringStartsWith("", "hello"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trims leading and trailing spaces, newlines, tabs, etc.
|
|
||||||
static inline std::string TrimString(std::string S) {
|
static inline std::string TrimString(std::string S) {
|
||||||
S.erase(S.begin(), std::find_if(S.begin(), S.end(), [](unsigned char ch) {
|
S.erase(S.begin(), std::find_if(S.begin(), S.end(), [](unsigned char ch) {
|
||||||
return !std::isspace(ch);
|
return !std::isspace(ch);
|
||||||
@@ -36,21 +25,6 @@ static inline std::string TrimString(std::string S) {
|
|||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("TrimString") {
|
|
||||||
CHECK(TrimString("hel lo") == "hel lo");
|
|
||||||
CHECK(TrimString(" hel lo") == "hel lo");
|
|
||||||
CHECK(TrimString(" hel lo ") == "hel lo");
|
|
||||||
CHECK(TrimString("hel lo ") == "hel lo");
|
|
||||||
CHECK(TrimString(" hel lo") == "hel lo");
|
|
||||||
CHECK(TrimString("hel lo ") == "hel lo");
|
|
||||||
CHECK(TrimString(" hel lo ") == "hel lo");
|
|
||||||
CHECK(TrimString("\t\thel\nlo\n\n") == "hel\nlo");
|
|
||||||
CHECK(TrimString("\n\thel\tlo\n\t") == "hel\tlo");
|
|
||||||
CHECK(TrimString(" ") == "");
|
|
||||||
CHECK(TrimString(" \t\n\r ") == "");
|
|
||||||
CHECK(TrimString("") == "");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GetDate() {
|
std::string GetDate() {
|
||||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||||
time_t tt = std::chrono::system_clock::to_time_t(now);
|
time_t tt = std::chrono::system_clock::to_time_t(now);
|
||||||
@@ -227,8 +201,7 @@ void TConsole::Command_Help(const std::string&, const std::vector<std::string>&
|
|||||||
say <message> sends the message to all players in chat
|
say <message> sends the message to all players in chat
|
||||||
lua [state id] switches to lua, optionally into a specific state id's lua
|
lua [state id] switches to lua, optionally into a specific state id's lua
|
||||||
settings [command] sets or gets settings for the server, run `settings help` for more info
|
settings [command] sets or gets settings for the server, run `settings help` for more info
|
||||||
status how the server is doing and what it's up to
|
status how the server is doing and what it's up to)";
|
||||||
clear clears the console window)";
|
|
||||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,13 +214,6 @@ std::string TConsole::ConcatArgs(const std::vector<std::string>& args, char spac
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConsole::Command_Clear(const std::string&, const std::vector<std::string>& args) {
|
|
||||||
if (!EnsureArgsCount(args, 0, size_t(-1))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mCommandline.write("\x1b[;H\x1b[2J");
|
|
||||||
}
|
|
||||||
|
|
||||||
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
||||||
if (!EnsureArgsCount(args, 1, size_t(-1))) {
|
if (!EnsureArgsCount(args, 1, size_t(-1))) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <rapidjson/rapidjson.h>
|
#include <rapidjson/rapidjson.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace json = rapidjson;
|
|
||||||
|
|
||||||
void THeartbeatThread::operator()() {
|
void THeartbeatThread::operator()() {
|
||||||
RegisterThread("Heartbeat");
|
RegisterThread("Heartbeat");
|
||||||
@@ -20,7 +19,7 @@ void THeartbeatThread::operator()() {
|
|||||||
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||||
bool isAuth = false;
|
bool isAuth = false;
|
||||||
size_t UpdateReminderCounter = 0;
|
size_t UpdateReminderCounter = 0;
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
++UpdateReminderCounter;
|
++UpdateReminderCounter;
|
||||||
Body = GenerateCall();
|
Body = GenerateCall();
|
||||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||||
@@ -57,7 +56,7 @@ void THeartbeatThread::operator()() {
|
|||||||
auto Target = "/heartbeat";
|
auto Target = "/heartbeat";
|
||||||
unsigned int ResponseCode = 0;
|
unsigned int ResponseCode = 0;
|
||||||
|
|
||||||
json::Document Doc;
|
rapidjson::Document Doc;
|
||||||
bool Ok = false;
|
bool Ok = false;
|
||||||
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
||||||
T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
|
T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
|
||||||
@@ -164,6 +163,7 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S
|
|||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::ShuttingDown);
|
||||||
if (mThread.joinable()) {
|
if (mThread.joinable()) {
|
||||||
|
mShutdown = true;
|
||||||
mThread.join();
|
mThread.join();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::Shutdown);
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Shutdown);
|
||||||
|
|||||||
+93
-45
@@ -16,29 +16,26 @@
|
|||||||
TLuaEngine* LuaAPI::MP::Engine;
|
TLuaEngine* LuaAPI::MP::Engine;
|
||||||
|
|
||||||
TLuaEngine::TLuaEngine()
|
TLuaEngine::TLuaEngine()
|
||||||
: mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") {
|
: mPluginMonitor(fs::path(Application::Settings.Resource) / "Server", *this, mShutdown) {
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
|
||||||
LuaAPI::MP::Engine = this;
|
LuaAPI::MP::Engine = this;
|
||||||
if (!fs::exists(Application::Settings.Resource)) {
|
if (!fs::exists(Application::Settings.Resource)) {
|
||||||
fs::create_directory(Application::Settings.Resource);
|
fs::create_directory(Application::Settings.Resource);
|
||||||
}
|
}
|
||||||
if (!fs::exists(mResourceServerPath)) {
|
fs::path Path = fs::path(Application::Settings.Resource) / "Server";
|
||||||
fs::create_directory(mResourceServerPath);
|
if (!fs::exists(Path)) {
|
||||||
|
fs::create_directory(Path);
|
||||||
}
|
}
|
||||||
|
mResourceServerPath = Path;
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::ShuttingDown);
|
||||||
|
mShutdown = true;
|
||||||
if (mThread.joinable()) {
|
if (mThread.joinable()) {
|
||||||
mThread.join();
|
mThread.join();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Shutdown);
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::Shutdown);
|
||||||
});
|
});
|
||||||
IThreaded::Start();
|
Start();
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("TLuaEngine ctor & dtor") {
|
|
||||||
Application::Settings.Resource = "beammp_server_test_resources";
|
|
||||||
TLuaEngine engine;
|
|
||||||
Application::GracefullyShutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::operator()() {
|
void TLuaEngine::operator()() {
|
||||||
@@ -57,28 +54,30 @@ void TLuaEngine::operator()() {
|
|||||||
|
|
||||||
auto ResultCheckThread = std::thread([&] {
|
auto ResultCheckThread = std::thread([&] {
|
||||||
RegisterThread("ResultCheckThread");
|
RegisterThread("ResultCheckThread");
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
|
mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20));
|
||||||
if (!mResultsToCheck.empty()) {
|
if (!mResultsToCheck.empty()) {
|
||||||
mResultsToCheck.remove_if([](const std::shared_ptr<TLuaResult>& Ptr) -> bool {
|
mResultsToCheck.remove_if([](const std::shared_ptr<TLuaResult>& Ptr) -> bool {
|
||||||
if (Ptr->Ready) {
|
if (Ptr->Ready) {
|
||||||
if (Ptr->Error) {
|
return true;
|
||||||
if (Ptr->ErrorMessage != BeamMPFnNotFoundError) {
|
} else if (Ptr->Error) {
|
||||||
beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage);
|
if (Ptr->ErrorMessage != BeamMPFnNotFoundError) {
|
||||||
}
|
beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// event loop
|
// event loop
|
||||||
auto Before = std::chrono::high_resolution_clock::now();
|
auto Before = std::chrono::high_resolution_clock::now();
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
|
if (mLuaStates.size() == 0) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(100));
|
||||||
|
}
|
||||||
{ // Timed Events Scope
|
{ // Timed Events Scope
|
||||||
std::unique_lock Lock(mTimedEventsMutex);
|
std::unique_lock Lock(mTimedEventsMutex);
|
||||||
for (auto& Timer : mTimedEvents) {
|
for (auto& Timer : mTimedEvents) {
|
||||||
@@ -104,18 +103,12 @@ void TLuaEngine::operator()() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mLuaStates.size() == 0) {
|
const auto Expected = std::chrono::milliseconds(10);
|
||||||
beammp_trace("No Lua states, event loop running extremely sparsely");
|
if (auto Diff = std::chrono::high_resolution_clock::now() - Before;
|
||||||
Application::SleepSafeSeconds(10);
|
Diff < Expected) {
|
||||||
|
std::this_thread::sleep_for(Expected - Diff);
|
||||||
} else {
|
} else {
|
||||||
constexpr double NsFactor = 1000000.0;
|
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
|
||||||
constexpr double Expected = 10.0; // ms
|
|
||||||
const auto Diff = (std::chrono::high_resolution_clock::now() - Before).count() / NsFactor;
|
|
||||||
if (Diff < Expected) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::nanoseconds(size_t((Expected - Diff) * NsFactor)));
|
|
||||||
} else {
|
|
||||||
beammp_tracef("Event loop cannot keep up! Running {}ms behind", Diff);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Before = std::chrono::high_resolution_clock::now();
|
Before = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
@@ -162,7 +155,7 @@ void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
|
|||||||
mResultsToCheckCond.notify_one();
|
mResultsToCheckCond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string /* event name */, std::vector<std::string> /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) {
|
std::unordered_map<std::string /*event name */, std::vector<std::string> /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) {
|
||||||
std::unordered_map<std::string, std::vector<std::string>> Result;
|
std::unordered_map<std::string, std::vector<std::string>> Result;
|
||||||
std::unique_lock Lock(mLuaEventsMutex);
|
std::unique_lock Lock(mLuaEventsMutex);
|
||||||
for (const auto& EventNameToEventMap : mLuaEvents) {
|
for (const auto& EventNameToEventMap : mLuaEvents) {
|
||||||
@@ -276,9 +269,6 @@ std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::CollectAndInitPlugins() {
|
void TLuaEngine::CollectAndInitPlugins() {
|
||||||
if (!fs::exists(mResourceServerPath)) {
|
|
||||||
fs::create_directories(mResourceServerPath);
|
|
||||||
}
|
|
||||||
for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) {
|
for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) {
|
||||||
auto Path = Dir.path();
|
auto Path = Dir.path();
|
||||||
Path = fs::relative(Path);
|
Path = fs::relative(Path);
|
||||||
@@ -328,7 +318,7 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name,
|
|||||||
std::unique_lock Lock(mLuaStatesMutex);
|
std::unique_lock Lock(mLuaStatesMutex);
|
||||||
if (mLuaStates.find(StateId) == mLuaStates.end()) {
|
if (mLuaStates.find(StateId) == mLuaStates.end()) {
|
||||||
beammp_debug("Creating lua state for state id \"" + StateId + "\"");
|
beammp_debug("Creating lua state for state id \"" + StateId + "\"");
|
||||||
auto DataPtr = std::make_unique<StateThreadData>(Name, StateId, *this);
|
auto DataPtr = std::make_unique<StateThreadData>(Name, mShutdown, StateId, *this);
|
||||||
mLuaStates[StateId] = std::move(DataPtr);
|
mLuaStates[StateId] = std::move(DataPtr);
|
||||||
RegisterEvent("onInit", StateId, "onInit");
|
RegisterEvent("onInit", StateId, "onInit");
|
||||||
if (!DontCallOnInit) {
|
if (!DontCallOnInit) {
|
||||||
@@ -616,8 +606,9 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) {
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine)
|
TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine)
|
||||||
: mName(Name)
|
: mName(Name)
|
||||||
|
, mShutdown(Shutdown)
|
||||||
, mStateId(StateId)
|
, mStateId(StateId)
|
||||||
, mState(luaL_newstate())
|
, mState(luaL_newstate())
|
||||||
, mEngine(&Engine) {
|
, mEngine(&Engine) {
|
||||||
@@ -726,15 +717,6 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
UtilTable.set_function("JsonUnflatten", &LuaAPI::MP::JsonUnflatten);
|
UtilTable.set_function("JsonUnflatten", &LuaAPI::MP::JsonUnflatten);
|
||||||
UtilTable.set_function("JsonPrettify", &LuaAPI::MP::JsonPrettify);
|
UtilTable.set_function("JsonPrettify", &LuaAPI::MP::JsonPrettify);
|
||||||
UtilTable.set_function("JsonMinify", &LuaAPI::MP::JsonMinify);
|
UtilTable.set_function("JsonMinify", &LuaAPI::MP::JsonMinify);
|
||||||
UtilTable.set_function("Stress", [](size_t n) {
|
|
||||||
std::vector<std::thread> s;
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
s.emplace_back([]{ while (true); });
|
|
||||||
}
|
|
||||||
for (auto& t : s) {
|
|
||||||
t.detach();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
UtilTable.set_function("Random", [this] {
|
UtilTable.set_function("Random", [this] {
|
||||||
return mUniformRealDistribution01(mMersenneTwister);
|
return mUniformRealDistribution01(mMersenneTwister);
|
||||||
});
|
});
|
||||||
@@ -829,7 +811,7 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co
|
|||||||
|
|
||||||
void TLuaEngine::StateThreadData::operator()() {
|
void TLuaEngine::StateThreadData::operator()() {
|
||||||
RegisterThread("Lua:" + mStateId);
|
RegisterThread("Lua:" + mStateId);
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
{ // StateExecuteQueue Scope
|
{ // StateExecuteQueue Scope
|
||||||
std::unique_lock Lock(mStateExecuteQueueMutex);
|
std::unique_lock Lock(mStateExecuteQueueMutex);
|
||||||
if (!mStateExecuteQueue.empty()) {
|
if (!mStateExecuteQueue.empty()) {
|
||||||
@@ -999,3 +981,69 @@ bool TLuaEngine::TimedEvent::Expired() {
|
|||||||
void TLuaEngine::TimedEvent::Reset() {
|
void TLuaEngine::TimedEvent::Reset() {
|
||||||
LastCompletion = std::chrono::high_resolution_clock::now();
|
LastCompletion = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TPluginMonitor::TPluginMonitor(const fs::path& Path, TLuaEngine& Engine, std::atomic_bool& Shutdown)
|
||||||
|
: mEngine(Engine)
|
||||||
|
, mPath(Path)
|
||||||
|
, mShutdown(Shutdown) {
|
||||||
|
if (!fs::exists(mPath)) {
|
||||||
|
fs::create_directories(mPath);
|
||||||
|
}
|
||||||
|
for (const auto& Entry : fs::recursive_directory_iterator(mPath)) {
|
||||||
|
// TODO: trigger an event when a subfolder file changes
|
||||||
|
if (Entry.is_regular_file()) {
|
||||||
|
mFileTimes[Entry.path().string()] = fs::last_write_time(Entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TPluginMonitor::operator()() {
|
||||||
|
RegisterThread("PluginMonitor");
|
||||||
|
beammp_info("PluginMonitor started");
|
||||||
|
while (!mShutdown) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||||
|
std::vector<std::string> ToRemove;
|
||||||
|
for (const auto& Pair : mFileTimes) {
|
||||||
|
try {
|
||||||
|
auto CurrentTime = fs::last_write_time(Pair.first);
|
||||||
|
if (CurrentTime != Pair.second) {
|
||||||
|
mFileTimes[Pair.first] = CurrentTime;
|
||||||
|
// grandparent of the path should be Resources/Server
|
||||||
|
if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) {
|
||||||
|
beammp_info("File \"" + Pair.first + "\" changed, reloading");
|
||||||
|
// is in root folder, so reload
|
||||||
|
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
|
||||||
|
auto Size = std::filesystem::file_size(Pair.first);
|
||||||
|
auto Contents = std::make_shared<std::string>();
|
||||||
|
Contents->resize(Size);
|
||||||
|
FileStream.read(Contents->data(), Contents->size());
|
||||||
|
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
|
||||||
|
auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
||||||
|
auto Res = mEngine.EnqueueScript(StateID, Chunk);
|
||||||
|
// TODO: call onInit
|
||||||
|
mEngine.AddResultToCheck(Res);
|
||||||
|
} else {
|
||||||
|
// TODO: trigger onFileChanged event
|
||||||
|
beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet");
|
||||||
|
/*
|
||||||
|
// is in subfolder, dont reload, just trigger an event
|
||||||
|
auto Results = mEngine.TriggerEvent("onFileChanged", "", Pair.first);
|
||||||
|
mEngine.WaitForAll(Results);
|
||||||
|
for (const auto& Result : Results) {
|
||||||
|
if (Result->Error) {
|
||||||
|
beammp_lua_error(Result->ErrorMessage);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
ToRemove.push_back(Pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& File : ToRemove) {
|
||||||
|
mFileTimes.erase(File);
|
||||||
|
beammp_warn("file '" + File + "' couldn't be accessed, so it was removed from plugin hot reload monitor (probably got deleted)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+19
-78
@@ -25,6 +25,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("UDPNetwork", Application::Status::ShuttingDown);
|
||||||
if (mUDPThread.joinable()) {
|
if (mUDPThread.joinable()) {
|
||||||
|
mShutdown = true;
|
||||||
mUDPThread.detach();
|
mUDPThread.detach();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Shutdown);
|
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Shutdown);
|
||||||
@@ -32,6 +33,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
Application::SetSubsystemStatus("TCPNetwork", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("TCPNetwork", Application::Status::ShuttingDown);
|
||||||
if (mTCPThread.joinable()) {
|
if (mTCPThread.joinable()) {
|
||||||
|
mShutdown = true;
|
||||||
mTCPThread.detach();
|
mTCPThread.detach();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Shutdown);
|
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Shutdown);
|
||||||
@@ -66,7 +68,7 @@ void TNetwork::UDPServerMain() {
|
|||||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
|
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
|
||||||
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
beammp_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"));
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
try {
|
try {
|
||||||
sockaddr_in client {};
|
sockaddr_in client {};
|
||||||
std::string Data = UDPRcvFromClient(client); // Receives any data from Socket
|
std::string Data = UDPRcvFromClient(client); // Receives any data from Socket
|
||||||
@@ -150,7 +152,7 @@ void TNetwork::TCPServerMain() {
|
|||||||
beammp_info("Vehicle event network online");
|
beammp_info("Vehicle event network online");
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
if (Application::IsShuttingDown()) {
|
if (mShutdown) {
|
||||||
beammp_debug("shutdown during TCP wait for accept loop");
|
beammp_debug("shutdown during TCP wait for accept loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -192,7 +194,6 @@ void TNetwork::TCPServerMain() {
|
|||||||
#undef GetObject // Fixes Windows
|
#undef GetObject // Fixes Windows
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
namespace json = rapidjson;
|
|
||||||
|
|
||||||
void TNetwork::Identify(const TConnection& client) {
|
void TNetwork::Identify(const TConnection& client) {
|
||||||
RegisterThreadAuto();
|
RegisterThreadAuto();
|
||||||
@@ -237,26 +238,13 @@ void TNetwork::HandleDownload(SOCKET TCPSock) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_ip_str(const struct sockaddr* sa, char* strBuf, size_t strBufSize) {
|
|
||||||
switch (sa->sa_family) {
|
|
||||||
case AF_INET:
|
|
||||||
inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), strBuf, strBufSize);
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), strBuf, strBufSize);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TNetwork::Authentication(const TConnection& ClientConnection) {
|
void TNetwork::Authentication(const TConnection& ClientConnection) {
|
||||||
auto Client = CreateClient(ClientConnection.Socket);
|
auto Client = CreateClient(ClientConnection.Socket);
|
||||||
char AddrBuf[INET6_ADDRSTRLEN];
|
char AddrBuf[64];
|
||||||
get_ip_str(&ClientConnection.SockAddr, AddrBuf, sizeof(AddrBuf));
|
// TODO: IPv6 would need this to be changed
|
||||||
beammp_trace("This thread is ip " + std::string(AddrBuf));
|
auto str = inet_ntop(AF_INET, reinterpret_cast<const void*>(&ClientConnection.SockAddr), AddrBuf, sizeof(ClientConnection.SockAddr));
|
||||||
Client->SetIdentifier("ip", AddrBuf);
|
beammp_trace("This thread is ip " + std::string(str));
|
||||||
|
Client->SetIdentifier("ip", str);
|
||||||
|
|
||||||
std::string Rc; // TODO: figure out why this is not default constructed
|
std::string Rc; // TODO: figure out why this is not default constructed
|
||||||
beammp_info("Identifying new ClientConnection...");
|
beammp_info("Identifying new ClientConnection...");
|
||||||
@@ -292,7 +280,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
|
|||||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, RequestString, "application/json", &ResponseCode);
|
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, RequestString, "application/json", &ResponseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
json::Document AuthResponse;
|
rapidjson::Document AuthResponse;
|
||||||
AuthResponse.Parse(Rc.c_str());
|
AuthResponse.Parse(Rc.c_str());
|
||||||
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
|
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
|
||||||
ClientKick(*Client, "Invalid key! Please restart your game.");
|
ClientKick(*Client, "Invalid key! Please restart your game.");
|
||||||
@@ -772,63 +760,14 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<size_t /* count */, size_t /* last chunk */> SplitIntoChunks(size_t FullSize, size_t ChunkSize) {
|
|
||||||
if (FullSize < ChunkSize) {
|
|
||||||
return { 0, FullSize };
|
|
||||||
}
|
|
||||||
size_t Count = FullSize / (FullSize / ChunkSize);
|
|
||||||
size_t LastChunkSize = FullSize - (Count * ChunkSize);
|
|
||||||
return { Count, LastChunkSize };
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("SplitIntoChunks") {
|
|
||||||
size_t FullSize;
|
|
||||||
size_t ChunkSize;
|
|
||||||
SUBCASE("Normal case") {
|
|
||||||
FullSize = 1234567;
|
|
||||||
ChunkSize = 1234;
|
|
||||||
}
|
|
||||||
SUBCASE("Zero original size") {
|
|
||||||
FullSize = 0;
|
|
||||||
ChunkSize = 100;
|
|
||||||
}
|
|
||||||
SUBCASE("Equal full size and chunk size") {
|
|
||||||
FullSize = 125;
|
|
||||||
ChunkSize = 125;
|
|
||||||
}
|
|
||||||
SUBCASE("Even split") {
|
|
||||||
FullSize = 10000;
|
|
||||||
ChunkSize = 100;
|
|
||||||
}
|
|
||||||
SUBCASE("Odd split") {
|
|
||||||
FullSize = 13;
|
|
||||||
ChunkSize = 2;
|
|
||||||
}
|
|
||||||
SUBCASE("Large sizes") {
|
|
||||||
FullSize = 10 * GB;
|
|
||||||
ChunkSize = 125 * MB;
|
|
||||||
}
|
|
||||||
auto [Count, LastSize] = SplitIntoChunks(FullSize, ChunkSize);
|
|
||||||
CHECK((Count * ChunkSize) + LastSize == FullSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size) {
|
|
||||||
if (TCPSendRaw(c, Socket, reinterpret_cast<char*>(DataPtr), Size)) {
|
|
||||||
return DataPtr + Size;
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) {
|
void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) {
|
||||||
std::ifstream f(Name.c_str(), std::ios::binary);
|
std::ifstream f(Name.c_str(), std::ios::binary);
|
||||||
auto Buf = f.rdbuf();
|
|
||||||
uint32_t Split = 125 * MB;
|
uint32_t Split = 125 * MB;
|
||||||
std::vector<uint8_t> Data;
|
char* Data;
|
||||||
if (Size > Split)
|
if (Size > Split)
|
||||||
Data.resize(Split);
|
Data = new char[Split];
|
||||||
else
|
else
|
||||||
Data.resize(Size);
|
Data = new char[Size];
|
||||||
SOCKET TCPSock;
|
SOCKET TCPSock;
|
||||||
if (D)
|
if (D)
|
||||||
TCPSock = c.GetDownSock();
|
TCPSock = c.GetDownSock();
|
||||||
@@ -839,8 +778,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
size_t Diff = Size - Sent;
|
size_t Diff = Size - Sent;
|
||||||
if (Diff > Split) {
|
if (Diff > Split) {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Split);
|
f.read(Data, Split);
|
||||||
if (!TCPSendRaw(c, TCPSock, reinterpret_cast<char*>(Data.data()), Split)) {
|
if (!TCPSendRaw(c, TCPSock, Data, Split)) {
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
break;
|
break;
|
||||||
@@ -848,8 +787,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
Sent += Split;
|
Sent += Split;
|
||||||
} else {
|
} else {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Diff);
|
f.read(Data, Diff);
|
||||||
if (!TCPSendRaw(c, TCPSock, reinterpret_cast<char*>(Data.data()), int32_t(Diff))) {
|
if (!TCPSendRaw(c, TCPSock, Data, int32_t(Diff))) {
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
break;
|
break;
|
||||||
@@ -857,6 +796,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
Sent += Diff;
|
Sent += Diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] Data;
|
||||||
|
f.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
||||||
|
|||||||
+2
-1
@@ -10,6 +10,7 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
|
|||||||
Application::SetSubsystemStatus("PPSMonitor", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("PPSMonitor", Application::Status::ShuttingDown);
|
||||||
if (mThread.joinable()) {
|
if (mThread.joinable()) {
|
||||||
beammp_debug("shutting down PPSMonitor");
|
beammp_debug("shutting down PPSMonitor");
|
||||||
|
mShutdown = true;
|
||||||
mThread.join();
|
mThread.join();
|
||||||
beammp_debug("shut down PPSMonitor");
|
beammp_debug("shut down PPSMonitor");
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,7 @@ void TPPSMonitor::operator()() {
|
|||||||
beammp_debug("PPSMonitor starting");
|
beammp_debug("PPSMonitor starting");
|
||||||
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
|
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
|
||||||
std::vector<std::shared_ptr<TClient>> TimedOutClients;
|
std::vector<std::shared_ptr<TClient>> TimedOutClients;
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!mShutdown) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
int C = 0, V = 0;
|
int C = 0, V = 0;
|
||||||
if (mServer.ClientCount() == 0) {
|
if (mServer.ClientCount() == 0) {
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
#include "TPluginMonitor.h"
|
|
||||||
|
|
||||||
#include "TLuaEngine.h"
|
|
||||||
|
|
||||||
TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr<TLuaEngine> Engine)
|
|
||||||
: mEngine(Engine)
|
|
||||||
, mPath(Path) {
|
|
||||||
Application::SetSubsystemStatus("PluginMonitor", Application::Status::Starting);
|
|
||||||
if (!fs::exists(mPath)) {
|
|
||||||
fs::create_directories(mPath);
|
|
||||||
}
|
|
||||||
for (const auto& Entry : fs::recursive_directory_iterator(mPath)) {
|
|
||||||
// TODO: trigger an event when a subfolder file changes
|
|
||||||
if (Entry.is_regular_file()) {
|
|
||||||
mFileTimes[Entry.path().string()] = fs::last_write_time(Entry.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Application::RegisterShutdownHandler([this] {
|
|
||||||
if (mThread.joinable()) {
|
|
||||||
mThread.join();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPluginMonitor::operator()() {
|
|
||||||
RegisterThread("PluginMonitor");
|
|
||||||
beammp_info("PluginMonitor started");
|
|
||||||
Application::SetSubsystemStatus("PluginMonitor", Application::Status::Good);
|
|
||||||
while (!Application::IsShuttingDown()) {
|
|
||||||
std::vector<std::string> ToRemove;
|
|
||||||
for (const auto& Pair : mFileTimes) {
|
|
||||||
try {
|
|
||||||
auto CurrentTime = fs::last_write_time(Pair.first);
|
|
||||||
if (CurrentTime > Pair.second) {
|
|
||||||
mFileTimes[Pair.first] = CurrentTime;
|
|
||||||
// grandparent of the path should be Resources/Server
|
|
||||||
if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) {
|
|
||||||
beammp_infof("File \"{}\" changed, reloading", Pair.first);
|
|
||||||
// is in root folder, so reload
|
|
||||||
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
|
|
||||||
auto Size = std::filesystem::file_size(Pair.first);
|
|
||||||
auto Contents = std::make_shared<std::string>();
|
|
||||||
Contents->resize(Size);
|
|
||||||
FileStream.read(Contents->data(), Contents->size());
|
|
||||||
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
|
|
||||||
auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
|
||||||
auto Res = mEngine->EnqueueScript(StateID, Chunk);
|
|
||||||
Res->WaitUntilReady();
|
|
||||||
if (Res->Error) {
|
|
||||||
beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage);
|
|
||||||
} else {
|
|
||||||
mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit"));
|
|
||||||
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// is in subfolder, dont reload, just trigger an event
|
|
||||||
beammp_debugf("File \"{}\" changed, not reloading because it's in a subdirectory. Triggering 'onFileChanged' event instead", Pair.first);
|
|
||||||
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
ToRemove.push_back(Pair.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Application::SleepSafeSeconds(3);
|
|
||||||
for (const auto& File : ToRemove) {
|
|
||||||
mFileTimes.erase(File);
|
|
||||||
beammp_warnf("File \"{}\" couldn't be accessed, so it was removed from plugin hot reload monitor (probably got deleted)", File);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Application::SetSubsystemStatus("PluginMonitor", Application::Status::Shutdown);
|
|
||||||
}
|
|
||||||
@@ -28,9 +28,8 @@ TResourceManager::TResourceManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mModsLoaded) {
|
if (mModsLoaded)
|
||||||
beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods");
|
beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods");
|
||||||
}
|
|
||||||
|
|
||||||
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -1,14 +1,30 @@
|
|||||||
#include "VehicleData.h"
|
#include "VehicleData.h"
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include <regex>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
TVehicleData::TVehicleData(int ID, std::string Data)
|
TVehicleData::TVehicleData(int ID, const std::string& Data)
|
||||||
: mID(ID)
|
: mID(ID)
|
||||||
, mData(std::move(Data)) {
|
, mData(Data) {
|
||||||
|
try {
|
||||||
|
std::regex Reg(R"(^[a-zA-Z0-9_\-.]+:[a-zA-Z0-9_\-.]+:[a-zA-Z0-9_\-.]+:\d+\-\d+:(\{.+\}))", std::regex::ECMAScript);
|
||||||
|
std::smatch Match;
|
||||||
|
std::string Result;
|
||||||
|
if (std::regex_search(Data, Match, Reg) && Match.size() > 1) {
|
||||||
|
Result = Match.str(1);
|
||||||
|
mJson = json::parse(Result);
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
beammp_debugf("Failed to parse vehicle data for vehicle {} as json, this isn't expected: {}", ID, e.what());
|
||||||
|
}
|
||||||
beammp_trace("vehicle " + std::to_string(mID) + " constructed");
|
beammp_trace("vehicle " + std::to_string(mID) + " constructed");
|
||||||
}
|
}
|
||||||
|
|
||||||
TVehicleData::~TVehicleData() {
|
TVehicleData::~TVehicleData() {
|
||||||
beammp_trace("vehicle " + std::to_string(mID) + " destroyed");
|
beammp_trace("vehicle " + std::to_string(mID) + " destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const json& TVehicleData::Json() const {
|
||||||
|
return mJson;
|
||||||
|
}
|
||||||
|
|||||||
+15
-10
@@ -11,14 +11,13 @@
|
|||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "TPPSMonitor.h"
|
#include "TPPSMonitor.h"
|
||||||
#include "TPluginMonitor.h"
|
|
||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
#include "TScopedTimer.h"
|
#include "TScopedTimer.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
||||||
static const std::string sCommandlineArguments = R"(
|
static const std::string sCommandlineArguments = R"(
|
||||||
USAGE:
|
USAGE:
|
||||||
BeamMP-Server [arguments]
|
BeamMP-Server [arguments]
|
||||||
@@ -45,6 +44,10 @@ EXAMPLES:
|
|||||||
'MyWestCoastServerConfig.toml'.
|
'MyWestCoastServerConfig.toml'.
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
// this is provided by the build system, leave empty for source builds
|
||||||
|
// global, yes, this is ugly, no, it cant be done another way
|
||||||
|
TSentry Sentry {};
|
||||||
|
|
||||||
struct MainArguments {
|
struct MainArguments {
|
||||||
int argc {};
|
int argc {};
|
||||||
char** argv {};
|
char** argv {};
|
||||||
@@ -69,7 +72,7 @@ int main(int argc, char** argv) {
|
|||||||
Sentry.LogException(e, _file_basename, _line);
|
Sentry.LogException(e, _file_basename, _line);
|
||||||
MainRet = -1;
|
MainRet = -1;
|
||||||
}
|
}
|
||||||
std::exit(MainRet);
|
return MainRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int BeamMPServerMain(MainArguments Arguments) {
|
int BeamMPServerMain(MainArguments Arguments) {
|
||||||
@@ -134,9 +137,9 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
|
|
||||||
TServer Server(Arguments.List);
|
TServer Server(Arguments.List);
|
||||||
TConfig Config(ConfigPath);
|
TConfig Config(ConfigPath);
|
||||||
auto LuaEngine = std::make_shared<TLuaEngine>();
|
TLuaEngine LuaEngine;
|
||||||
LuaEngine->SetServer(&Server);
|
LuaEngine.SetServer(&Server);
|
||||||
Application::Console().InitializeLuaConsole(*LuaEngine);
|
Application::Console().InitializeLuaConsole(LuaEngine);
|
||||||
|
|
||||||
if (Config.Failed()) {
|
if (Config.Failed()) {
|
||||||
beammp_info("Closing in 10 seconds");
|
beammp_info("Closing in 10 seconds");
|
||||||
@@ -156,14 +159,16 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
TPPSMonitor PPSMonitor(Server);
|
TPPSMonitor PPSMonitor(Server);
|
||||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||||
LuaEngine->SetNetwork(&Network);
|
LuaEngine.SetNetwork(&Network);
|
||||||
PPSMonitor.SetNetwork(Network);
|
PPSMonitor.SetNetwork(Network);
|
||||||
Application::CheckForUpdates();
|
Application::CheckForUpdates();
|
||||||
|
|
||||||
TPluginMonitor PluginMonitor(fs::path(Application::Settings.Resource) / "Server", LuaEngine);
|
std::unique_ptr<Http::Server::THttpServerInstance> HttpServer;
|
||||||
|
|
||||||
if (Application::Settings.HTTPServerEnabled) {
|
if (Application::Settings.HTTPServerEnabled) {
|
||||||
Http::Server::THttpServerInstance HttpServerInstance {};
|
HttpServer = std::make_unique<Http::Server::THttpServerInstance>(
|
||||||
|
Server,
|
||||||
|
Network,
|
||||||
|
ResourceManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#define DOCTEST_CONFIG_IMPLEMENT
|
|
||||||
#include <doctest/doctest.h>
|
|
||||||
|
|
||||||
#include <Common.h>
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
doctest::Context context;
|
|
||||||
|
|
||||||
// Application::InitializeConsole();
|
|
||||||
|
|
||||||
context.applyCommandLine(argc, argv);
|
|
||||||
|
|
||||||
int res = context.run(); // run
|
|
||||||
|
|
||||||
if (context.shouldExit()) // important - query flags (and --exit) rely on the user doing this
|
|
||||||
return res; // propagate the result of the tests
|
|
||||||
|
|
||||||
int client_stuff_return_code = 0;
|
|
||||||
// your program - if the testing framework is integrated in your production code
|
|
||||||
|
|
||||||
return res + client_stuff_return_code; // the result from doctest is propagated here as well
|
|
||||||
}
|
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
message(STATUS "Getting, checking and running vcpkg, this may take a while")
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
vcpkg
|
||||||
|
GIT_REPOSITORY https://github.com/microsoft/vcpkg.git
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_GetProperties(vcpkg)
|
||||||
|
|
||||||
|
if(NOT vcpkg_POPULATED)
|
||||||
|
FetchContent_Populate(vcpkg)
|
||||||
|
execute_process(COMMAND ./${vcpkg_SOURCE_DIR}/bootstrap-vcpkg.sh)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE ${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake
|
||||||
|
CACHE STRING "Vcpkg toolchain file")
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "beammp-server",
|
||||||
|
"version-string": "3.0.2",
|
||||||
|
"homepage": "https://beammp.com",
|
||||||
|
"dependencies": [
|
||||||
|
"sentry-native",
|
||||||
|
"lua",
|
||||||
|
"zlib",
|
||||||
|
"rapidjson",
|
||||||
|
"nlohmann-json",
|
||||||
|
"openssl",
|
||||||
|
"curl",
|
||||||
|
"sol2",
|
||||||
|
"cpp-httplib",
|
||||||
|
"toml11"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user