mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 10:13:42 +00:00
Compare commits
223 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 588242822c | |||
| 58da200901 | |||
| f4ccf6c177 | |||
| 27103a73a9 | |||
| 2727f90430 | |||
| 2a96546c8c | |||
| 6462636b29 | |||
| 5742ab0dad | |||
| 0087205d55 | |||
| d16843e45d | |||
| 24516dbfd7 | |||
| a311d58e11 | |||
| 1444d91e7e | |||
| 1e2f060107 | |||
| fdb5da2ed6 | |||
| de57613326 | |||
| b49782e8a3 | |||
| ff80b4cf63 | |||
| 4c23b78f84 | |||
| aca3c52c20 | |||
| 51d6c4fb0a | |||
| 2af9491fd6 | |||
| 95c036836e | |||
| a7f2f85e45 | |||
| 42c5aaad5a | |||
| 88684bd9af | |||
| c6457f7df4 | |||
| ba3fd0e144 | |||
| e3b6fd7998 | |||
| 943159cd40 | |||
| 9423831937 | |||
| 95188042c5 | |||
| a0a7b8ecce | |||
| 53617abae4 | |||
| 853b078124 | |||
| 549517c518 | |||
| 2be4b8fd91 | |||
| 2cfb27820a | |||
| 1ff12cb2bf | |||
| 518cb0664e | |||
| 80432eb718 | |||
| b1caf5c29a | |||
| 950cee9fd0 | |||
| 046097579e | |||
| cacdc004da | |||
| 8250d5876f | |||
| a7b02c459e | |||
| 0d5ef404f6 | |||
| 07cf7d7c21 | |||
| 809a851c71 | |||
| fe36191baf | |||
| 7d137eb496 | |||
| fd6234bd21 | |||
| 9f0b057c14 | |||
| 0143748953 | |||
| 8ec90d5186 | |||
| d054214b7f | |||
| 003a8269aa | |||
| 59b1b45625 | |||
| 15e5cee166 | |||
| 12123582ad | |||
| 3fb227e468 | |||
| 31e9004011 | |||
| f98c8dabb0 | |||
| e8665bfb72 | |||
| 3d13381abd | |||
| 5352e4ff03 | |||
| c571e218c7 | |||
| 5725717e29 | |||
| 7f5447f25e | |||
| b33b396089 | |||
| ff3cbebac0 | |||
| 0f9a994c10 | |||
| c4b72be50a | |||
| 5a3140c84a | |||
| bea8006a26 | |||
| a2dc42c5f5 | |||
| 4b92532203 | |||
| 683e13a4a0 | |||
| 9d6dbefb9d | |||
| 981b56b846 | |||
| 9f52ab2e54 | |||
| 8fada3ac04 | |||
| 5330013dc3 | |||
| c7e0461a86 | |||
| 032c1daf30 | |||
| d1efebe068 | |||
| c77e2b3fd8 | |||
| e92847e628 | |||
| afb18ccff7 | |||
| 4659a9362d | |||
| fe6e1e6266 | |||
| c0faff5b05 | |||
| f4ffa2cdda | |||
| 1409d4ef80 | |||
| 51e662fdda | |||
| 72950fdab2 | |||
| b9f594896a | |||
| 8551e56e42 | |||
| f4fc182d5e | |||
| ee1e948a65 | |||
| e3081a971e | |||
| 7c5bf9473e | |||
| 5a44a8e9c5 | |||
| d1a0eaffab | |||
| 10322bf24e | |||
| 969cd93358 | |||
| b3a8b1a682 | |||
| 2774a73d83 | |||
| 739eaad199 | |||
| b53b72d604 | |||
| 85fd9e9ee3 | |||
| 57e6e98423 | |||
| e3c6bdb50d | |||
| 1f89b202b4 | |||
| 0a31107e56 | |||
| 9237f0dd43 | |||
| ce834a634c | |||
| f65607cb00 | |||
| b0475f262f | |||
| cee824ad46 | |||
| 530e977d9d | |||
| 807cb68f0f | |||
| 3850740ded | |||
| cfc7b302fe | |||
| a7c28a8d0d | |||
| 9fe1a94d42 | |||
| 1eee56a666 | |||
| 28fe6e9634 | |||
| 4512f44ea1 | |||
| 3efd31bc84 | |||
| 5684134894 | |||
| e6c97de3c4 | |||
| f550d0bba9 | |||
| 2b4fec6d11 | |||
| 550c658ac5 | |||
| da41862f49 | |||
| d5769ce9be | |||
| 4bf9244005 | |||
| 89f63024ab | |||
| f258678751 | |||
| 4d2f68068d | |||
| 9f636345ef | |||
| 3d0d5e9e4c | |||
| a1ca8e0576 | |||
| b22f21a6b2 | |||
| 531a901431 | |||
| 46f778bd01 | |||
| f3b6eea418 | |||
| bceb3aefe4 | |||
| 6c72432992 | |||
| 17d3f303ca | |||
| 2cbcf96282 | |||
| 7d4fd44dbf | |||
| 71c2af1224 | |||
| 2e112fc5f1 | |||
| 96c93a6aa6 | |||
| 9dbb91fd84 | |||
| 26c33ae2fb | |||
| 3eb943309e | |||
| 3c8e8399cb | |||
| 5b500a3da5 | |||
| ade19123b7 | |||
| 5ee18e0576 | |||
| 77d23caa63 | |||
| 79856cde8e | |||
| 7bc230974a | |||
| b1ab7e65da | |||
| c82c53e3d1 | |||
| 9e861ab993 | |||
| ae11ba5f0d | |||
| 1427966d1a | |||
| db92f2867d | |||
| 208a41d45f | |||
| f2915f9c2a | |||
| 1abf6d5adf | |||
| f626474b4f | |||
| 6f3960d2c2 | |||
| 6cb19a7991 | |||
| 4fe3c50edd | |||
| fe7335fb0e | |||
| 40cd203047 | |||
| 1fc03500f0 | |||
| 73729746ff | |||
| ffdf4dce60 | |||
| bb5d7fdcf4 | |||
| 2df2475b59 | |||
| bb32b9cfea | |||
| 3f034264f9 | |||
| c67fffed58 | |||
| f50b821733 | |||
| 1e5d19bca4 | |||
| 1011de1971 | |||
| 7336d63f58 | |||
| c5d7369088 | |||
| c3a463552f | |||
| ae9462898e | |||
| 529b7e2ae4 | |||
| 1bee72a175 | |||
| f1e1b6cc28 | |||
| 573bd77bbd | |||
| 770e03e3c6 | |||
| 11d4522dc7 | |||
| 830bc47987 | |||
| bec698fbdc | |||
| 60cc835daf | |||
| a85ce18589 | |||
| 1228c2fabe | |||
| 3da0af37e4 | |||
| baa41dd65a | |||
| 534b457f48 | |||
| 029cf94e68 | |||
| 15f7a6ba85 | |||
| 86b5d91579 | |||
| 31486bcb56 | |||
| 08660d83dc | |||
| 6d8f75a577 | |||
| 018246cea5 | |||
| a584e25bf3 | |||
| d4d773b769 | |||
| 56a02f0215 | |||
| 7231c69e10 | |||
| 3c68dfaeaf |
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[Bug] Enter issue title here"
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Fill out general information**
|
||||||
|
OS (windows, linux, ...):
|
||||||
|
BeamMP-Server Version:
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Do x ...
|
||||||
|
2. Do y ...
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Logs**
|
||||||
|
Please attach the `Server.log` from the run in which the issue appeared, preferably with Debug turned on in the `ServerConfig.toml`.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[Feature Request]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. For example: "I'm always frustrated when ...".
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen. Also supply OS information if relevant, for example "*On Linux*, I would like to be able to...".
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the feature request here.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
name: CMake Linux Build
|
name: CMake Linux Build
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
@@ -12,12 +12,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
env:
|
||||||
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
run: |
|
run: |
|
||||||
|
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
|
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
|
||||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||||
sudo apt-get install -y libboost1.70-dev libboost1.70
|
sudo apt-get install -y libboost1.70-dev libboost1.70
|
||||||
|
|
||||||
@@ -27,7 +30,9 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
|
env:
|
||||||
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
|
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: CMake Windows Build
|
name: CMake Windows Build
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
@@ -12,15 +12,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
||||||
uses: lukka/run-vcpkg@main
|
uses: lukka/run-vcpkg@main
|
||||||
id: runvcpkg
|
id: runvcpkg
|
||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
@@ -29,7 +29,9 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
env:
|
||||||
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
|
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: Release ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
body: |
|
body: |
|
||||||
@@ -37,12 +37,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev
|
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
|
||||||
sudo add-apt-repository ppa:mhier/libboost-latest
|
sudo add-apt-repository ppa:mhier/libboost-latest
|
||||||
sudo apt-get install -y libboost1.70-dev libboost1.70
|
sudo apt-get install -y libboost1.70-dev libboost1.70
|
||||||
|
|
||||||
@@ -52,14 +52,15 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{github.workspace}}/build-linux
|
working-directory: ${{github.workspace}}/build-linux
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
|
env:
|
||||||
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
|
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||||
|
|
||||||
- name: Build
|
- 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
|
||||||
|
|
||||||
|
|
||||||
- name: Upload Release Asset
|
- name: Upload Release Asset
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
@@ -78,15 +79,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'recursive'
|
||||||
|
|
||||||
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
- name: Restore artifacts, or run vcpkg, build and cache artifacts
|
||||||
uses: lukka/run-vcpkg@main
|
uses: lukka/run-vcpkg@main
|
||||||
id: runvcpkg
|
id: runvcpkg
|
||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
@@ -95,7 +96,9 @@ jobs:
|
|||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
env:
|
||||||
|
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
|
||||||
|
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
working-directory: ${{github.workspace}}/build-windows
|
||||||
|
|||||||
+11
@@ -1,5 +1,10 @@
|
|||||||
|
.idea/
|
||||||
|
.sentry-native/
|
||||||
|
*.orig
|
||||||
|
*.toml
|
||||||
boost_*
|
boost_*
|
||||||
Resources
|
Resources
|
||||||
|
run-in-env.sh
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
@@ -468,3 +473,9 @@ cmake-build-debug/include/commandline/Makefile
|
|||||||
*.manifest
|
*.manifest
|
||||||
*.rc
|
*.rc
|
||||||
*.res
|
*.res
|
||||||
|
BeamMP-Server
|
||||||
|
*.patch
|
||||||
|
callgrind.*
|
||||||
|
notes/*
|
||||||
|
compile_commands.json
|
||||||
|
nohup.out
|
||||||
|
|||||||
+6
-3
@@ -1,12 +1,15 @@
|
|||||||
[submodule "include/commandline"]
|
[submodule "include/commandline"]
|
||||||
path = include/commandline
|
path = include/commandline
|
||||||
url = https://github.com/lionkor/commandline
|
url = https://github.com/lionkor/commandline
|
||||||
[submodule "socket.io-client-cpp"]
|
|
||||||
path = socket.io-client-cpp
|
|
||||||
url = https://github.com/socketio/socket.io-client-cpp
|
|
||||||
[submodule "asio"]
|
[submodule "asio"]
|
||||||
path = asio
|
path = asio
|
||||||
url = https://github.com/chriskohlhoff/asio
|
url = https://github.com/chriskohlhoff/asio
|
||||||
[submodule "rapidjson"]
|
[submodule "rapidjson"]
|
||||||
path = rapidjson
|
path = rapidjson
|
||||||
url = https://github.com/Tencent/rapidjson
|
url = https://github.com/Tencent/rapidjson
|
||||||
|
[submodule "include/toml11"]
|
||||||
|
path = include/toml11
|
||||||
|
url = https://github.com/ToruNiina/toml11
|
||||||
|
[submodule "include/sentry-native"]
|
||||||
|
path = include/sentry-native
|
||||||
|
url = https://github.com/getsentry/sentry-native
|
||||||
Generated
-1
@@ -1 +0,0 @@
|
|||||||
Server
|
|
||||||
Generated
-4
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
|
||||||
</project>
|
|
||||||
Generated
-16
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/asio" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/include/commandline" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/rapidjson" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/rapidjson/thirdparty/gtest" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/asio" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/catch" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson/thirdparty/gtest" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/websocketpp" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
+81
-8
@@ -1,10 +1,36 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(Server)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message(STATUS "You can find build instructions and a list of dependencies in the README at \
|
||||||
|
https://github.com/BeamMP/BeamMP-Server")
|
||||||
|
|
||||||
|
project(BeamMP-Server
|
||||||
|
DESCRIPTION "Server for BeamMP - The Multiplayer Mod for BeamNG.drive"
|
||||||
|
HOMEPAGE_URL https://beammp.com
|
||||||
|
LANGUAGES CXX C)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
# this has to happen before sentry, so that crashpad on windows links with these settings.
|
||||||
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
|
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})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories("include/sentry-native/include")
|
||||||
|
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||||
|
if (MSVC)
|
||||||
|
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||||
|
endif()
|
||||||
|
set(SENTRY_BACKEND breakpad)
|
||||||
|
add_subdirectory("include/sentry-native")
|
||||||
|
|
||||||
|
message(STATUS "Setting compiler flags")
|
||||||
|
if (WIN32)
|
||||||
|
|
||||||
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
|
||||||
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
|
||||||
include_directories(${VcpkgRoot}/include)
|
include_directories(${VcpkgRoot}/include)
|
||||||
@@ -15,21 +41,36 @@ elseif (UNIX)
|
|||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
|
||||||
if (SANITIZE)
|
if (SANITIZE)
|
||||||
message(STATUS "sanitize is ON")
|
message(STATUS "sanitize is ON")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLanAGS} -fsanitize=undefined,thread")
|
||||||
endif (SANITIZE)
|
endif (SANITIZE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
message(STATUS "Checking for Sentry URL")
|
||||||
|
# this is set by the build system.
|
||||||
|
# IMPORTANT: if you're building from source, just leave this empty
|
||||||
|
if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL)
|
||||||
|
message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \
|
||||||
|
This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.")
|
||||||
|
set(BEAMMP_SECRET_SENTRY_URL "")
|
||||||
|
else()
|
||||||
|
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
|
||||||
|
message(STATUS "Sentry URL is length ${URL_LEN}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Adding local source dependencies")
|
||||||
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
||||||
include_directories("asio/asio/include")
|
include_directories("asio/asio/include")
|
||||||
include_directories("rapidjson/include")
|
include_directories("rapidjson/include")
|
||||||
include_directories("websocketpp")
|
include_directories("websocketpp")
|
||||||
add_subdirectory("socket.io-client-cpp")
|
|
||||||
add_subdirectory("include/commandline")
|
add_subdirectory("include/commandline")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||||
|
|
||||||
|
message(STATUS "Looking for Boost")
|
||||||
find_package(Boost REQUIRED COMPONENTS system thread)
|
find_package(Boost REQUIRED COMPONENTS system thread)
|
||||||
|
|
||||||
add_executable(BeamMP-Server
|
add_executable(BeamMP-Server
|
||||||
@@ -46,23 +87,55 @@ add_executable(BeamMP-Server
|
|||||||
include/TResourceManager.h src/TResourceManager.cpp
|
include/TResourceManager.h src/TResourceManager.cpp
|
||||||
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
||||||
include/Http.h src/Http.cpp
|
include/Http.h src/Http.cpp
|
||||||
#include/SocketIO.h src/SocketIO.cpp
|
include/TSentry.h src/TSentry.cpp
|
||||||
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
||||||
include/TNetwork.h src/TNetwork.cpp)
|
include/TNetwork.h src/TNetwork.cpp)
|
||||||
|
|
||||||
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
|
||||||
|
|
||||||
|
target_include_directories(BeamMP-Server PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||||
|
|
||||||
|
message(STATUS "Looking for Lua")
|
||||||
find_package(Lua REQUIRED)
|
find_package(Lua REQUIRED)
|
||||||
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src")
|
target_include_directories(BeamMP-Server PUBLIC
|
||||||
|
${Boost_INCLUDE_DIRS}
|
||||||
|
${LUA_INCLUDE_DIR}
|
||||||
|
${CURL_INCLUDE_DIRS}
|
||||||
|
"include/tomlplusplus"
|
||||||
|
"include/sentry-native/include"
|
||||||
|
"include/curl/include")
|
||||||
|
|
||||||
|
message(STATUS "Looking for SSL")
|
||||||
|
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
|
message(STATUS "CURL IS ${CURL_LIBRARIES}")
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline sioclient_tls)
|
target_link_libraries(BeamMP-Server
|
||||||
|
z
|
||||||
|
pthread
|
||||||
|
stdc++fs
|
||||||
|
${LUA_LIBRARIES}
|
||||||
|
crypto
|
||||||
|
${OPENSSL_LIBRARIES}
|
||||||
|
commandline
|
||||||
|
sentry)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
include(FindLua)
|
include(FindLua)
|
||||||
|
message(STATUS "Looking for libz")
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
message(STATUS "Looking for RapidJSON")
|
||||||
find_package(RapidJSON CONFIG REQUIRED)
|
find_package(RapidJSON CONFIG REQUIRED)
|
||||||
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
||||||
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls)
|
target_link_libraries(BeamMP-Server PRIVATE
|
||||||
|
ws2_32
|
||||||
|
ZLIB::ZLIB
|
||||||
|
${LUA_LIBRARIES}
|
||||||
|
${OPENSSL_LIBRARIES}
|
||||||
|
commandline
|
||||||
|
sentry)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# v2.3.0
|
||||||
|
|
||||||
|
- ADDED version check - the server will now let you know when a new release is out
|
||||||
|
- ADDED logging of various errors, crashes and exceptions to the backend
|
||||||
|
- ADDED chat messages are now logged to the server console as [CHAT]
|
||||||
|
- ADDED debug message telling you when the server heartbeats to the backend
|
||||||
|
- REMOVED various [DEBUG] messages which were confusing (such as "breaking client loop")
|
||||||
|
- FIXED various crashes and issues with handling unexpected backend responses
|
||||||
|
- FIXED minor bugs due to code correctness
|
||||||
|
|
||||||
|
# v2.2.0
|
||||||
|
|
||||||
|
- FIXED major security flaw
|
||||||
|
- FIXED minor bugs
|
||||||
|
|
||||||
|
# v2.1.4
|
||||||
|
|
||||||
|
- ADDED debug heartbeat print
|
||||||
|
- ADDED kicking every player before shutdown
|
||||||
|
- FIXED rare bug which led to violent crash
|
||||||
|
- FIXED minor bugs
|
||||||
|
|
||||||
|
# v2.1.3
|
||||||
|
|
||||||
|
- FIXED Lua events not cancelling properly on Linux
|
||||||
|
|
||||||
|
# v2.1.2
|
||||||
|
|
||||||
|
- CHANGED default map to gridmap v2
|
||||||
|
- FIXED version number display
|
||||||
|
|
||||||
|
# v2.1.1
|
||||||
|
# v2.1.0 (pre-v2.1.1)
|
||||||
|
# v2.0.4 (pre-v2.1.0)
|
||||||
|
|
||||||
|
- REMOVED boost as a runtime dependency
|
||||||
|
- FIXED Lua plugins on Linux
|
||||||
|
- FIXED console history on Windows
|
||||||
|
- CHANGED to new config format TOML
|
||||||
|
|
||||||
|
# v2.0.3
|
||||||
|
|
||||||
|
- WORKAROUND for timeout bug / ghost player bug
|
||||||
|
- FIXED 100% CPU spin when stdin is /dev/null.
|
||||||
|
|
||||||
|
# v2.0.2
|
||||||
|
|
||||||
|
- ADDED fully new commandline
|
||||||
|
- ADDED new backend
|
||||||
|
- ADDED automated build system
|
||||||
|
- ADDED lua GetPlayerIdentifiers
|
||||||
|
- ADDED lots of debug info
|
||||||
|
- ADDED better POSTing and GETing
|
||||||
|
- ADDED a license
|
||||||
|
- FIXED ghost players in player list issue
|
||||||
|
- FIXED ghost vehicle after joining issue
|
||||||
|
- FIXED missing vehicle after joining issue
|
||||||
|
- FIXED a lot of desync issues
|
||||||
|
- FIXED some memory leaks
|
||||||
|
- FIXED various crashes
|
||||||
|
- FIXED various data-races
|
||||||
|
- FIXED some linux-specific crashes
|
||||||
|
- FIXED some linux-specific issues
|
||||||
|
- FIXED bug which caused kicking to be logged as leaving
|
||||||
|
- FIXED various internal developer quality-of-life things
|
||||||
@@ -1 +1,2 @@
|
|||||||
Copyright (c) 2019-present Anonymous275. BeamMP Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries, the only permission that has been granted is to use the software in its compiled form as distributed from the BeamMP.com website. Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
|
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor). BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
|
||||||
|
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.
|
||||||
|
|||||||
@@ -6,9 +6,30 @@
|
|||||||
This is the server for the multiplayer mod **[BeamMP](https://beammp.com/)** for the game [BeamNG.drive](https://www.beamng.com/).
|
This is the server for the multiplayer mod **[BeamMP](https://beammp.com/)** for the game [BeamNG.drive](https://www.beamng.com/).
|
||||||
The server is the point throug which all clients communicate. You can write lua mods for the server, detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com).
|
The server is the point throug which all clients communicate. You can write lua mods for the server, detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com).
|
||||||
|
|
||||||
|
## Minimum Requirements
|
||||||
|
|
||||||
|
These values are guesstimated and are subject to change with each release.
|
||||||
|
|
||||||
|
* RAM: 50+ MiB usable (not counting OS overhead)
|
||||||
|
* CPU: >1GHz, preferably multicore
|
||||||
|
* OS: Windows, Linux (theoretically any POSIX)
|
||||||
|
* GPU: None
|
||||||
|
* HDD: 10 MiB + Mods/Plugins
|
||||||
|
* Bandwidth: 5-10 Mb/s upload
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" label or with nobody assigned, any [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer) cards in the "To-Do" column.
|
||||||
|
|
||||||
|
To contribute, look at the active [issues](https://github.com/BeamMP/BeamMP-Server/issues) and at the [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer). Any issues that have the "help wanted" label or don't have anyone assigned and any trello cards that aren't assigned or in the "In-Progress" section are good tasks to take on. You can either contribute by programming or by testing and adding more info and ideas.
|
||||||
|
|
||||||
|
Fork this repository, make a new branch for your feature, implement your feature or fix, and then create a pull-request here. Even incomplete features and fixes can be pull-requested.
|
||||||
|
|
||||||
|
If you need support with understanding the codebase, please write us in the discord. You'll need to be proficient in modern C++.
|
||||||
|
|
||||||
## About Building from Source
|
## About Building from Source
|
||||||
|
|
||||||
We only allow building unmodified (original) source code. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`.
|
We only allow building unmodified (original) source code for public use. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`.
|
||||||
|
|
||||||
## Supported Operating Systems
|
## Supported Operating Systems
|
||||||
|
|
||||||
@@ -40,27 +61,34 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
|
|||||||
- `make`
|
- `make`
|
||||||
- `cmake`
|
- `cmake`
|
||||||
- `g++`
|
- `g++`
|
||||||
|
- `libcurl4-openssl-dev` or similar (search for `libcurl` and look for one with `-dev`)
|
||||||
|
|
||||||
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
|
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
|
||||||
- `liblua5.3`
|
- `liblua5.3-dev`
|
||||||
|
|
||||||
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
|
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
|
||||||
You can also use any version of `libluajit`, but the same applies regarding the version.
|
You can also use any version of `libluajit`, but the same applies regarding the version.
|
||||||
- `libz-dev`
|
- `libz-dev`
|
||||||
- `rapidjson-dev`
|
- `rapidjson-dev`
|
||||||
- `libboost1.70-dev`
|
- `libopenssl-dev` or `libssl-dev`
|
||||||
|
|
||||||
If your distro doesn't have 1.7x version of libboost, you'll have to compile it from source or find another way to get it for your distro.
|
**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
|
||||||
- `libopenssl-dev`
|
If you can't find this version of boost (only 1.6x, for example), you can either update to a newer version of your distro, build boost yourself, or use an unstable rolling release (like Debian `sid` aka `unstable`).
|
||||||
|
|
||||||
|
In the end you should end up with a command something like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev libboost1.71-all-dev
|
||||||
|
```
|
||||||
|
|
||||||
### How to build
|
### How to build
|
||||||
|
|
||||||
On windows. use git-bash for these commands.
|
On windows, use git-bash for these commands. On Linux, these should work in your shell.
|
||||||
|
|
||||||
1. Make sure you have all [prerequisites](#prerequisites) installed
|
1. Make sure you have all [prerequisites](#prerequisites) installed
|
||||||
2. Clone the repository in a location of your choice with `git clone --recursive 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. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20.
|
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. `cd` into it with `cd BeamMP-Server`
|
4. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
|
||||||
5. Run `cmake .` (with `.`)
|
5. Run `cmake .` (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.
|
||||||
@@ -71,3 +99,4 @@ On windows. use git-bash for these commands.
|
|||||||
|
|
||||||
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor).
|
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor).
|
||||||
BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
|
BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
|
||||||
|
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.
|
||||||
|
|||||||
+27
-10
@@ -12,12 +12,23 @@
|
|||||||
|
|
||||||
class TServer;
|
class TServer;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// for socklen_t
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
|
struct TConnection final {
|
||||||
|
SOCKET Socket;
|
||||||
|
struct sockaddr SockAddr;
|
||||||
|
socklen_t SockAddrLen;
|
||||||
|
};
|
||||||
|
|
||||||
class TClient final {
|
class TClient final {
|
||||||
public:
|
public:
|
||||||
using TSetOfVehicleData = std::unordered_set<TVehicleData>;
|
using TSetOfVehicleData = std::vector<TVehicleData>;
|
||||||
|
|
||||||
struct TVehicleDataLockPair {
|
struct TVehicleDataLockPair {
|
||||||
TSetOfVehicleData& VehicleData;
|
TSetOfVehicleData* VehicleData;
|
||||||
std::unique_lock<std::mutex> Lock;
|
std::unique_lock<std::mutex> Lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,25 +41,28 @@ public:
|
|||||||
TVehicleDataLockPair GetAllCars();
|
TVehicleDataLockPair GetAllCars();
|
||||||
void SetName(const std::string& Name) { mName = Name; }
|
void SetName(const std::string& Name) { mName = Name; }
|
||||||
void SetRoles(const std::string& Role) { mRole = Role; }
|
void SetRoles(const std::string& Role) { mRole = Role; }
|
||||||
void AddIdentifier(const std::string& ID) { mIdentifiers.insert(ID); };
|
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
|
||||||
std::string GetCarData(int Ident);
|
std::string GetCarData(int Ident);
|
||||||
void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
|
void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
|
||||||
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
|
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
|
||||||
void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; }
|
void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; }
|
||||||
void SetStatus(int Status) { mStatus = Status; }
|
void SetStatus(int Status) { mStatus = Status; }
|
||||||
|
// locks
|
||||||
void DeleteCar(int Ident);
|
void DeleteCar(int Ident);
|
||||||
[[nodiscard]] std::set<std::string> GetIdentifiers() const { return mIdentifiers; }
|
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
||||||
[[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; }
|
[[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; }
|
||||||
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
|
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
|
||||||
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
|
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
|
||||||
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
||||||
[[nodiscard]] std::string GetName() const { return mName; }
|
[[nodiscard]] std::string GetName() const { return mName; }
|
||||||
|
void SetUnicycleID(int ID) { mUnicycleID = ID; }
|
||||||
void SetID(int ID) { mID = ID; }
|
void SetID(int ID) { mID = ID; }
|
||||||
[[nodiscard]] int GetOpenCarID() const;
|
[[nodiscard]] int GetOpenCarID() const;
|
||||||
[[nodiscard]] int GetCarCount() const;
|
[[nodiscard]] int GetCarCount() const;
|
||||||
void ClearCars();
|
void ClearCars();
|
||||||
[[nodiscard]] int GetStatus() const { return mStatus; }
|
[[nodiscard]] int GetStatus() const { return mStatus; }
|
||||||
[[nodiscard]] int GetID() const { return mID; }
|
[[nodiscard]] int GetID() const { return mID; }
|
||||||
|
[[nodiscard]] int GetUnicycleID() const { return mUnicycleID; }
|
||||||
[[nodiscard]] bool IsConnected() const { return mIsConnected; }
|
[[nodiscard]] bool IsConnected() const { return mIsConnected; }
|
||||||
[[nodiscard]] bool IsSynced() const { return mIsSynced; }
|
[[nodiscard]] bool IsSynced() const { return mIsSynced; }
|
||||||
[[nodiscard]] bool IsSyncing() const { return mIsSyncing; }
|
[[nodiscard]] bool IsSyncing() const { return mIsSyncing; }
|
||||||
@@ -57,9 +71,9 @@ public:
|
|||||||
void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; }
|
void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; }
|
||||||
void SetIsSyncing(bool NewIsSyncing) { mIsSyncing = NewIsSyncing; }
|
void SetIsSyncing(bool NewIsSyncing) { mIsSyncing = NewIsSyncing; }
|
||||||
void EnqueuePacket(const std::string& Packet);
|
void EnqueuePacket(const std::string& Packet);
|
||||||
[[nodiscard]] std::queue<std::string>& MissedPacketQueue() { return mMissedPacketsDuringSyncing; }
|
[[nodiscard]] std::queue<std::string>& MissedPacketQueue() { return mPacketsSync; }
|
||||||
[[nodiscard]] const std::queue<std::string>& MissedPacketQueue() const { return mMissedPacketsDuringSyncing; }
|
[[nodiscard]] const std::queue<std::string>& MissedPacketQueue() const { return mPacketsSync; }
|
||||||
[[nodiscard]] size_t MissedPacketQueueSize() const { return mMissedPacketsDuringSyncing.size(); }
|
[[nodiscard]] size_t MissedPacketQueueSize() const { return mPacketsSync.size(); }
|
||||||
[[nodiscard]] std::mutex& MissedPacketQueueMutex() const { return mMissedPacketsMutex; }
|
[[nodiscard]] std::mutex& MissedPacketQueueMutex() const { return mMissedPacketsMutex; }
|
||||||
void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; }
|
void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; }
|
||||||
[[nodiscard]] TServer& Server() const;
|
[[nodiscard]] TServer& Server() const;
|
||||||
@@ -67,19 +81,22 @@ public:
|
|||||||
int SecondsSinceLastPing();
|
int SecondsSinceLastPing();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void InsertVehicle(int ID, const std::string& Data);
|
||||||
|
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
bool mIsConnected = false;
|
bool mIsConnected = false;
|
||||||
bool mIsSynced = false;
|
bool mIsSynced = false;
|
||||||
bool mIsSyncing = false;
|
bool mIsSyncing = false;
|
||||||
mutable std::mutex mMissedPacketsMutex;
|
mutable std::mutex mMissedPacketsMutex;
|
||||||
std::queue<std::string> mMissedPacketsDuringSyncing;
|
std::queue<std::string> mPacketsSync;
|
||||||
std::set<std::string> mIdentifiers;
|
std::unordered_map<std::string, std::string> mIdentifiers;
|
||||||
bool mIsGuest = false;
|
bool mIsGuest = false;
|
||||||
std::mutex mVehicleDataMutex;
|
std::mutex mVehicleDataMutex;
|
||||||
TSetOfVehicleData mVehicleData;
|
TSetOfVehicleData mVehicleData;
|
||||||
std::string mName = "Unknown Client";
|
std::string mName = "Unknown Client";
|
||||||
SOCKET mSocket[2] { SOCKET(-1) };
|
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
|
||||||
sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is
|
sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is
|
||||||
|
int mUnicycleID = -1;
|
||||||
std::string mRole;
|
std::string mRole;
|
||||||
std::string mDID;
|
std::string mDID;
|
||||||
int mStatus = 0;
|
int mStatus = 0;
|
||||||
|
|||||||
+63
-11
@@ -1,13 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "TSentry.h"
|
||||||
|
extern TSentry Sentry;
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "Compat.h"
|
||||||
|
|
||||||
#include "TConsole.h"
|
#include "TConsole.h"
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
uint8_t patch;
|
||||||
|
Version(uint8_t major, uint8_t minor, uint8_t patch);
|
||||||
|
Version(const std::array<uint8_t, 3>& v);
|
||||||
|
std::string AsString();
|
||||||
|
};
|
||||||
|
|
||||||
// static class handling application start, shutdown, etc.
|
// static class handling application start, shutdown, etc.
|
||||||
// yes, static classes, singletons, globals are all pretty
|
// yes, static classes, singletons, globals are all pretty
|
||||||
// bad idioms. In this case we need a central way to access
|
// bad idioms. In this case we need a central way to access
|
||||||
@@ -18,20 +35,29 @@ public:
|
|||||||
// types
|
// types
|
||||||
struct TSettings {
|
struct TSettings {
|
||||||
TSettings() noexcept
|
TSettings() noexcept
|
||||||
: DebugModeEnabled(true) { }
|
: ServerName("BeamMP Server")
|
||||||
|
, ServerDesc("BeamMP Default Description")
|
||||||
|
, Resource("Resources")
|
||||||
|
, MapName("/levels/gridmap_v2/info.json")
|
||||||
|
, MaxPlayers(10)
|
||||||
|
, Private(false)
|
||||||
|
, MaxCars(1)
|
||||||
|
, DebugModeEnabled(false)
|
||||||
|
, Port(30814) { }
|
||||||
std::string ServerName;
|
std::string ServerName;
|
||||||
std::string ServerDesc;
|
std::string ServerDesc;
|
||||||
std::string Resource;
|
std::string Resource;
|
||||||
std::string MapName;
|
std::string MapName;
|
||||||
std::string Key;
|
std::string Key;
|
||||||
int MaxPlayers {};
|
int MaxPlayers;
|
||||||
bool Private {};
|
bool Private;
|
||||||
int MaxCars {};
|
int MaxCars;
|
||||||
bool DebugModeEnabled;
|
bool DebugModeEnabled;
|
||||||
int Port {};
|
int Port;
|
||||||
std::string CustomIP;
|
std::string CustomIP;
|
||||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
using TShutdownHandler = std::function<void()>;
|
using TShutdownHandler = std::function<void()>;
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
@@ -42,26 +68,34 @@ public:
|
|||||||
// Causes all threads to finish up and exit gracefull gracefully
|
// Causes all threads to finish up and exit gracefull gracefully
|
||||||
static void GracefullyShutdown();
|
static void GracefullyShutdown();
|
||||||
static TConsole& Console() { return *mConsole; }
|
static TConsole& Console() { return *mConsole; }
|
||||||
static std::string ServerVersion() { return "2.0.1"; }
|
static std::string ServerVersionString();
|
||||||
static std::string ClientVersion() { return "2.0"; }
|
static const Version& ServerVersion() { return mVersion; }
|
||||||
|
static std::string ClientVersionString() { return "2.0"; }
|
||||||
static std::string PPS() { return mPPS; }
|
static std::string PPS() { return mPPS; }
|
||||||
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
|
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||||
|
|
||||||
static inline TSettings Settings {};
|
static inline TSettings Settings {};
|
||||||
|
|
||||||
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
||||||
static std::string GetBackendHostname() { return "backend.beammp.com"; }
|
static std::string GetBackendHostname() { return "backend.beammp.com"; }
|
||||||
|
static std::string GetBackup1Hostname() { return "backup1.beammp.com"; }
|
||||||
|
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
|
||||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||||
|
static void CheckForUpdates();
|
||||||
|
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||||
|
static bool IsOutdated(const Version& Current, const Version& Newest);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline std::string mPPS;
|
static inline std::string mPPS;
|
||||||
static std::unique_ptr<TConsole> mConsole;
|
static std::unique_ptr<TConsole> mConsole;
|
||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
|
static inline Version mVersion { 2, 4, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ThreadName();
|
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 1024
|
#define KB 1024
|
||||||
@@ -100,7 +134,11 @@ void RegisterThread(const std::string str);
|
|||||||
|
|
||||||
#define warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
#define warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
|
||||||
#define info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
#define info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
|
||||||
#define error(x) Application::Console().Write(_this_location + std::string("[ERROR] ") + (x))
|
#define error(x) \
|
||||||
|
do { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \
|
||||||
|
Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \
|
||||||
|
} while (false)
|
||||||
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
|
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
|
||||||
#define debug(x) \
|
#define debug(x) \
|
||||||
do { \
|
do { \
|
||||||
@@ -108,8 +146,22 @@ void RegisterThread(const std::string str);
|
|||||||
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
|
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
// trace() is a debug-build debug()
|
||||||
|
#if defined(DEBUG)
|
||||||
|
#define trace(x) \
|
||||||
|
do { \
|
||||||
|
if (Application::Settings.DebugModeEnabled) { \
|
||||||
|
Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
#else
|
||||||
|
#define trace(x)
|
||||||
|
#endif // defined(DEBUG)
|
||||||
|
|
||||||
|
void LogChatMessage(const std::string& name, int id, const std::string& msg);
|
||||||
|
|
||||||
#define Biggest 30000
|
#define Biggest 30000
|
||||||
std::string Comp(std::string Data);
|
std::string Comp(std::string Data);
|
||||||
std::string DeComp(std::string Compressed);
|
std::string DeComp(std::string Compressed);
|
||||||
|
|
||||||
|
std::string GetPlatformAgnosticErrorString();
|
||||||
|
|||||||
+9
-7
@@ -7,14 +7,15 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
using SOCKET = int;
|
using SOCKET = int;
|
||||||
using DWORD = unsigned long;
|
using DWORD = unsigned long;
|
||||||
using PDWORD = unsigned long*;
|
using PDWORD = unsigned long*;
|
||||||
using LPDWORD = unsigned long*;
|
using LPDWORD = unsigned long*;
|
||||||
char _getch();
|
char _getch();
|
||||||
inline void CloseSocketProper(int socket) {
|
inline void CloseSocketProper(int TheSocket) {
|
||||||
shutdown(socket, SHUT_RDWR);
|
shutdown(TheSocket, SHUT_RDWR);
|
||||||
close(socket);
|
close(TheSocket);
|
||||||
}
|
}
|
||||||
#endif // unix
|
#endif // unix
|
||||||
|
|
||||||
@@ -23,9 +24,10 @@ inline void CloseSocketProper(int socket) {
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
inline void CloseSocketProper(SOCKET socket) {
|
inline void CloseSocketProper(SOCKET TheSocket) {
|
||||||
shutdown(socket, SD_BOTH);
|
shutdown(TheSocket, 2); // 2 == SD_BOTH
|
||||||
closesocket(socket);
|
closesocket(TheSocket);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
@@ -33,4 +35,4 @@ inline void CloseSocketProper(SOCKET socket) {
|
|||||||
|
|
||||||
#if !defined(WIN32) && !defined(__unix)
|
#if !defined(WIN32) && !defined(__unix)
|
||||||
#error "OS not supported"
|
#error "OS not supported"
|
||||||
#endif
|
#endif
|
||||||
+10
-5
@@ -40,7 +40,7 @@ static const char* const ANSI_WHITE_BOLD = "\u001b[37;1m";
|
|||||||
static const char* const ANSI_BOLD = "\u001b[1m";
|
static const char* const ANSI_BOLD = "\u001b[1m";
|
||||||
static const char* const ANSI_UNDERLINE = "\u001b[4m";
|
static const char* const ANSI_UNDERLINE = "\u001b[4m";
|
||||||
|
|
||||||
#if DEBUG
|
#ifdef DEBUG
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line,
|
inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line,
|
||||||
[[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) {
|
[[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) {
|
||||||
@@ -59,10 +59,15 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch
|
|||||||
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
|
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
|
||||||
#else
|
#else
|
||||||
// In release build, these macros turn into NOPs. The compiler will optimize these out.
|
// In release build, these macros turn into NOPs. The compiler will optimize these out.
|
||||||
#define Assert(x) \
|
#define Assert(cond) \
|
||||||
do { \
|
do { \
|
||||||
|
bool result = (cond); \
|
||||||
|
if (!result) { \
|
||||||
|
Sentry.LogAssert(#cond, _file_basename, _line, __func__); \
|
||||||
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
#define AssertNotReachable() \
|
#define AssertNotReachable() \
|
||||||
do { \
|
do { \
|
||||||
|
Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \
|
||||||
} while (false)
|
} while (false)
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
template <typename FnT>
|
||||||
|
class Defer final {
|
||||||
|
public:
|
||||||
|
Defer(FnT fn)
|
||||||
|
: mFunction([&fn] { (void)fn(); }) { }
|
||||||
|
~Defer() {
|
||||||
|
if (mFunction) {
|
||||||
|
mFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void()> mFunction;
|
||||||
|
};
|
||||||
+7
-3
@@ -4,6 +4,10 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace Http {
|
namespace Http {
|
||||||
std::string GET(const std::string& host, int port, const std::string& target);
|
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
||||||
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);
|
std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr);
|
||||||
}
|
namespace Status {
|
||||||
|
std::string ToString(int code);
|
||||||
|
}
|
||||||
|
const std::string ErrorString = "-1";
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
// Created by anon on 4/21/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
#include "rapidjson/prettywriter.h"
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
#include "rapidjson/writer.h"
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
// Use ReadLock(m) and WriteLock(m) to lock it.
|
// Use ReadLock(m) and WriteLock(m) to lock it.
|
||||||
using RWMutex = std::shared_mutex;
|
using RWMutex = std::shared_mutex;
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <deque>
|
|
||||||
#include <mutex>
|
|
||||||
#include <sio_client.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We send relevant server events over socket.io to the backend.
|
|
||||||
*
|
|
||||||
* We send all events to `backend.beammp.com`, to the room `/key`
|
|
||||||
* where `key` is the currently active auth-key.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum class SocketIOEvent {
|
|
||||||
ConsoleOut,
|
|
||||||
CPUUsage,
|
|
||||||
MemoryUsage,
|
|
||||||
NetworkUsage,
|
|
||||||
PlayerList,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketIORoom {
|
|
||||||
None,
|
|
||||||
Stats,
|
|
||||||
Player,
|
|
||||||
Info,
|
|
||||||
Console,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketIO final {
|
|
||||||
private:
|
|
||||||
struct Event;
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum class EventType {
|
|
||||||
};
|
|
||||||
|
|
||||||
// Singleton pattern
|
|
||||||
static SocketIO& Get();
|
|
||||||
|
|
||||||
void Emit(SocketIOEvent Event, const std::string& Data);
|
|
||||||
|
|
||||||
~SocketIO();
|
|
||||||
|
|
||||||
void SetAuthenticated(bool auth) { mAuthenticated = auth; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
SocketIO() noexcept;
|
|
||||||
|
|
||||||
void ThreadMain();
|
|
||||||
|
|
||||||
struct Event {
|
|
||||||
std::string Name;
|
|
||||||
std::string Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool mAuthenticated { false };
|
|
||||||
sio::client mClient;
|
|
||||||
std::thread mThread;
|
|
||||||
std::atomic_bool mCloseThread { false };
|
|
||||||
std::mutex mQueueMutex;
|
|
||||||
std::deque<Event> mQueue;
|
|
||||||
|
|
||||||
friend std::unique_ptr<SocketIO> std::make_unique<SocketIO>();
|
|
||||||
};
|
|
||||||
|
|
||||||
+14
-3
@@ -2,11 +2,22 @@
|
|||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class TConfig {
|
class TConfig {
|
||||||
public:
|
public:
|
||||||
explicit TConfig(const std::string& ConfigFile);
|
explicit TConfig();
|
||||||
|
|
||||||
|
[[nodiscard]] bool Failed() const { return mFailed; }
|
||||||
|
|
||||||
|
void FlushToFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string RemoveComments(const std::string& Line);
|
void CreateConfigFile(std::string_view name);
|
||||||
static void SetValues(const std::string& Line, int Index);
|
void ParseFromFile(std::string_view name);
|
||||||
|
void PrintDebug();
|
||||||
|
|
||||||
|
void ParseOldFormat();
|
||||||
|
|
||||||
|
bool mFailed { false };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ private:
|
|||||||
bool mShutdown = false;
|
bool mShutdown = false;
|
||||||
TResourceManager& mResourceManager;
|
TResourceManager& mResourceManager;
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
#include "IThreaded.h"
|
#include "IThreaded.h"
|
||||||
#include "TLuaFile.h"
|
#include "TLuaFile.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
#include <optional>
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
class TLuaEngine : public IThreaded {
|
class TLuaEngine : public IThreaded {
|
||||||
public:
|
public:
|
||||||
@@ -25,14 +26,16 @@ public:
|
|||||||
|
|
||||||
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
|
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, lua_State*> mGlobals;
|
||||||
private:
|
private:
|
||||||
void FolderList(const std::string& Path, bool HotSwap);
|
void FolderList(const std::string& Path, bool HotSwap);
|
||||||
void RegisterFiles(const std::string& Path, bool HotSwap);
|
void RegisterFiles(const fs::path& Path, bool HotSwap);
|
||||||
bool NewFile(const std::string& Path);
|
bool IsNewFile(const std::string& Path);
|
||||||
|
|
||||||
TNetwork& mNetwork;
|
TNetwork& mNetwork;
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
bool mShutdown { false };
|
bool mShutdown { false };
|
||||||
TSetOfLuaFile mLuaFiles;
|
TSetOfLuaFile mLuaFiles;
|
||||||
|
std::mutex mListMutex;
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-6
@@ -37,12 +37,13 @@ public:
|
|||||||
~TLuaFile();
|
~TLuaFile();
|
||||||
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
|
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
|
||||||
TLuaEngine& Engine() { return mEngine; }
|
TLuaEngine& Engine() { return mEngine; }
|
||||||
[[nodiscard]] std::string GetPluginName() const;
|
std::string GetPluginPath() const;
|
||||||
[[nodiscard]] std::string GetFileName() const;
|
std::string GetPluginName() const;
|
||||||
[[nodiscard]] const lua_State* GetState() const;
|
std::string GetFileName() const;
|
||||||
[[nodiscard]] bool GetStopThread() const { return mStopThread; }
|
const lua_State* GetState() const;
|
||||||
[[nodiscard]] const TLuaEngine& Engine() const { return mEngine; }
|
bool GetStopThread() const { return mStopThread; }
|
||||||
[[nodiscard]] std::string GetRegistered(const std::string& Event) const;
|
const TLuaEngine& Engine() const { return mEngine; }
|
||||||
|
std::string GetRegistered(const std::string& Event) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TLuaEngine& mEngine;
|
TLuaEngine& mEngine;
|
||||||
@@ -54,6 +55,7 @@ private:
|
|||||||
bool mStopThread = false;
|
bool mStopThread = false;
|
||||||
bool mConsole = false;
|
bool mConsole = false;
|
||||||
void Load();
|
void Load();
|
||||||
|
std::mutex mInitMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
||||||
|
|||||||
+5
-3
@@ -4,6 +4,8 @@
|
|||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
|
||||||
|
struct TConnection;
|
||||||
|
|
||||||
class TNetwork {
|
class TNetwork {
|
||||||
public:
|
public:
|
||||||
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
||||||
@@ -15,8 +17,8 @@ public:
|
|||||||
std::string TCPRcv(TClient& c);
|
std::string TCPRcv(TClient& c);
|
||||||
void ClientKick(TClient& c, const std::string& R);
|
void ClientKick(TClient& c, const std::string& R);
|
||||||
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
||||||
void Identify(SOCKET TCPSock);
|
void Identify(const TConnection& client);
|
||||||
void Authentication(SOCKET TCPSock);
|
void Authentication(const TConnection& ClientConnection);
|
||||||
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
|
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
|
||||||
void SyncResources(TClient& c);
|
void SyncResources(TClient& c);
|
||||||
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const;
|
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const;
|
||||||
@@ -44,6 +46,6 @@ private:
|
|||||||
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked);
|
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked);
|
||||||
void Parse(TClient& c, const std::string& Packet);
|
void Parse(TClient& c, const std::string& Packet);
|
||||||
void SendFile(TClient& c, const std::string& Name);
|
void SendFile(TClient& c, const std::string& Name);
|
||||||
static bool TCPSendRaw(SOCKET C, 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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef SENTRY_H
|
||||||
|
#define SENTRY_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
enum class SentryLevel {
|
||||||
|
Debug = -1,
|
||||||
|
Info = 0,
|
||||||
|
Warning = 1,
|
||||||
|
Error = 2,
|
||||||
|
Fatal = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// singleton, dont make this twice
|
||||||
|
class TSentry final {
|
||||||
|
public:
|
||||||
|
TSentry();
|
||||||
|
~TSentry();
|
||||||
|
|
||||||
|
void PrintWelcome();
|
||||||
|
void SetupUser();
|
||||||
|
void Log(SentryLevel level, const std::string& logger, const std::string& text);
|
||||||
|
void LogError(const std::string& text, const std::string& file, const std::string& line);
|
||||||
|
void SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map);
|
||||||
|
void LogException(const std::exception& e, const std::string& file, const std::string& line);
|
||||||
|
void LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function);
|
||||||
|
void AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line);
|
||||||
|
// cleared when Logged
|
||||||
|
void SetTransaction(const std::string& id);
|
||||||
|
[[nodiscard]] std::unique_lock<std::mutex> CreateExclusiveContext();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mValid { true };
|
||||||
|
std::mutex mMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SENTRY_H
|
||||||
@@ -31,5 +31,7 @@ private:
|
|||||||
TClientSet mClients;
|
TClientSet mClients;
|
||||||
mutable RWMutex mClientsMutex;
|
mutable RWMutex mClientsMutex;
|
||||||
static void ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network);
|
static void ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network);
|
||||||
|
static bool ShouldSpawn(TClient& c, const std::string& CarJson, int ID);
|
||||||
|
static bool IsUnicycle(TClient& c, const std::string& CarJson);
|
||||||
static void Apply(TClient& c, int VID, const std::string& pckt);
|
static void Apply(TClient& c, int VID, const std::string& pckt);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,20 +6,25 @@ class TVehicleData final {
|
|||||||
public:
|
public:
|
||||||
TVehicleData(int ID, std::string Data);
|
TVehicleData(int ID, std::string Data);
|
||||||
~TVehicleData();
|
~TVehicleData();
|
||||||
|
// 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,
|
||||||
|
// and end up making no sense, so let's just leave the copy ctor.
|
||||||
|
// TVehicleData(const TVehicleData&) = delete;
|
||||||
|
|
||||||
[[nodiscard]] bool IsInvalid() const { return mID == -1; }
|
[[nodiscard]] bool IsInvalid() const { return mID == -1; }
|
||||||
[[nodiscard]] int ID() const { return mID; }
|
[[nodiscard]] int ID() const { return mID; }
|
||||||
|
|
||||||
[[nodiscard]] std::string Data() const { return mData; }
|
[[nodiscard]] std::string Data() const { return mData; }
|
||||||
void SetData(const std::string& Data) const { mData = Data; }
|
void SetData(const std::string& Data) { mData = Data; }
|
||||||
|
|
||||||
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mID { -1 };
|
int mID { -1 };
|
||||||
mutable std::string mData;
|
std::string mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: unused now, remove?
|
||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<TVehicleData> {
|
struct hash<TVehicleData> {
|
||||||
|
|||||||
+1
-1
Submodule include/commandline updated: 412ece748d...c34703df11
Submodule
+1
Submodule include/sentry-native added at 521860373d
Submodule
+1
Submodule include/toml11 added at 647381020e
Submodule socket.io-client-cpp deleted from b196fa7537
+28
-18
@@ -6,15 +6,19 @@
|
|||||||
// FIXME: add debug prints
|
// FIXME: add debug prints
|
||||||
|
|
||||||
void TClient::DeleteCar(int Ident) {
|
void TClient::DeleteCar(int Ident) {
|
||||||
for (auto& v : mVehicleData) {
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
if (v.ID() == Ident) {
|
auto iter = std::find_if(mVehicleData.begin(), mVehicleData.end(), [&](auto& elem) {
|
||||||
mVehicleData.erase(v);
|
return Ident == elem.ID();
|
||||||
break;
|
});
|
||||||
}
|
if (iter != mVehicleData.end()) {
|
||||||
|
mVehicleData.erase(iter);
|
||||||
|
} else {
|
||||||
|
debug("tried to erase a vehicle that doesn't exist (not an error)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TClient::ClearCars() {
|
void TClient::ClearCars() {
|
||||||
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
mVehicleData.clear();
|
mVehicleData.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,30 +38,37 @@ int TClient::GetOpenCarID() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TClient::AddNewCar(int Ident, const std::string& Data) {
|
void TClient::AddNewCar(int Ident, const std::string& Data) {
|
||||||
mVehicleData.emplace(Ident, Data);
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
|
mVehicleData.emplace_back(Ident, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
TClient::TVehicleDataLockPair TClient::GetAllCars() {
|
TClient::TVehicleDataLockPair TClient::GetAllCars() {
|
||||||
return { mVehicleData, std::unique_lock(mVehicleDataMutex) };
|
return { &mVehicleData, std::unique_lock(mVehicleDataMutex) };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TClient::GetCarData(int Ident) {
|
std::string TClient::GetCarData(int Ident) {
|
||||||
for (auto& v : mVehicleData) {
|
{ // lock
|
||||||
if (v.ID() == Ident) {
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
return v.Data();
|
for (auto& v : mVehicleData) {
|
||||||
|
if (v.ID() == Ident) {
|
||||||
|
return v.Data();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} // unlock
|
||||||
DeleteCar(Ident);
|
DeleteCar(Ident);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TClient::SetCarData(int Ident, const std::string& Data) {
|
void TClient::SetCarData(int Ident, const std::string& Data) {
|
||||||
for (auto& v : mVehicleData) {
|
{ // lock
|
||||||
if (v.ID() == Ident) {
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
v.SetData(Data);
|
for (auto& v : mVehicleData) {
|
||||||
return;
|
if (v.ID() == Ident) {
|
||||||
|
v.SetData(Data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} // unlock
|
||||||
DeleteCar(Ident);
|
DeleteCar(Ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +82,7 @@ TServer& TClient::Server() const {
|
|||||||
|
|
||||||
void TClient::EnqueuePacket(const std::string& Packet) {
|
void TClient::EnqueuePacket(const std::string& Packet) {
|
||||||
std::unique_lock Lock(mMissedPacketsMutex);
|
std::unique_lock Lock(mMissedPacketsMutex);
|
||||||
mMissedPacketsDuringSyncing.push(Packet);
|
mPacketsSync.push(Packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
TClient::TClient(TServer& Server)
|
TClient::TClient(TServer& Server)
|
||||||
@@ -81,7 +92,6 @@ TClient::TClient(TServer& Server)
|
|||||||
|
|
||||||
void TClient::UpdatePingTime() {
|
void TClient::UpdatePingTime() {
|
||||||
mLastPingTime = std::chrono::high_resolution_clock::now();
|
mLastPingTime = std::chrono::high_resolution_clock::now();
|
||||||
//debug(GetName() + ": " + std::string("ping time updated!: ") + ((SecondsSinceLastPing() == 0) ? "OK" : "ERR"));
|
|
||||||
}
|
}
|
||||||
int TClient::SecondsSinceLastPing() {
|
int TClient::SecondsSinceLastPing() {
|
||||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
|||||||
+117
-4
@@ -2,11 +2,17 @@
|
|||||||
|
|
||||||
#include "TConsole.h"
|
#include "TConsole.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <charconv>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <regex>
|
||||||
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include "CustomAssert.h"
|
||||||
|
#include "Http.h"
|
||||||
|
|
||||||
std::unique_ptr<TConsole> Application::mConsole = std::make_unique<TConsole>();
|
std::unique_ptr<TConsole> Application::mConsole = std::make_unique<TConsole>();
|
||||||
|
|
||||||
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||||
@@ -24,6 +30,57 @@ void Application::GracefullyShutdown() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Application::ServerVersionString() {
|
||||||
|
return mVersion.AsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
|
||||||
|
std::array<uint8_t, 3> Version;
|
||||||
|
std::stringstream ss(str);
|
||||||
|
for (uint8_t& i : Version) {
|
||||||
|
std::string Part;
|
||||||
|
std::getline(ss, Part, '.');
|
||||||
|
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
|
||||||
|
}
|
||||||
|
return Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This should be used by operator< on Version
|
||||||
|
bool Application::IsOutdated(const Version& Current, const Version& Newest) {
|
||||||
|
if (Newest.major > Current.major) {
|
||||||
|
return true;
|
||||||
|
} else if (Newest.major == Current.major && Newest.minor > Current.minor) {
|
||||||
|
return true;
|
||||||
|
} else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::CheckForUpdates() {
|
||||||
|
// checks current version against latest version
|
||||||
|
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
||||||
|
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
|
||||||
|
bool Matches = std::regex_match(Response, VersionRegex);
|
||||||
|
if (Matches) {
|
||||||
|
auto MyVersion = ServerVersion();
|
||||||
|
auto RemoteVersion = Version(VersionStrToInts(Response));
|
||||||
|
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||||
|
std::string RealVersionString = RemoteVersion.AsString();
|
||||||
|
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
|
||||||
|
} else {
|
||||||
|
info("Server up-to-date!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn("Unable to fetch version from backend.");
|
||||||
|
trace("got " + Response);
|
||||||
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
|
Sentry.SetContext("get-response", { { "response", Response } });
|
||||||
|
Sentry.LogError("failed to get server version", _file_basename, _line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string Comp(std::string Data) {
|
std::string Comp(std::string Data) {
|
||||||
std::array<char, Biggest> C {};
|
std::array<char, Biggest> C {};
|
||||||
// obsolete
|
// obsolete
|
||||||
@@ -70,10 +127,12 @@ std::string DeComp(std::string Compressed) {
|
|||||||
|
|
||||||
// thread name stuff
|
// thread name stuff
|
||||||
|
|
||||||
std::map<std::thread::id, std::string> threadNameMap;
|
static std::map<std::thread::id, std::string> threadNameMap {};
|
||||||
|
static std::mutex ThreadNameMapMutex {};
|
||||||
|
|
||||||
std::string ThreadName() {
|
std::string ThreadName(bool DebugModeOverride) {
|
||||||
if (Application::Settings.DebugModeEnabled) {
|
auto Lock = std::unique_lock(ThreadNameMapMutex);
|
||||||
|
if (DebugModeOverride || Application::Settings.DebugModeEnabled) {
|
||||||
auto id = std::this_thread::get_id();
|
auto id = std::this_thread::get_id();
|
||||||
if (threadNameMap.find(id) != threadNameMap.end()) {
|
if (threadNameMap.find(id) != threadNameMap.end()) {
|
||||||
// found
|
// found
|
||||||
@@ -83,6 +142,60 @@ std::string ThreadName() {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterThread(const std::string str) {
|
void RegisterThread(const std::string& str) {
|
||||||
|
auto Lock = std::unique_lock(ThreadNameMapMutex);
|
||||||
threadNameMap[std::this_thread::get_id()] = str;
|
threadNameMap[std::this_thread::get_id()] = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
|
||||||
|
: major(major)
|
||||||
|
, minor(minor)
|
||||||
|
, patch(patch) { }
|
||||||
|
|
||||||
|
Version::Version(const std::array<uint8_t, 3>& v)
|
||||||
|
: Version(v[0], v[1], v[2]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Version::AsString() {
|
||||||
|
std::stringstream ss {};
|
||||||
|
ss << int(major) << "." << int(minor) << "." << int(patch);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "[CHAT] ";
|
||||||
|
if (id != -1) {
|
||||||
|
ss << "(" << id << ") <" << name << ">";
|
||||||
|
} else {
|
||||||
|
ss << name << "";
|
||||||
|
}
|
||||||
|
ss << msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetPlatformAgnosticErrorString() {
|
||||||
|
#ifdef WIN32
|
||||||
|
// This will provide us with the error code and an error message, all in one.
|
||||||
|
int err;
|
||||||
|
char msgbuf[256];
|
||||||
|
msgbuf[0] = '\0';
|
||||||
|
|
||||||
|
err = GetLastError();
|
||||||
|
|
||||||
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr,
|
||||||
|
err,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
msgbuf,
|
||||||
|
sizeof(msgbuf),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (*msgbuf) {
|
||||||
|
return std::to_string(GetLastError()) + " - " + std::string(msgbuf);
|
||||||
|
} else {
|
||||||
|
return std::to_string(GetLastError());
|
||||||
|
}
|
||||||
|
#else // posix
|
||||||
|
return std::strerror(errno);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
+121
-52
@@ -7,6 +7,7 @@
|
|||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <boost/beast.hpp>
|
#include <boost/beast.hpp>
|
||||||
#include <boost/beast/ssl.hpp>
|
#include <boost/beast/ssl.hpp>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||||
@@ -14,47 +15,7 @@ namespace net = boost::asio; // from <boost/asio.hpp>
|
|||||||
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
std::string Http::GET(const std::string& host, int port, const std::string& target) {
|
std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
|
||||||
// FIXME: doesn't support https
|
|
||||||
// if it causes issues, yell at me and I'll fix it asap. - Lion
|
|
||||||
try {
|
|
||||||
net::io_context io;
|
|
||||||
tcp::resolver resolver(io);
|
|
||||||
beast::tcp_stream stream(io);
|
|
||||||
auto const results = resolver.resolve(host, std::to_string(port));
|
|
||||||
stream.connect(results);
|
|
||||||
|
|
||||||
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
|
|
||||||
|
|
||||||
req.set(http::field::host, host);
|
|
||||||
// tell the server what we are (boost beast)
|
|
||||||
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
|
||||||
|
|
||||||
http::write(stream, req);
|
|
||||||
|
|
||||||
// used for reading
|
|
||||||
beast::flat_buffer buffer;
|
|
||||||
http::response<http::string_body> response;
|
|
||||||
|
|
||||||
http::read(stream, buffer, response);
|
|
||||||
|
|
||||||
std::string result(response.body());
|
|
||||||
|
|
||||||
beast::error_code ec;
|
|
||||||
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
|
||||||
if (ec && ec != beast::errc::not_connected) {
|
|
||||||
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
Application::Console().Write(e.what());
|
|
||||||
return "-1";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
|
|
||||||
try {
|
try {
|
||||||
net::io_context io;
|
net::io_context io;
|
||||||
|
|
||||||
@@ -68,7 +29,7 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
decltype(resolver)::results_type results;
|
decltype(resolver)::results_type results;
|
||||||
auto try_connect_with_protocol = [&](tcp protocol) {
|
auto try_connect_with_protocol = [&](tcp protocol) {
|
||||||
try {
|
try {
|
||||||
results = resolver.resolve(protocol, host, std::to_string(443));
|
results = resolver.resolve(protocol, host, std::to_string(port));
|
||||||
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||||
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
||||||
// FIXME: we could throw and crash, if we like
|
// FIXME: we could throw and crash, if we like
|
||||||
@@ -87,21 +48,18 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
//debug("IPv6 connect failed, trying IPv4");
|
//debug("IPv6 connect failed, trying IPv4");
|
||||||
bool ok = try_connect_with_protocol(tcp::v4());
|
bool ok = try_connect_with_protocol(tcp::v4());
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
//error("failed to resolve or connect in POST " + host + target);
|
Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target);
|
||||||
return "-1";
|
return "-1";
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
stream.handshake(ssl::stream_base::client);
|
stream.handshake(ssl::stream_base::client);
|
||||||
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
|
http::request<http::string_body> req { verb, target, 11 /* http 1.1 */ };
|
||||||
|
|
||||||
req.set(http::field::host, host);
|
req.set(http::field::host, host);
|
||||||
if (!body.empty()) {
|
if (!body.empty()) {
|
||||||
if (json) {
|
req.set(http::field::content_type, ContentType); // "application/json"
|
||||||
// FIXME: json is untested.
|
// "application/x-www-form-urlencoded"
|
||||||
req.set(http::field::content_type, "application/json");
|
|
||||||
} else {
|
|
||||||
req.set(http::field::content_type, "application/x-www-form-urlencoded");
|
|
||||||
}
|
|
||||||
req.set(http::field::content_length, std::to_string(body.size()));
|
req.set(http::field::content_length, std::to_string(body.size()));
|
||||||
req.body() = body;
|
req.body() = body;
|
||||||
// info("body is " + body + " (" + req.body() + ")");
|
// info("body is " + body + " (" + req.body() + ")");
|
||||||
@@ -112,6 +70,16 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
req.set(pair.first, pair.second);
|
req.set(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> request_data;
|
||||||
|
for (const auto& header : req.base()) {
|
||||||
|
// need to do explicit casts to convert string_view to string
|
||||||
|
// since string_view may not be null-terminated (and in fact isn't, here)
|
||||||
|
std::string KeyString(header.name_string());
|
||||||
|
std::string ValueString(header.value());
|
||||||
|
request_data[KeyString] = ValueString;
|
||||||
|
}
|
||||||
|
Sentry.SetContext("https-post-request-data", request_data);
|
||||||
|
|
||||||
std::stringstream oss;
|
std::stringstream oss;
|
||||||
oss << req;
|
oss << req;
|
||||||
|
|
||||||
@@ -125,6 +93,24 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
|
|
||||||
http::read(stream, buffer, response);
|
http::read(stream, buffer, response);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> response_data;
|
||||||
|
response_data["reponse-code"] = std::to_string(response.result_int());
|
||||||
|
if (status) {
|
||||||
|
*status = response.result_int();
|
||||||
|
}
|
||||||
|
for (const auto& header : response.base()) {
|
||||||
|
// need to do explicit casts to convert string_view to string
|
||||||
|
// since string_view may not be null-terminated (and in fact isn't, here)
|
||||||
|
std::string KeyString(header.name_string());
|
||||||
|
std::string ValueString(header.value());
|
||||||
|
response_data[KeyString] = ValueString;
|
||||||
|
}
|
||||||
|
Sentry.SetContext("https-post-response-data", response_data);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
*status = response.base().result_int();
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
result << response;
|
result << response;
|
||||||
|
|
||||||
@@ -135,11 +121,94 @@ std::string Http::POST(const std::string& host, const std::string& target, const
|
|||||||
// info(result.str());
|
// info(result.str());
|
||||||
std::string debug_response_str;
|
std::string debug_response_str;
|
||||||
std::getline(result, debug_response_str);
|
std::getline(result, debug_response_str);
|
||||||
|
|
||||||
//debug("POST " + host + target + ": " + debug_response_str);
|
//debug("POST " + host + target + ": " + debug_response_str);
|
||||||
return std::string(response.body());
|
return std::string(response.body());
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
Application::Console().Write(e.what());
|
Application::Console().Write(__func__ + std::string(": ") + e.what());
|
||||||
return "-1";
|
return Http::ErrorString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
|
||||||
|
return GenericRequest(http::verb::get, host, port, target, {}, {}, {}, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
|
||||||
|
return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 2616, RFC 7231
|
||||||
|
static std::map<size_t, const char*> Map = {
|
||||||
|
{ 100, "Continue" },
|
||||||
|
{ 101, "Switching Protocols" },
|
||||||
|
{ 102, "Processing" },
|
||||||
|
{ 103, "Early Hints" },
|
||||||
|
{ 200, "OK" },
|
||||||
|
{ 201, "Created" },
|
||||||
|
{ 202, "Accepted" },
|
||||||
|
{ 203, "Non-Authoritative Information" },
|
||||||
|
{ 204, "No Content" },
|
||||||
|
{ 205, "Reset Content" },
|
||||||
|
{ 206, "Partial Content" },
|
||||||
|
{ 207, "Multi-Status" },
|
||||||
|
{ 208, "Already Reported" },
|
||||||
|
{ 226, "IM Used" },
|
||||||
|
{ 300, "Multiple Choices" },
|
||||||
|
{ 301, "Moved Permanently" },
|
||||||
|
{ 302, "Found" },
|
||||||
|
{ 303, "See Other" },
|
||||||
|
{ 304, "Not Modified" },
|
||||||
|
{ 305, "Use Proxy" },
|
||||||
|
{ 306, "(Unused)" },
|
||||||
|
{ 307, "Temporary Redirect" },
|
||||||
|
{ 308, "Permanent Redirect" },
|
||||||
|
{ 400, "Bad Request" },
|
||||||
|
{ 401, "Unauthorized" },
|
||||||
|
{ 402, "Payment Required" },
|
||||||
|
{ 403, "Forbidden" },
|
||||||
|
{ 404, "Not Found" },
|
||||||
|
{ 405, "Method Not Allowed" },
|
||||||
|
{ 406, "Not Acceptable" },
|
||||||
|
{ 407, "Proxy Authentication Required" },
|
||||||
|
{ 408, "Request Timeout" },
|
||||||
|
{ 409, "Conflict" },
|
||||||
|
{ 410, "Gone" },
|
||||||
|
{ 411, "Length Required" },
|
||||||
|
{ 412, "Precondition Failed" },
|
||||||
|
{ 413, "Payload Too Large" },
|
||||||
|
{ 414, "URI Too Long" },
|
||||||
|
{ 415, "Unsupported Media Type" },
|
||||||
|
{ 416, "Range Not Satisfiable" },
|
||||||
|
{ 417, "Expectation Failed" },
|
||||||
|
{ 421, "Misdirected Request" },
|
||||||
|
{ 422, "Unprocessable Entity" },
|
||||||
|
{ 423, "Locked" },
|
||||||
|
{ 424, "Failed Dependency" },
|
||||||
|
{ 425, "Too Early" },
|
||||||
|
{ 426, "Upgrade Required" },
|
||||||
|
{ 428, "Precondition Required" },
|
||||||
|
{ 429, "Too Many Requests" },
|
||||||
|
{ 431, "Request Header Fields Too Large" },
|
||||||
|
{ 451, "Unavailable For Legal Reasons" },
|
||||||
|
{ 500, "Internal Server Error" },
|
||||||
|
{ 501, "Not Implemented" },
|
||||||
|
{ 502, "Bad Gateway" },
|
||||||
|
{ 503, "Service Unavailable" },
|
||||||
|
{ 504, "Gateway Timeout" },
|
||||||
|
{ 505, "HTTP Version Not Supported" },
|
||||||
|
{ 506, "Variant Also Negotiates" },
|
||||||
|
{ 507, "Insufficient Storage" },
|
||||||
|
{ 508, "Loop Detected" },
|
||||||
|
{ 510, "Not Extended" },
|
||||||
|
{ 511, "Network Authentication Required" },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string Http::Status::ToString(int code) {
|
||||||
|
if (Map.find(code) != Map.end()) {
|
||||||
|
return Map.at(code);
|
||||||
|
} else {
|
||||||
|
return "Unassigned";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
#include "SocketIO.h"
|
|
||||||
#include "Common.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
//TODO Default disabled with config option
|
|
||||||
static std::unique_ptr<SocketIO> SocketIOInstance = std::make_unique<SocketIO>();
|
|
||||||
|
|
||||||
SocketIO& SocketIO::Get() {
|
|
||||||
return *SocketIOInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIO::SocketIO() noexcept
|
|
||||||
: mThread([this] { ThreadMain(); }) {
|
|
||||||
|
|
||||||
mClient.socket()->on("network", [&](sio::event&e) {
|
|
||||||
if(e.get_message()->get_string() == "Welcome"){
|
|
||||||
info("SocketIO Authenticated!");
|
|
||||||
mAuthenticated = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mClient.socket()->on("welcome", [&](sio::event&) {
|
|
||||||
info("Got welcome from backend! Authenticating SocketIO...");
|
|
||||||
mClient.socket()->emit("onInitConnection", Application::Settings.Key);
|
|
||||||
});
|
|
||||||
|
|
||||||
mClient.set_logs_quiet();
|
|
||||||
mClient.set_reconnect_delay(10000);
|
|
||||||
mClient.connect(Application::GetBackendUrlForSocketIO());
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIO::~SocketIO() {
|
|
||||||
mCloseThread.store(true);
|
|
||||||
mThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static constexpr auto EventNameFromEnum(SocketIOEvent Event) {
|
|
||||||
switch (Event) {
|
|
||||||
case SocketIOEvent::CPUUsage:
|
|
||||||
return "cpu usage";
|
|
||||||
case SocketIOEvent::MemoryUsage:
|
|
||||||
return "memory usage";
|
|
||||||
case SocketIOEvent::ConsoleOut:
|
|
||||||
return "console out";
|
|
||||||
case SocketIOEvent::NetworkUsage:
|
|
||||||
return "network usage";
|
|
||||||
case SocketIOEvent::PlayerList:
|
|
||||||
return "player list";
|
|
||||||
default:
|
|
||||||
error("unreachable code reached (developer error)");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SocketIO::Emit(SocketIOEvent Event, const std::string& Data) {
|
|
||||||
if (!mAuthenticated) {
|
|
||||||
debug("trying to emit a socket.io event when not yet authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string EventName = EventNameFromEnum(Event);
|
|
||||||
debug("emitting event \"" + EventName + "\" with data: \"" + Data);
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
mQueue.push_back({EventName, Data });
|
|
||||||
debug("queue now has " + std::to_string(mQueue.size()) + " events");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SocketIO::ThreadMain() {
|
|
||||||
while (!mCloseThread.load()) {
|
|
||||||
bool empty;
|
|
||||||
{ // queue lock scope
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
empty = mQueue.empty();
|
|
||||||
} // end queue lock scope
|
|
||||||
if (empty || !mClient.opened()) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
Event TheEvent;
|
|
||||||
{ // queue lock scope
|
|
||||||
std::unique_lock Lock(mQueueMutex);
|
|
||||||
TheEvent = mQueue.front();
|
|
||||||
mQueue.pop_front();
|
|
||||||
} // end queue lock scope
|
|
||||||
debug("sending \"" + TheEvent.Name + "\" event");
|
|
||||||
mClient.socket()->emit(TheEvent.Name, TheEvent.Data);
|
|
||||||
debug("sent \"" + TheEvent.Name + "\" event");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// using std::cout as this happens during static destruction and the logger might be dead already
|
|
||||||
std::cout << "closing " + std::string(__func__) << std::endl;
|
|
||||||
|
|
||||||
mClient.sync_close();
|
|
||||||
mClient.clear_con_listeners();
|
|
||||||
|
|
||||||
std::cout << "closed" << std::endl;
|
|
||||||
}
|
|
||||||
+177
-113
@@ -1,126 +1,190 @@
|
|||||||
#include "../include/TConfig.h"
|
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
|
||||||
|
|
||||||
|
#include <toml11/toml.hpp> // header-only version of TOML++
|
||||||
|
|
||||||
|
#include "TConfig.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <istream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
TConfig::TConfig(const std::string& ConfigFile) {
|
static const char* ConfigFileName = static_cast<const char*>("ServerConfig.toml");
|
||||||
std::ifstream File(ConfigFile);
|
|
||||||
if (File.good()) {
|
static constexpr std::string_view StrDebug = "Debug";
|
||||||
std::string line;
|
static constexpr std::string_view StrPrivate = "Private";
|
||||||
int index = 1;
|
static constexpr std::string_view StrPort = "Port";
|
||||||
while (getline(File, line)) {
|
static constexpr std::string_view StrMaxCars = "MaxCars";
|
||||||
index++;
|
static constexpr std::string_view StrMaxPlayers = "MaxPlayers";
|
||||||
|
static constexpr std::string_view StrMap = "Map";
|
||||||
|
static constexpr std::string_view StrName = "Name";
|
||||||
|
static constexpr std::string_view StrDescription = "Description";
|
||||||
|
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
||||||
|
static constexpr std::string_view StrAuthKey = "AuthKey";
|
||||||
|
|
||||||
|
TConfig::TConfig() {
|
||||||
|
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
|
||||||
|
info("No config file found! Generating one...");
|
||||||
|
CreateConfigFile(ConfigFileName);
|
||||||
|
}
|
||||||
|
if (!mFailed) {
|
||||||
|
if (fs::exists("Server.cfg")) {
|
||||||
|
warn("An old \"Server.cfg\" file still exists. Please note that this is no longer used. Instead, \"" + std::string(ConfigFileName) + "\" is used. You can safely delete the \"Server.cfg\".");
|
||||||
}
|
}
|
||||||
if (index - 1 < 11) {
|
ParseFromFile(ConfigFileName);
|
||||||
error(("Outdated/Incorrect config please remove it server will close in 5 secs"));
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
}
|
||||||
_Exit(0);
|
|
||||||
}
|
void TConfig::FlushToFile() {
|
||||||
File.close();
|
auto data = toml::parse(ConfigFileName);
|
||||||
File.open(("Server.cfg"));
|
data["General"] = toml::table();
|
||||||
info(("Config found updating values"));
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
index = 1;
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
while (std::getline(File, line)) {
|
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||||
if (line.rfind('#', 0) != 0 && line.rfind(' ', 0) != 0) { //Checks if it starts as Comment
|
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||||
std::string CleanLine = RemoveComments(line); //Cleans it from the Comments
|
data["General"][StrName.data()] = Application::Settings.ServerName;
|
||||||
SetValues(CleanLine, index); //sets the values
|
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
|
||||||
index++;
|
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
|
||||||
}
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
|
std::ofstream Stream(ConfigFileName);
|
||||||
|
Stream << data << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TConfig::CreateConfigFile(std::string_view name) {
|
||||||
|
// build from old config Server.cfg
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs::exists("Server.cfg")) {
|
||||||
|
// parse it (this is weird and bad and should be removed in some future version)
|
||||||
|
ParseOldFormat();
|
||||||
}
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // create file context
|
||||||
|
std::ofstream ofs(name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = toml::parse<toml::preserve_comments>(name.data());
|
||||||
|
|
||||||
|
data["General"] = toml::table();
|
||||||
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
|
data["General"][StrPrivate.data()] = Application::Settings.Private;
|
||||||
|
data["General"][StrPort.data()] = Application::Settings.Port;
|
||||||
|
data["General"][StrName.data()] = Application::Settings.ServerName;
|
||||||
|
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
|
||||||
|
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
|
||||||
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
|
|
||||||
|
std::ofstream ofs { std::string(name) };
|
||||||
|
if (ofs.good()) {
|
||||||
|
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';
|
||||||
|
ofs << data << '\n';
|
||||||
|
error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
|
||||||
|
mFailed = true;
|
||||||
} else {
|
} else {
|
||||||
info(("Config not found generating default"));
|
error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
||||||
std::ofstream FileStream;
|
mFailed = true;
|
||||||
FileStream.open(ConfigFile);
|
|
||||||
// TODO REPLACE THIS SHIT OMG
|
|
||||||
FileStream << ("# This is the BeamMP Server Configuration File v0.60\n"
|
|
||||||
"Debug = false # true or false to enable debug console output\n"
|
|
||||||
"Private = true # Private?\n"
|
|
||||||
"Port = 30814 # Port to run the server on UDP and TCP\n"
|
|
||||||
"Cars = 1 # Max cars for every player\n"
|
|
||||||
"MaxPlayers = 10 # Maximum Amount of Clients\n"
|
|
||||||
"Map = \"/levels/gridmap/info.json\" # Default Map\n"
|
|
||||||
"Name = \"BeamMP New Server\" # Server Name\n"
|
|
||||||
"Desc = \"BeamMP Default Description\" # Server Description\n"
|
|
||||||
"use = \"Resources\" # Resource file name\n"
|
|
||||||
"AuthKey = \"\" # Auth Key");
|
|
||||||
FileStream.close();
|
|
||||||
error(("You are required to input the AuthKey"));
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
||||||
_Exit(0);
|
|
||||||
}
|
}
|
||||||
debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
|
|
||||||
debug("Private : " + std::string(Application::Settings.Private ? "true" : "false"));
|
|
||||||
debug("Port : " + std::to_string(Application::Settings.Port));
|
|
||||||
debug("Max Cars : " + std::to_string(Application::Settings.MaxCars));
|
|
||||||
debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers));
|
|
||||||
debug("MapName : \"" + Application::Settings.MapName + "\"");
|
|
||||||
debug("ServerName : \"" + Application::Settings.ServerName + "\"");
|
|
||||||
debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\"");
|
|
||||||
debug("File : \"" + Application::Settings.Resource + "\"");
|
|
||||||
debug("Key length : " + std::to_string(Application::Settings.Key.length()) + "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TConfig::RemoveComments(const std::string& Line) {
|
void TConfig::ParseFromFile(std::string_view name) {
|
||||||
std::string Return;
|
try {
|
||||||
for (char c : Line) {
|
toml::value data = toml::parse<toml::preserve_comments>(name.data());
|
||||||
if (c == '#')
|
Application::Settings.DebugModeEnabled = data["General"][StrDebug.data()].as_boolean();
|
||||||
|
Application::Settings.Private = data["General"][StrPrivate.data()].as_boolean();
|
||||||
|
Application::Settings.Port = data["General"][StrPort.data()].as_integer();
|
||||||
|
Application::Settings.MaxCars = data["General"][StrMaxCars.data()].as_integer();
|
||||||
|
Application::Settings.MaxPlayers = data["General"][StrMaxPlayers.data()].as_integer();
|
||||||
|
Application::Settings.MapName = data["General"][StrMap.data()].as_string();
|
||||||
|
Application::Settings.ServerName = data["General"][StrName.data()].as_string();
|
||||||
|
Application::Settings.ServerDesc = data["General"][StrDescription.data()].as_string();
|
||||||
|
Application::Settings.Resource = data["General"][StrResourceFolder.data()].as_string();
|
||||||
|
Application::Settings.Key = data["General"][StrAuthKey.data()].as_string();
|
||||||
|
} catch (const std::exception& err) {
|
||||||
|
error("Error parsing config file value: " + std::string(err.what()));
|
||||||
|
mFailed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PrintDebug();
|
||||||
|
// all good so far, let's check if there's a key
|
||||||
|
if (Application::Settings.Key.empty()) {
|
||||||
|
error("No AuthKey specified in the \"" + std::string(ConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server.");
|
||||||
|
mFailed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TConfig::PrintDebug() {
|
||||||
|
debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||||
|
debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false"));
|
||||||
|
debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port));
|
||||||
|
debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars));
|
||||||
|
debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers));
|
||||||
|
debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\"");
|
||||||
|
debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\"");
|
||||||
|
debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
|
||||||
|
debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
|
||||||
|
// special!
|
||||||
|
debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TConfig::ParseOldFormat() {
|
||||||
|
std::ifstream File("Server.cfg");
|
||||||
|
// read all, strip comments
|
||||||
|
std::string Content;
|
||||||
|
for (;;) {
|
||||||
|
std::string Line;
|
||||||
|
std::getline(File, Line);
|
||||||
|
if (!Line.empty() && Line.at(0) != '#') {
|
||||||
|
Line = Line.substr(0, Line.find_first_of('#'));
|
||||||
|
Content += Line + "\n";
|
||||||
|
}
|
||||||
|
if (!File.good()) {
|
||||||
break;
|
break;
|
||||||
Return += c;
|
|
||||||
}
|
|
||||||
return Return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TConfig::SetValues(const std::string& Line, int Index) {
|
|
||||||
int state = 0;
|
|
||||||
std::string Data;
|
|
||||||
bool Switch = false;
|
|
||||||
if (Index > 5)
|
|
||||||
Switch = true;
|
|
||||||
for (char c : Line) {
|
|
||||||
if (Switch) {
|
|
||||||
if (c == '\"')
|
|
||||||
state++;
|
|
||||||
if (state > 0 && state < 2)
|
|
||||||
Data += c;
|
|
||||||
} else {
|
|
||||||
if (c == ' ')
|
|
||||||
state++;
|
|
||||||
if (state > 1)
|
|
||||||
Data += c;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Data = Data.substr(1);
|
std::stringstream Str(Content);
|
||||||
std::string::size_type sz;
|
std::string Key, Ignore, Value;
|
||||||
bool FoundTrue = std::string(Data).find("true") != std::string::npos; //searches for "true"
|
for (;;) {
|
||||||
switch (Index) {
|
Str >> Key >> std::ws >> Ignore >> std::ws;
|
||||||
case 1:
|
std::getline(Str, Value);
|
||||||
Application::Settings.DebugModeEnabled = FoundTrue; //checks and sets the Debug Value
|
if (Str.eof()) {
|
||||||
break;
|
break;
|
||||||
case 2:
|
}
|
||||||
Application::Settings.Private = FoundTrue; //checks and sets the Private Value
|
std::stringstream ValueStream(Value);
|
||||||
break;
|
ValueStream >> std::ws; // strip leading whitespace if any
|
||||||
case 3:
|
Value = ValueStream.str();
|
||||||
Application::Settings.Port = std::stoi(Data, &sz); //sets the Port
|
if (Key == "Debug") {
|
||||||
break;
|
Application::Settings.DebugModeEnabled = Value.find("true") != std::string::npos;
|
||||||
case 4:
|
} else if (Key == "Private") {
|
||||||
Application::Settings.MaxCars = std::stoi(Data, &sz); //sets the Max Car amount
|
Application::Settings.Private = Value.find("true") != std::string::npos;
|
||||||
break;
|
} else if (Key == "Port") {
|
||||||
case 5:
|
ValueStream >> Application::Settings.Port;
|
||||||
Application::Settings.MaxPlayers = std::stoi(Data, &sz); //sets the Max Amount of player
|
} else if (Key == "Cars") {
|
||||||
break;
|
ValueStream >> Application::Settings.MaxCars;
|
||||||
case 6:
|
} else if (Key == "MaxPlayers") {
|
||||||
Application::Settings.MapName = Data; //Map
|
ValueStream >> Application::Settings.MaxPlayers;
|
||||||
break;
|
} else if (Key == "Map") {
|
||||||
case 7:
|
Application::Settings.MapName = Value.substr(1, Value.size() - 3);
|
||||||
Application::Settings.ServerName = Data; //Name
|
} else if (Key == "Name") {
|
||||||
break;
|
Application::Settings.ServerName = Value.substr(1, Value.size() - 3);
|
||||||
case 8:
|
} else if (Key == "Desc") {
|
||||||
Application::Settings.ServerDesc = Data; //desc
|
Application::Settings.ServerDesc = Value.substr(1, Value.size() - 3);
|
||||||
break;
|
} else if (Key == "use") {
|
||||||
case 9:
|
Application::Settings.Resource = Value.substr(1, Value.size() - 3);
|
||||||
Application::Settings.Resource = Data; //File name
|
} else if (Key == "AuthKey") {
|
||||||
break;
|
Application::Settings.Key = Value.substr(1, Value.size() - 3);
|
||||||
case 10:
|
} else {
|
||||||
Application::Settings.Key = Data; //File name
|
warn("unknown key in old auth file (ignored): " + Key);
|
||||||
default:
|
}
|
||||||
break;
|
Str >> std::ws;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-14
@@ -26,9 +26,7 @@ void THeartbeatThread::operator()() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
debug("heartbeat (after " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()) + "s)");
|
||||||
debug("heartbeat @ " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()));
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
Last = Body;
|
Last = Body;
|
||||||
LastNormalUpdateTime = Now;
|
LastNormalUpdateTime = Now;
|
||||||
@@ -37,17 +35,39 @@ void THeartbeatThread::operator()() {
|
|||||||
|
|
||||||
Body += "&pps=" + Application::PPS();
|
Body += "&pps=" + Application::PPS();
|
||||||
|
|
||||||
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
|
auto SentryReportError = [&](const std::string& transaction, int status) {
|
||||||
|
if (status < 0) {
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (T.substr(0, 2) != "20") {
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
//Backend system refused server startup!
|
Sentry.SetContext("heartbeat",
|
||||||
|
{ { "response-body", T },
|
||||||
|
{ "request-body", Body } });
|
||||||
|
Sentry.SetTransaction(transaction);
|
||||||
|
trace("sending log to sentry: " + std::to_string(status) + " for " + transaction);
|
||||||
|
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Target = "/heartbeat";
|
||||||
|
unsigned int ResponseCode = 0;
|
||||||
|
T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
|
|
||||||
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
|
trace("got " + T + " from backend");
|
||||||
|
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
|
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
// TODO backup2 + HTTP flag (no TSL)
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
if (T.substr(0, 2) != "20") {
|
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
|
||||||
warn("Backend system refused server! Server might not show in the public list");
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
debug("server returned \"" + T + "\"");
|
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
isAuth = false;
|
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
|
||||||
|
warn("Backend system refused server! Server will not show in the public server list.");
|
||||||
|
|
||||||
|
isAuth = false;
|
||||||
|
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +84,7 @@ void THeartbeatThread::operator()() {
|
|||||||
//SocketIO::Get().SetAuthenticated(isAuth);
|
//SocketIO::Get().SetAuthenticated(isAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string THeartbeatThread::GenerateCall() {
|
std::string THeartbeatThread::GenerateCall() {
|
||||||
std::stringstream Ret;
|
std::stringstream Ret;
|
||||||
|
|
||||||
@@ -73,8 +94,8 @@ std::string THeartbeatThread::GenerateCall() {
|
|||||||
<< "&port=" << Application::Settings.Port
|
<< "&port=" << Application::Settings.Port
|
||||||
<< "&map=" << Application::Settings.MapName
|
<< "&map=" << Application::Settings.MapName
|
||||||
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
||||||
<< "&version=" << Application::ServerVersion()
|
<< "&version=" << Application::ServerVersionString()
|
||||||
<< "&clientversion=" << Application::ClientVersion()
|
<< "&clientversion=" << Application::ClientVersionString()
|
||||||
<< "&name=" << Application::Settings.ServerName
|
<< "&name=" << Application::Settings.ServerName
|
||||||
<< "&modlist=" << mResourceManager.TrimmedList()
|
<< "&modlist=" << mResourceManager.TrimmedList()
|
||||||
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
||||||
|
|||||||
+30
-18
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, lua_State*> TLuaEngine::mGlobals;
|
||||||
|
|
||||||
// necessary as lua relies on global state
|
// necessary as lua relies on global state
|
||||||
TLuaEngine* TheEngine;
|
TLuaEngine* TheEngine;
|
||||||
|
|
||||||
@@ -66,37 +68,47 @@ std::optional<std::reference_wrapper<TLuaFile>> TLuaEngine::GetScript(lua_State*
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
|
void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
|
||||||
|
auto Lock = std::unique_lock(mListMutex);
|
||||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||||
auto pos = entry.path().filename().string().find('.');
|
if (fs::is_directory(entry)) {
|
||||||
if (pos == std::string::npos) {
|
RegisterFiles(entry.path(), HotSwap);
|
||||||
RegisterFiles(entry.path().string(), HotSwap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::RegisterFiles(const std::string& Path, bool HotSwap) {
|
void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
|
||||||
std::string Name = Path.substr(Path.find_last_of('\\') + 1);
|
std::string Name = Path.filename().string();
|
||||||
if (!HotSwap)
|
if (!HotSwap)
|
||||||
info(("Loading plugin : ") + Name);
|
info(("Loading plugin : ") + Name);
|
||||||
|
std::vector<fs::path> Entries;
|
||||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||||
auto pos = entry.path().string().find((".lua"));
|
if (entry.path().extension() == ".lua") {
|
||||||
if (pos != std::string::npos && entry.path().string().length() - pos == 4) {
|
Entries.push_back(entry);
|
||||||
if (!HotSwap || NewFile(entry.path().string())) {
|
}
|
||||||
auto FileName = entry.path().string();
|
}
|
||||||
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
|
std::sort(Entries.begin(), Entries.end(), [](const fs::path& first, const fs::path& second) {
|
||||||
auto& Script = *ScriptToInsert;
|
auto firstStr = first.string();
|
||||||
mLuaFiles.insert(std::move(ScriptToInsert));
|
auto secondStr = second.string();
|
||||||
Script.Init(Name, FileName, fs::last_write_time(FileName));
|
std::transform(firstStr.begin(), firstStr.end(), firstStr.begin(), ::tolower);
|
||||||
if (HotSwap)
|
std::transform(secondStr.begin(), secondStr.end(), secondStr.begin(), ::tolower);
|
||||||
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
|
return firstStr < secondStr;
|
||||||
}
|
});
|
||||||
|
for (const fs::path& Entry : Entries) {
|
||||||
|
if (!HotSwap || IsNewFile(Entry.string())) {
|
||||||
|
auto FileName = Entry.string();
|
||||||
|
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
|
||||||
|
auto& Script = *ScriptToInsert;
|
||||||
|
mLuaFiles.insert(std::move(ScriptToInsert));
|
||||||
|
Script.Init(Name, FileName, fs::last_write_time(FileName));
|
||||||
|
if (HotSwap)
|
||||||
|
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TLuaEngine::NewFile(const std::string& Path) {
|
bool TLuaEngine::IsNewFile(const std::string& Path) {
|
||||||
for (auto& Script : mLuaFiles) {
|
for (auto& Script : mLuaFiles) {
|
||||||
if (Path == Script->GetFileName())
|
if (fs::absolute(Path) == fs::absolute(Script->GetFileName()))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+412
-95
@@ -2,14 +2,45 @@
|
|||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
|
#include "Defer.h"
|
||||||
|
#include "Http.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
#include <functional>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// TODO: REWRITE
|
namespace LuaTable {
|
||||||
|
void Begin(lua_State* L) {
|
||||||
|
lua_newtable(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void End(lua_State* L, const std::string& name) {
|
||||||
|
lua_setglobal(L, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginEntry(lua_State* L, const std::string& name) {
|
||||||
|
lua_pushstring(L, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndEntry(lua_State* L) {
|
||||||
|
lua_settable(L, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertFunction(lua_State* L, const std::string& name, lua_CFunction func) {
|
||||||
|
BeginEntry(L, name);
|
||||||
|
lua_pushcfunction(L, func);
|
||||||
|
EndEntry(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertInteger(lua_State* L, const std::string& name, lua_Integer i) {
|
||||||
|
BeginEntry(L, name);
|
||||||
|
lua_pushinteger(L, i);
|
||||||
|
EndEntry(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
|
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
|
||||||
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
|
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
|
||||||
@@ -39,12 +70,13 @@ std::shared_ptr<TLuaArg> CreateArg(lua_State* L, int T, int S) {
|
|||||||
}
|
}
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearStack(lua_State* L) {
|
void ClearStack(lua_State* L) {
|
||||||
lua_settop(L, 0);
|
lua_settop(L, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
|
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
|
||||||
RegisterThread(lua->GetFileName());
|
|
||||||
std::lock_guard<std::mutex> lockGuard(lua->Lock);
|
std::lock_guard<std::mutex> lockGuard(lua->Lock);
|
||||||
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return CallFunction(lua, R, arg); });
|
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return CallFunction(lua, R, arg); });
|
||||||
std::future<std::any> f1 = task.get_future();
|
std::future<std::any> f1 = task.get_future();
|
||||||
@@ -56,6 +88,7 @@ std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> a
|
|||||||
SendError(lua->Engine(), lua->GetState(), R + " took too long to respond");
|
SendError(lua->Engine(), lua->GetState(), R + " took too long to respond");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
||||||
Assert(lua);
|
Assert(lua);
|
||||||
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return Trigger(lua, R, arg); });
|
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return Trigger(lua, R, arg); });
|
||||||
@@ -70,17 +103,16 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
|
|||||||
return f1.get();
|
return f1.get();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
||||||
std::any R;
|
std::any R;
|
||||||
std::string Type;
|
|
||||||
int Ret = 0;
|
int Ret = 0;
|
||||||
for (auto& Script : Engine().LuaFiles()) {
|
for (auto& Script : Engine().LuaFiles()) {
|
||||||
if (Script->IsRegistered(Event)) {
|
if (Script->IsRegistered(Event)) {
|
||||||
if (local) {
|
if (local) {
|
||||||
if (Script->GetPluginName() == Caller->GetPluginName()) {
|
if (Script->GetPluginName() == Caller->GetPluginName()) {
|
||||||
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
||||||
Type = R.type().name();
|
if (R.type() == typeid(int)) {
|
||||||
if (Type.find("int") != std::string::npos) {
|
|
||||||
if (std::any_cast<int>(R))
|
if (std::any_cast<int>(R))
|
||||||
Ret++;
|
Ret++;
|
||||||
} else if (Event == "onPlayerAuth")
|
} else if (Event == "onPlayerAuth")
|
||||||
@@ -88,8 +120,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
||||||
Type = R.type().name();
|
if (R.type() == typeid(int)) {
|
||||||
if (Type.find("int") != std::string::npos) {
|
|
||||||
if (std::any_cast<int>(R))
|
if (std::any_cast<int>(R))
|
||||||
Ret++;
|
Ret++;
|
||||||
} else if (Event == "onPlayerAuth")
|
} else if (Event == "onPlayerAuth")
|
||||||
@@ -99,6 +130,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
|
|||||||
}
|
}
|
||||||
return Ret;
|
return Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConsoleCheck(lua_State* L, int r) {
|
bool ConsoleCheck(lua_State* L, int r) {
|
||||||
if (r != LUA_OK) {
|
if (r != LUA_OK) {
|
||||||
std::string msg = lua_tostring(L, -1);
|
std::string msg = lua_tostring(L, -1);
|
||||||
@@ -107,9 +139,16 @@ bool ConsoleCheck(lua_State* L, int r) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckLua(lua_State* L, int r) {
|
bool CheckLua(lua_State* L, int r) {
|
||||||
if (r != LUA_OK) {
|
if (r != LUA_OK) {
|
||||||
std::string msg = lua_tostring(L, -1);
|
std::string msg = "Unknown";
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
auto MsgMaybe = lua_tostring(L, -1);
|
||||||
|
if (MsgMaybe) {
|
||||||
|
msg = MsgMaybe;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto MaybeS = Engine().GetScript(L);
|
auto MaybeS = Engine().GetScript(L);
|
||||||
if (MaybeS.has_value()) {
|
if (MaybeS.has_value()) {
|
||||||
TLuaFile& S = MaybeS.value();
|
TLuaFile& S = MaybeS.value();
|
||||||
@@ -117,7 +156,7 @@ bool CheckLua(lua_State* L, int r) {
|
|||||||
warn(a + " | " + msg);
|
warn(a + " | " + msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// What the fuck, what do we do?!
|
// This should never happen since it's not directly called from "userspace" Lua.
|
||||||
AssertNotReachable();
|
AssertNotReachable();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -126,7 +165,10 @@ bool CheckLua(lua_State* L, int r) {
|
|||||||
int lua_RegisterEvent(lua_State* L) {
|
int lua_RegisterEvent(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("RegisterEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
|
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
|
||||||
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
|
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
|
||||||
@@ -134,10 +176,14 @@ int lua_RegisterEvent(lua_State* L) {
|
|||||||
SendError(Engine(), L, "RegisterEvent invalid argument count expected 2 got " + std::to_string(Args));
|
SendError(Engine(), L, "RegisterEvent invalid argument count expected 2 got " + std::to_string(Args));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_TriggerEventL(lua_State* L) {
|
int lua_TriggerEventL(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("TriggerEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args > 0) {
|
if (Args > 0) {
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@@ -153,7 +199,10 @@ int lua_TriggerEventL(lua_State* L) {
|
|||||||
int lua_TriggerEventG(lua_State* L) {
|
int lua_TriggerEventG(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("TriggerGlobalEvent: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
if (Args > 0) {
|
if (Args > 0) {
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@@ -181,7 +230,7 @@ void ExecuteAsync(TLuaFile* lua, const std::string& FuncName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
||||||
RegisterThread(lua->GetFileName());
|
|
||||||
lua->SetStopThread(false);
|
lua->SetStopThread(false);
|
||||||
int D = 1000 / U;
|
int D = 1000 / U;
|
||||||
while (!lua->GetStopThread()) {
|
while (!lua->GetStopThread()) {
|
||||||
@@ -192,11 +241,15 @@ void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
|
|||||||
|
|
||||||
int lua_StopThread(lua_State* L) {
|
int lua_StopThread(lua_State* L) {
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
// ugly, but whatever, this is safe as fuck
|
error("StopThread: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// ugly, but whatever, this is very safe
|
||||||
MaybeScript.value().get().SetStopThread(true);
|
MaybeScript.value().get().SetStopThread(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_CreateThread(lua_State* L) {
|
int lua_CreateThread(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
if (Args > 1) {
|
if (Args > 1) {
|
||||||
@@ -206,7 +259,10 @@ int lua_CreateThread(lua_State* L) {
|
|||||||
int U = int(lua_tointeger(L, 2));
|
int U = int(lua_tointeger(L, 2));
|
||||||
if (U > 0 && U < 501) {
|
if (U > 0 && U < 501) {
|
||||||
auto MaybeScript = Engine().GetScript(L);
|
auto MaybeScript = Engine().GetScript(L);
|
||||||
Assert(MaybeScript.has_value());
|
if (!MaybeScript.has_value()) {
|
||||||
|
error("CreateThread: There is no script associated with this lua_State.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
TLuaFile& Script = MaybeScript.value();
|
TLuaFile& Script = MaybeScript.value();
|
||||||
std::thread t1(CallAsync, &Script, STR, U);
|
std::thread t1(CallAsync, &Script, STR, U);
|
||||||
t1.detach();
|
t1.detach();
|
||||||
@@ -220,6 +276,7 @@ int lua_CreateThread(lua_State* L) {
|
|||||||
SendError(Engine(), L, ("CreateThread not enough arguments"));
|
SendError(Engine(), L, ("CreateThread not enough arguments"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_Sleep(lua_State* L) {
|
int lua_Sleep(lua_State* L) {
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
int t = int(lua_tonumber(L, 1));
|
int t = int(lua_tonumber(L, 1));
|
||||||
@@ -230,6 +287,7 @@ int lua_Sleep(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
|
std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
|
||||||
std::optional<std::weak_ptr<TClient>> MaybeClient { std::nullopt };
|
std::optional<std::weak_ptr<TClient>> MaybeClient { std::nullopt };
|
||||||
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool {
|
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool {
|
||||||
@@ -245,7 +303,7 @@ std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
|
|||||||
});
|
});
|
||||||
return MaybeClient;
|
return MaybeClient;
|
||||||
}
|
}
|
||||||
// CONTINUE
|
|
||||||
int lua_isConnected(lua_State* L) {
|
int lua_isConnected(lua_State* L) {
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
int ID = int(lua_tonumber(L, 1));
|
int ID = int(lua_tonumber(L, 1));
|
||||||
@@ -260,6 +318,7 @@ int lua_isConnected(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_GetPlayerName(lua_State* L) {
|
int lua_GetPlayerName(lua_State* L) {
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
int ID = int(lua_tonumber(L, 1));
|
int ID = int(lua_tonumber(L, 1));
|
||||||
@@ -274,10 +333,12 @@ int lua_GetPlayerName(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_GetPlayerCount(lua_State* L) {
|
int lua_GetPlayerCount(lua_State* L) {
|
||||||
lua_pushinteger(L, Engine().Server().ClientCount());
|
lua_pushinteger(L, Engine().Server().ClientCount());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_GetGuest(lua_State* L) {
|
int lua_GetGuest(lua_State* L) {
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
int ID = int(lua_tonumber(L, 1));
|
int ID = int(lua_tonumber(L, 1));
|
||||||
@@ -292,6 +353,7 @@ int lua_GetGuest(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_GetAllPlayers(lua_State* L) {
|
int lua_GetAllPlayers(lua_State* L) {
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
Engine().Server().ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
Engine().Server().ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
||||||
@@ -311,6 +373,7 @@ int lua_GetAllPlayers(lua_State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_GetIdentifiers(lua_State* L) {
|
int lua_GetIdentifiers(lua_State* L) {
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1)));
|
auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1)));
|
||||||
@@ -318,11 +381,11 @@ int lua_GetIdentifiers(lua_State* L) {
|
|||||||
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
|
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
|
||||||
if (IDs.empty())
|
if (IDs.empty())
|
||||||
return 0;
|
return 0;
|
||||||
lua_newtable(L);
|
LuaTable::Begin(L);
|
||||||
for (const std::string& ID : IDs) {
|
for (const auto& Pair : IDs) {
|
||||||
lua_pushstring(L, ID.substr(0, ID.find(':')).c_str());
|
LuaTable::BeginEntry(L, Pair.first);
|
||||||
lua_pushstring(L, ID.c_str());
|
lua_pushstring(L, Pair.second.c_str());
|
||||||
lua_settable(L, -3);
|
LuaTable::EndEntry(L);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
return 0;
|
return 0;
|
||||||
@@ -342,7 +405,7 @@ int lua_GetCars(lua_State* L) {
|
|||||||
TClient::TSetOfVehicleData VehicleData;
|
TClient::TSetOfVehicleData VehicleData;
|
||||||
{ // Vehicle Data Lock Scope
|
{ // Vehicle Data Lock Scope
|
||||||
auto LockedData = Client->GetAllCars();
|
auto LockedData = Client->GetAllCars();
|
||||||
VehicleData = LockedData.VehicleData;
|
VehicleData = *LockedData.VehicleData;
|
||||||
} // End Vehicle Data Lock Scope
|
} // End Vehicle Data Lock Scope
|
||||||
if (VehicleData.empty())
|
if (VehicleData.empty())
|
||||||
return 0;
|
return 0;
|
||||||
@@ -360,6 +423,7 @@ int lua_GetCars(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_dropPlayer(lua_State* L) {
|
int lua_dropPlayer(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
if (lua_isnumber(L, 1)) {
|
if (lua_isnumber(L, 1)) {
|
||||||
@@ -380,12 +444,15 @@ int lua_dropPlayer(lua_State* L) {
|
|||||||
SendError(Engine(), L, ("DropPlayer not enough arguments"));
|
SendError(Engine(), L, ("DropPlayer not enough arguments"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_sendChat(lua_State* L) {
|
int lua_sendChat(lua_State* L) {
|
||||||
if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) {
|
if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) {
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
int ID = int(lua_tointeger(L, 1));
|
int ID = int(lua_tointeger(L, 1));
|
||||||
if (ID == -1) {
|
if (ID == -1) {
|
||||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
auto msg = std::string(lua_tostring(L, 2));
|
||||||
|
LogChatMessage("<Server> (to everyone) ", -1, msg);
|
||||||
|
std::string Packet = "C:Server: " + msg;
|
||||||
Engine().Network().SendToAll(nullptr, Packet, true, true);
|
Engine().Network().SendToAll(nullptr, Packet, true, true);
|
||||||
} else {
|
} else {
|
||||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||||
@@ -393,7 +460,9 @@ int lua_sendChat(lua_State* L) {
|
|||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!c->IsSynced())
|
if (!c->IsSynced())
|
||||||
return 0;
|
return 0;
|
||||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
auto msg = std::string(lua_tostring(L, 2));
|
||||||
|
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, msg);
|
||||||
|
std::string Packet = "C:Server: " + msg;
|
||||||
Engine().Network().Respond(*c, Packet, true);
|
Engine().Network().Respond(*c, Packet, true);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
||||||
@@ -404,6 +473,7 @@ int lua_sendChat(lua_State* L) {
|
|||||||
SendError(Engine(), L, ("SendChatMessage invalid argument [1] expected number"));
|
SendError(Engine(), L, ("SendChatMessage invalid argument [1] expected number"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_RemoveVehicle(lua_State* L) {
|
int lua_RemoveVehicle(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
if (Args != 2) {
|
if (Args != 2) {
|
||||||
@@ -428,10 +498,12 @@ int lua_RemoveVehicle(lua_State* L) {
|
|||||||
SendError(Engine(), L, ("RemoveVehicle invalid argument expected number"));
|
SendError(Engine(), L, ("RemoveVehicle invalid argument expected number"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_HWID(lua_State* L) {
|
int lua_HWID(lua_State* L) {
|
||||||
lua_pushinteger(L, -1);
|
lua_pushinteger(L, -1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_RemoteEvent(lua_State* L) {
|
int lua_RemoteEvent(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
if (Args != 3) {
|
if (Args != 3) {
|
||||||
@@ -465,14 +537,12 @@ int lua_RemoteEvent(lua_State* L) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int lua_ServerExit(lua_State* L) {
|
|
||||||
if (lua_gettop(L) > 0) {
|
int lua_ServerExit(lua_State*) {
|
||||||
if (lua_isnumber(L, 1)) {
|
Application::GracefullyShutdown();
|
||||||
_Exit(int(lua_tointeger(L, 1)));
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
_Exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_Set(lua_State* L) {
|
int lua_Set(lua_State* L) {
|
||||||
int Args = lua_gettop(L);
|
int Args = lua_gettop(L);
|
||||||
if (Args != 2) {
|
if (Args != 2) {
|
||||||
@@ -495,49 +565,49 @@ int lua_Set(lua_State* L) {
|
|||||||
case 0: //debug
|
case 0: //debug
|
||||||
if (lua_isboolean(L, 2)) {
|
if (lua_isboolean(L, 2)) {
|
||||||
Application::Settings.DebugModeEnabled = lua_toboolean(L, 2);
|
Application::Settings.DebugModeEnabled = lua_toboolean(L, 2);
|
||||||
info(Name + (" | Debug -> ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
info(Name + (" | Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 0"));
|
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 0"));
|
||||||
break;
|
break;
|
||||||
case 1: //private
|
case 1: //private
|
||||||
if (lua_isboolean(L, 2)) {
|
if (lua_isboolean(L, 2)) {
|
||||||
Application::Settings.Private = lua_toboolean(L, 2);
|
Application::Settings.Private = lua_toboolean(L, 2);
|
||||||
info(Name + (" | Private -> ") + (Application::Settings.Private ? "true" : "false"));
|
info(Name + (" | Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 1"));
|
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 1"));
|
||||||
break;
|
break;
|
||||||
case 2: //max cars
|
case 2: //max cars
|
||||||
if (lua_isnumber(L, 2)) {
|
if (lua_isnumber(L, 2)) {
|
||||||
Application::Settings.MaxCars = int(lua_tointeger(L, 2));
|
Application::Settings.MaxCars = int(lua_tointeger(L, 2));
|
||||||
info(Name + (" | MaxCars -> ") + std::to_string(Application::Settings.MaxCars));
|
info(Name + (" | Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 2"));
|
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 2"));
|
||||||
break;
|
break;
|
||||||
case 3: //max players
|
case 3: //max players
|
||||||
if (lua_isnumber(L, 2)) {
|
if (lua_isnumber(L, 2)) {
|
||||||
Application::Settings.MaxPlayers = int(lua_tointeger(L, 2));
|
Application::Settings.MaxPlayers = int(lua_tointeger(L, 2));
|
||||||
info(Name + (" | MaxPlayers -> ") + std::to_string(Application::Settings.MaxPlayers));
|
info(Name + (" | Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 3"));
|
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 3"));
|
||||||
break;
|
break;
|
||||||
case 4: //Map
|
case 4: //Map
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.MapName = lua_tostring(L, 2);
|
Application::Settings.MapName = lua_tostring(L, 2);
|
||||||
info(Name + (" | MapName -> ") + Application::Settings.MapName);
|
info(Name + (" | Set `Map` to ") + Application::Settings.MapName);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 4"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 4"));
|
||||||
break;
|
break;
|
||||||
case 5: //Name
|
case 5: //Name
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.ServerName = lua_tostring(L, 2);
|
Application::Settings.ServerName = lua_tostring(L, 2);
|
||||||
info(Name + (" | ServerName -> ") + Application::Settings.ServerName);
|
info(Name + (" | Set `Name` to ") + Application::Settings.ServerName);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 5"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 5"));
|
||||||
break;
|
break;
|
||||||
case 6: //Desc
|
case 6: //Desc
|
||||||
if (lua_isstring(L, 2)) {
|
if (lua_isstring(L, 2)) {
|
||||||
Application::Settings.ServerDesc = lua_tostring(L, 2);
|
Application::Settings.ServerDesc = lua_tostring(L, 2);
|
||||||
info(Name + (" | ServerDesc -> ") + Application::Settings.ServerDesc);
|
info(Name + (" | Set `Description` to ") + Application::Settings.ServerDesc);
|
||||||
} else
|
} else
|
||||||
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 6"));
|
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 6"));
|
||||||
break;
|
break;
|
||||||
@@ -545,11 +615,11 @@ int lua_Set(lua_State* L) {
|
|||||||
warn(("Invalid config ID : ") + std::to_string(C));
|
warn(("Invalid config ID : ") + std::to_string(C));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
int lua_Print(lua_State* L) {
|
int InternalLuaPrint(lua_State* L, bool pretty) {
|
||||||
int Arg = lua_gettop(L);
|
int Arg = lua_gettop(L);
|
||||||
std::string to_print;
|
std::string to_print;
|
||||||
for (int i = 1; i <= Arg; i++) {
|
for (int i = 1; i <= Arg; i++) {
|
||||||
@@ -582,14 +652,81 @@ int lua_Print(lua_State* L) {
|
|||||||
to_print += "\t";
|
to_print += "\t";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
luaprint(to_print);
|
if (pretty) {
|
||||||
|
luaprint(to_print);
|
||||||
|
} else {
|
||||||
|
Application::Console().WriteRaw(to_print);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int lua_Print(lua_State* L) {
|
||||||
|
return InternalLuaPrint(L, true);
|
||||||
|
}
|
||||||
|
int lua_PrintRaw(lua_State* L) {
|
||||||
|
return InternalLuaPrint(L, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_TempFix(lua_State* L);
|
int lua_TempFix(lua_State* L) {
|
||||||
|
if (lua_isnumber(L, 1)) {
|
||||||
|
int ID = int(lua_tonumber(L, 1));
|
||||||
|
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||||
|
if (!MaybeClient || MaybeClient.value().expired())
|
||||||
|
return 0;
|
||||||
|
std::string Ret;
|
||||||
|
auto c = MaybeClient.value().lock();
|
||||||
|
if (c->IsGuest()) {
|
||||||
|
Ret = "Guest-" + c->GetName();
|
||||||
|
} else
|
||||||
|
Ret = c->GetName();
|
||||||
|
lua_pushstring(L, Ret.c_str());
|
||||||
|
} else
|
||||||
|
SendError(Engine(), L, "GetDID not enough arguments");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Registered(lua_State* L) {
|
||||||
|
|
||||||
|
lua_Debug info;
|
||||||
|
lua_getstack(L, 0, &info);
|
||||||
|
lua_getinfo(L, "n", &info);
|
||||||
|
|
||||||
|
if (auto it = TLuaEngine::mGlobals.find(info.name); it != TLuaEngine::mGlobals.end()) {
|
||||||
|
lua_getglobal(it->second, info.name);
|
||||||
|
if (lua_isfunction(it->second, -1)) {
|
||||||
|
lua_pcall(it->second, 0, 0, 0); //TODO revisit to allow arguments and return also we need to mutex this
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendError(Engine(), L, "Cannot find global '" + std::string(info.name) + "\'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Register(lua_State* L) {
|
||||||
|
if (lua_isstring(L, 1)) {
|
||||||
|
std::string Name(lua_tolstring(L, 1, nullptr));
|
||||||
|
lua_getglobal(L, Name.c_str());
|
||||||
|
if (lua_isfunction(L, -1)) {
|
||||||
|
TLuaEngine::mGlobals.emplace(Name, L);
|
||||||
|
for (auto& Script : Engine().LuaFiles()) {
|
||||||
|
if (Script->GetState() != L) {
|
||||||
|
lua_register(Script->GetState(), Name.c_str(), lua_Registered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
SendError(Engine(), L, Name + " is not a global function!");
|
||||||
|
ClearStack(L);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SendError(Engine(), L, "Wrong arguments to `Register`, expected string");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
|
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
|
||||||
|
auto Lock = std::unique_lock(mInitMutex);
|
||||||
// set global engine for lua_* functions
|
// set global engine for lua_* functions
|
||||||
if (!TheEngine) {
|
if (!TheEngine) {
|
||||||
TheEngine = &mEngine;
|
TheEngine = &mEngine;
|
||||||
@@ -619,17 +756,19 @@ void TLuaFile::Execute(const std::string& Command) {
|
|||||||
lua_settop(mLuaState, 0);
|
lua_settop(mLuaState, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaFile::Reload() {
|
void TLuaFile::Reload() {
|
||||||
if (CheckLua(mLuaState, luaL_dofile(mLuaState, mFileName.c_str()))) {
|
if (CheckLua(mLuaState, luaL_dofile(mLuaState, mFileName.c_str()))) {
|
||||||
CallFunction(this, ("onInit"), nullptr);
|
CallFunction(this, ("onInit"), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TLuaFile::GetOrigin() {
|
std::string TLuaFile::GetOrigin() {
|
||||||
return fs::path(GetFileName()).filename().string();
|
return fs::path(GetFileName()).filename().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
|
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
|
||||||
RegisterThread(lua->GetFileName());
|
|
||||||
lua_State* luaState = lua->GetState();
|
lua_State* luaState = lua->GetState();
|
||||||
lua_getglobal(luaState, FuncName.c_str());
|
lua_getglobal(luaState, FuncName.c_str());
|
||||||
if (lua_isfunction(luaState, -1)) {
|
if (lua_isfunction(luaState, -1)) {
|
||||||
@@ -641,9 +780,13 @@ std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_pt
|
|||||||
int R = lua_pcall(luaState, Size, 1, 0);
|
int R = lua_pcall(luaState, Size, 1, 0);
|
||||||
if (CheckLua(luaState, R)) {
|
if (CheckLua(luaState, R)) {
|
||||||
if (lua_isnumber(luaState, -1)) {
|
if (lua_isnumber(luaState, -1)) {
|
||||||
return int(lua_tointeger(luaState, -1));
|
auto ret = int(lua_tointeger(luaState, -1));
|
||||||
|
ClearStack(luaState);
|
||||||
|
return ret;
|
||||||
} else if (lua_isstring(luaState, -1)) {
|
} else if (lua_isstring(luaState, -1)) {
|
||||||
return std::string(lua_tostring(luaState, -1));
|
auto ret = std::string(lua_tostring(luaState, -1));
|
||||||
|
ClearStack(luaState);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,31 +802,203 @@ void TLuaFile::SetFileName(const std::string& Name) {
|
|||||||
mFileName = Name;
|
mFileName = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOSName() -> Linux || Windows || Other
|
||||||
|
int lua_GetOSName(lua_State* L) {
|
||||||
|
#if defined(__linux) || defined(__linux__)
|
||||||
|
lua_pushstring(L, "Linux");
|
||||||
|
#elif defined(WIN32)
|
||||||
|
lua_pushstring(L, "Windows");
|
||||||
|
#else
|
||||||
|
lua_pushstring(L, "Unknown");
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetServerVersion(lua_State* L) {
|
||||||
|
const auto& ver = Application::ServerVersion();
|
||||||
|
lua_pushinteger(L, ver.major);
|
||||||
|
lua_pushinteger(L, ver.minor);
|
||||||
|
lua_pushinteger(L, ver.patch);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// status, body = HttpsGET(host, port, target)
|
||||||
|
// example usage:
|
||||||
|
// send a GET https://example.com:443/index.html:
|
||||||
|
// status, body = MP.HttpGET("example.com", 443, "/index.html")
|
||||||
|
int lua_HttpsGET(lua_State* L) {
|
||||||
|
if (!lua_isstring(L, 1)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects host (type string) as first argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isnumber(L, 2)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects port (type number) as second argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 3)) {
|
||||||
|
SendError(Engine(), L, "`HttpsGET` expects target (type string) as third argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Host = lua_tostring(L, 1);
|
||||||
|
auto Port = int(lua_tointeger(L, 2));
|
||||||
|
auto Target = lua_tostring(L, 3);
|
||||||
|
|
||||||
|
ClearStack(L);
|
||||||
|
|
||||||
|
unsigned int Status;
|
||||||
|
auto Body = Http::GET(Host, Port, Target, &Status);
|
||||||
|
lua_pushinteger(L, Status);
|
||||||
|
|
||||||
|
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
|
||||||
|
if (Body == Http::ErrorString) {
|
||||||
|
SendError(Engine(), L, "HTTPS GET " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
debug("GET " + PrettyRemote + " completed status " + std::to_string(Status));
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, Body.c_str());
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// status, body = HttpsPOST(host, port, target, body, content_type)
|
||||||
|
int lua_HttpsPOST(lua_State* L) {
|
||||||
|
if (!lua_isstring(L, 1)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects host (type string) as 1. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isnumber(L, 2)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects port (type number) as 2. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 3)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects target (type string) as 3. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 4)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects body (type string) as 4. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!lua_isstring(L, 5)) {
|
||||||
|
SendError(Engine(), L, "`HttpsPOST` expects content_type (type string) as 5. argument.");
|
||||||
|
ClearStack(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Host = lua_tostring(L, 1);
|
||||||
|
auto Port = int(lua_tointeger(L, 2));
|
||||||
|
auto Target = lua_tostring(L, 3);
|
||||||
|
auto RequestBody = lua_tostring(L, 4);
|
||||||
|
auto ContentType = lua_tostring(L, 5);
|
||||||
|
|
||||||
|
ClearStack(L);
|
||||||
|
|
||||||
|
// build fields
|
||||||
|
std::unordered_map<std::string, std::string> Fields;
|
||||||
|
|
||||||
|
unsigned int Status;
|
||||||
|
auto ResponseBody = Http::POST(Host, Port, Target, {}, RequestBody, ContentType, &Status);
|
||||||
|
|
||||||
|
lua_pushinteger(L, Status);
|
||||||
|
|
||||||
|
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
|
||||||
|
if (ResponseBody == Http::ErrorString) {
|
||||||
|
SendError(Engine(), L, "HTTPS POST " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
debug("POST " + PrettyRemote + " completed status " + std::to_string(Status));
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pushstring(L, ResponseBody.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetPluginName(lua_State* L) {
|
||||||
|
auto MaybeFile = Engine().GetScript(L);
|
||||||
|
if (MaybeFile) {
|
||||||
|
lua_pushstring(L, MaybeFile.value().get().GetPluginName().c_str());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
warn("no plugin associated with this state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_GetPluginPath(lua_State* L) {
|
||||||
|
auto MaybeFile = Engine().GetScript(L);
|
||||||
|
if (MaybeFile) {
|
||||||
|
lua_pushstring(L, MaybeFile.value().get().GetPluginPath().c_str());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
warn("no plugin associated with this state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lua_Dump(lua_State* L) {
|
||||||
|
}
|
||||||
|
|
||||||
void TLuaFile::Load() {
|
void TLuaFile::Load() {
|
||||||
Assert(mLuaState);
|
Assert(mLuaState);
|
||||||
luaL_openlibs(mLuaState);
|
luaL_openlibs(mLuaState);
|
||||||
lua_register(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
|
|
||||||
lua_register(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
|
LuaTable::Begin(mLuaState);
|
||||||
lua_register(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
|
|
||||||
lua_register(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
|
LuaTable::BeginEntry(mLuaState, "Settings");
|
||||||
lua_register(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
|
LuaTable::Begin(mLuaState);
|
||||||
lua_register(mLuaState, "isPlayerConnected", lua_isConnected);
|
// put Settings enums here
|
||||||
lua_register(mLuaState, "RegisterEvent", lua_RegisterEvent);
|
LuaTable::InsertInteger(mLuaState, "Debug", 0);
|
||||||
lua_register(mLuaState, "GetPlayerName", lua_GetPlayerName);
|
LuaTable::InsertInteger(mLuaState, "Private", 1);
|
||||||
lua_register(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
|
LuaTable::InsertInteger(mLuaState, "MaxCars", 2);
|
||||||
lua_register(mLuaState, "GetPlayerDiscordID", lua_TempFix);
|
LuaTable::InsertInteger(mLuaState, "MaxPlayers", 3);
|
||||||
lua_register(mLuaState, "CreateThread", lua_CreateThread);
|
LuaTable::InsertInteger(mLuaState, "Map", 4);
|
||||||
lua_register(mLuaState, "GetPlayerVehicles", lua_GetCars);
|
LuaTable::InsertInteger(mLuaState, "Name", 5);
|
||||||
lua_register(mLuaState, "SendChatMessage", lua_sendChat);
|
LuaTable::InsertInteger(mLuaState, "Description", 6);
|
||||||
lua_register(mLuaState, "GetPlayers", lua_GetAllPlayers);
|
LuaTable::EndEntry(mLuaState);
|
||||||
lua_register(mLuaState, "GetPlayerGuest", lua_GetGuest);
|
|
||||||
lua_register(mLuaState, "StopThread", lua_StopThread);
|
LuaTable::InsertFunction(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
|
||||||
lua_register(mLuaState, "DropPlayer", lua_dropPlayer);
|
LuaTable::InsertFunction(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
|
||||||
lua_register(mLuaState, "GetPlayerHWID", lua_HWID);
|
LuaTable::InsertFunction(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
|
||||||
lua_register(mLuaState, "exit", lua_ServerExit);
|
LuaTable::InsertFunction(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
|
||||||
lua_register(mLuaState, "Sleep", lua_Sleep);
|
LuaTable::InsertFunction(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "IsPlayerConnected", lua_isConnected);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "RegisterEvent", lua_RegisterEvent);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerName", lua_GetPlayerName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerDiscordID", lua_TempFix);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "CreateThread", lua_CreateThread);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerVehicles", lua_GetCars);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "SendChatMessage", lua_sendChat);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayers", lua_GetAllPlayers);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerGuest", lua_GetGuest);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "StopThread", lua_StopThread);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "DropPlayer", lua_dropPlayer);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Register", lua_Register);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPlayerHWID", lua_HWID);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Sleep", lua_Sleep);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "Set", lua_Set);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetOSName", lua_GetOSName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "HttpsGET", lua_HttpsGET);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "HttpsPOST", lua_HttpsPOST);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetServerVersion", lua_GetServerVersion);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPluginName", lua_GetPluginName);
|
||||||
|
LuaTable::InsertFunction(mLuaState, "GetPluginPath", lua_GetPluginPath);
|
||||||
|
|
||||||
|
LuaTable::End(mLuaState, "MP");
|
||||||
|
|
||||||
lua_register(mLuaState, "print", lua_Print);
|
lua_register(mLuaState, "print", lua_Print);
|
||||||
lua_register(mLuaState, "Set", lua_Set);
|
lua_register(mLuaState, "printRaw", lua_PrintRaw);
|
||||||
|
lua_register(mLuaState, "dump", lua_Dump);
|
||||||
|
lua_register(mLuaState, "exit", lua_ServerExit);
|
||||||
if (!mConsole)
|
if (!mConsole)
|
||||||
Reload();
|
Reload();
|
||||||
}
|
}
|
||||||
@@ -691,6 +1006,7 @@ void TLuaFile::Load() {
|
|||||||
void TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
|
void TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
|
||||||
mRegisteredEvents.insert(std::make_pair(Event, FunctionName));
|
mRegisteredEvents.insert(std::make_pair(Event, FunctionName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaFile::UnRegisterEvent(const std::string& Event) {
|
void TLuaFile::UnRegisterEvent(const std::string& Event) {
|
||||||
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
||||||
if (a.first == Event) {
|
if (a.first == Event) {
|
||||||
@@ -699,6 +1015,7 @@ void TLuaFile::UnRegisterEvent(const std::string& Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TLuaFile::IsRegistered(const std::string& Event) {
|
bool TLuaFile::IsRegistered(const std::string& Event) {
|
||||||
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
||||||
if (a.first == Event)
|
if (a.first == Event)
|
||||||
@@ -706,6 +1023,7 @@ bool TLuaFile::IsRegistered(const std::string& Event) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TLuaFile::GetRegistered(const std::string& Event) const {
|
std::string TLuaFile::GetRegistered(const std::string& Event) const {
|
||||||
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
|
||||||
if (a.first == Event)
|
if (a.first == Event)
|
||||||
@@ -713,12 +1031,15 @@ std::string TLuaFile::GetRegistered(const std::string& Event) const {
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TLuaFile::GetFileName() const {
|
std::string TLuaFile::GetFileName() const {
|
||||||
return mFileName;
|
return mFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TLuaFile::GetPluginName() const {
|
std::string TLuaFile::GetPluginName() const {
|
||||||
return mPluginName;
|
return mPluginName;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_State* TLuaFile::GetState() {
|
lua_State* TLuaFile::GetState() {
|
||||||
return mLuaState;
|
return mLuaState;
|
||||||
}
|
}
|
||||||
@@ -739,6 +1060,13 @@ TLuaFile::~TLuaFile() {
|
|||||||
lua_close(mLuaState);
|
lua_close(mLuaState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TLuaFile::GetPluginPath() const {
|
||||||
|
auto path = fs::path(Application::Settings.Resource);
|
||||||
|
path /= "Server";
|
||||||
|
path /= mPluginName;
|
||||||
|
return path.string();
|
||||||
|
}
|
||||||
|
|
||||||
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
||||||
Assert(L);
|
Assert(L);
|
||||||
auto MaybeS = Engine.GetScript(L);
|
auto MaybeS = Engine.GetScript(L);
|
||||||
@@ -749,42 +1077,31 @@ void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
|
|||||||
TLuaFile& S = MaybeS.value();
|
TLuaFile& S = MaybeS.value();
|
||||||
a = fs::path(S.GetFileName()).filename().string();
|
a = fs::path(S.GetFileName()).filename().string();
|
||||||
}
|
}
|
||||||
warn(a + (" | Incorrect Call of ") + msg);
|
warn(a + (" | Error in MP Lua call: ") + msg);
|
||||||
}
|
|
||||||
int lua_TempFix(lua_State* L) {
|
|
||||||
if (lua_isnumber(L, 1)) {
|
|
||||||
int ID = int(lua_tonumber(L, 1));
|
|
||||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
|
||||||
if (!MaybeClient || MaybeClient.value().expired())
|
|
||||||
return 0;
|
|
||||||
std::string Ret;
|
|
||||||
auto c = MaybeClient.value().lock();
|
|
||||||
if (c->IsGuest()) {
|
|
||||||
Ret = "Guest-" + c->GetName();
|
|
||||||
} else
|
|
||||||
Ret = c->GetName();
|
|
||||||
lua_pushstring(L, Ret.c_str());
|
|
||||||
} else
|
|
||||||
SendError(Engine(), L, "GetDID not enough arguments");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaArg::PushArgs(lua_State* State) {
|
void TLuaArg::PushArgs(lua_State* State) {
|
||||||
for (std::any arg : args) {
|
for (std::any arg : args) {
|
||||||
if (!arg.has_value())
|
if (!arg.has_value()) {
|
||||||
|
error("arg didn't have a value, this is not expected, bad");
|
||||||
return;
|
return;
|
||||||
std::string Type = arg.type().name();
|
}
|
||||||
if (Type.find("bool") != std::string::npos) {
|
const auto& Type = arg.type();
|
||||||
|
if (Type == typeid(bool)) {
|
||||||
lua_pushboolean(State, std::any_cast<bool>(arg));
|
lua_pushboolean(State, std::any_cast<bool>(arg));
|
||||||
}
|
} else if (Type == typeid(std::string)) {
|
||||||
if (Type.find("basic_string") != std::string::npos || Type.find("char") != std::string::npos) {
|
|
||||||
lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
|
lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
|
||||||
}
|
} else if (Type == typeid(const char*)) {
|
||||||
if (Type.find("int") != std::string::npos) {
|
lua_pushstring(State, std::any_cast<const char*>(arg));
|
||||||
|
} else if (Type == typeid(int)) {
|
||||||
lua_pushinteger(State, std::any_cast<int>(arg));
|
lua_pushinteger(State, std::any_cast<int>(arg));
|
||||||
}
|
} else if (Type == typeid(float)) {
|
||||||
if (Type.find("float") != std::string::npos) {
|
|
||||||
lua_pushnumber(State, std::any_cast<float>(arg));
|
lua_pushnumber(State, std::any_cast<float>(arg));
|
||||||
|
} else if (Type == typeid(double)) {
|
||||||
|
lua_pushnumber(State, std::any_cast<double>(arg));
|
||||||
|
} else {
|
||||||
|
// if this happens, implement a sane behavior for that value
|
||||||
|
error("what in the hell is " + std::string(arg.type().name()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+140
-186
@@ -9,6 +9,15 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
: mServer(Server)
|
: mServer(Server)
|
||||||
, mPPSMonitor(PPSMonitor)
|
, mPPSMonitor(PPSMonitor)
|
||||||
, mResourceManager(ResourceManager) {
|
, mResourceManager(ResourceManager) {
|
||||||
|
Application::RegisterShutdownHandler([&] {
|
||||||
|
debug("Kicking all players due to shutdown");
|
||||||
|
Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool {
|
||||||
|
if (!client.expired()) {
|
||||||
|
ClientKick(*client.lock(), "Server shutdown");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mUDPThread.joinable()) {
|
if (mUDPThread.joinable()) {
|
||||||
debug("shutting down TCPServer");
|
debug("shutting down TCPServer");
|
||||||
@@ -18,7 +27,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mUDPThread.joinable()) {
|
if (mTCPThread.joinable()) {
|
||||||
debug("shutting down TCPServer");
|
debug("shutting down TCPServer");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mTCPThread.detach();
|
mTCPThread.detach();
|
||||||
@@ -37,22 +46,7 @@ void TNetwork::UDPServerMain() {
|
|||||||
error(("Can't start Winsock!"));
|
error(("Can't start Winsock!"));
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
|
#endif // WIN32
|
||||||
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
// Create a server hint structure for the server
|
|
||||||
sockaddr_in serverAddr {};
|
|
||||||
serverAddr.sin_addr.S_un.S_addr = ADDR_ANY; //Any Local
|
|
||||||
serverAddr.sin_family = AF_INET; // Address format is IPv4
|
|
||||||
serverAddr.sin_port = htons(Application::Settings.Port); // Convert from little to big endian
|
|
||||||
|
|
||||||
// Try and bind the socket to the IP and port
|
|
||||||
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
|
|
||||||
error(("Can't bind socket!") + std::to_string(WSAGetLastError()));
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
||||||
exit(-1);
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
#else // unix
|
|
||||||
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
// Create a server hint structure for the server
|
// Create a server hint structure for the server
|
||||||
sockaddr_in serverAddr {};
|
sockaddr_in serverAddr {};
|
||||||
@@ -62,12 +56,11 @@ void TNetwork::UDPServerMain() {
|
|||||||
|
|
||||||
// Try and bind the socket to the IP and port
|
// Try and bind the socket to the IP and port
|
||||||
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
|
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
|
||||||
error(("Can't bind socket!") + std::string(strerror(errno)));
|
error("bind() failed: " + GetPlatformAgnosticErrorString());
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
|
||||||
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
|
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
|
||||||
@@ -88,11 +81,11 @@ void TNetwork::UDPServerMain() {
|
|||||||
ReadLock Lock(mServer.GetClientMutex());
|
ReadLock Lock(mServer.GetClientMutex());
|
||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
Client = ClientPtr.lock();
|
Client = ClientPtr.lock();
|
||||||
}else return true;
|
} else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Client->GetID() == ID) {
|
if (Client->GetID() == ID) {
|
||||||
Client->UpdatePingTime();
|
|
||||||
Client->SetUDPAddr(client);
|
Client->SetUDPAddr(client);
|
||||||
Client->SetIsConnected(true);
|
Client->SetIsConnected(true);
|
||||||
TServer::GlobalParser(ClientPtr, Data.substr(2), mPPSMonitor, *this);
|
TServer::GlobalParser(ClientPtr, Data.substr(2), mPPSMonitor, *this);
|
||||||
@@ -114,13 +107,23 @@ void TNetwork::TCPServerMain() {
|
|||||||
error("Can't start Winsock!");
|
error("Can't start Winsock!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SOCKET client, Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
#endif // WIN32
|
||||||
|
TConnection client {};
|
||||||
|
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
int optval = 1;
|
||||||
|
#ifdef WIN32
|
||||||
|
const char* optval_ptr = reinterpret_cast<const char*>(&optval);
|
||||||
|
#else
|
||||||
|
void* optval_ptr = reinterpret_cast<void*>(&optval);
|
||||||
|
#endif
|
||||||
|
setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, optval_ptr, sizeof(optval));
|
||||||
|
// TODO: check optval or return value idk
|
||||||
sockaddr_in addr {};
|
sockaddr_in addr {};
|
||||||
addr.sin_addr.S_un.S_addr = ADDR_ANY;
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(Application::Settings.Port);
|
addr.sin_port = htons(uint16_t(Application::Settings.Port));
|
||||||
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||||
error("Can't bind socket! " + std::to_string(WSAGetLastError()));
|
error("bind() failed: " + GetPlatformAgnosticErrorString());
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
@@ -128,53 +131,9 @@ void TNetwork::TCPServerMain() {
|
|||||||
error("Invalid listening socket");
|
error("Invalid listening socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(Listener, SOMAXCONN)) {
|
if (listen(Listener, SOMAXCONN)) {
|
||||||
error("listener failed " + std::to_string(GetLastError()));
|
error("listen() failed: " + GetPlatformAgnosticErrorString());
|
||||||
//TODO Fix me leak for Listener socket
|
// FIXME leak Listener
|
||||||
return;
|
|
||||||
}
|
|
||||||
info("Vehicle event network online");
|
|
||||||
do {
|
|
||||||
try {
|
|
||||||
client = accept(Listener, nullptr, nullptr);
|
|
||||||
if (client == -1) {
|
|
||||||
warn("Got an invalid client socket on connect! Skipping...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::thread ID(&TNetwork::Identify, this, client);
|
|
||||||
ID.detach();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
error("fatal: " + std::string(e.what()));
|
|
||||||
}
|
|
||||||
} while (client);
|
|
||||||
|
|
||||||
CloseSocketProper(client);
|
|
||||||
WSACleanup();
|
|
||||||
#else // unix
|
|
||||||
// wondering why we need slightly different implementations of this?
|
|
||||||
// ask ms.
|
|
||||||
SOCKET client = -1;
|
|
||||||
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
int optval = 1;
|
|
||||||
setsockopt(Listener, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
|
||||||
// TODO: check optval or return value idk
|
|
||||||
sockaddr_in addr {};
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(uint16_t(Application::Settings.Port));
|
|
||||||
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
|
|
||||||
error(("Can't bind socket! ") + std::string(strerror(errno)));
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
if (Listener == -1) {
|
|
||||||
error(("Invalid listening socket"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (listen(Listener, SOMAXCONN)) {
|
|
||||||
error(("listener failed ") + std::string(strerror(errno)));
|
|
||||||
//TODO fix me leak Listener
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info(("Vehicle event network online"));
|
info(("Vehicle event network online"));
|
||||||
@@ -184,8 +143,9 @@ void TNetwork::TCPServerMain() {
|
|||||||
debug("shutdown during TCP wait for accept loop");
|
debug("shutdown during TCP wait for accept loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
client = accept(Listener, nullptr, nullptr);
|
client.SockAddrLen = sizeof(client.SockAddr);
|
||||||
if (client == -1) {
|
client.Socket = accept(Listener, &client.SockAddr, &client.SockAddrLen);
|
||||||
|
if (client.Socket == -1) {
|
||||||
warn(("Got an invalid client socket on connect! Skipping..."));
|
warn(("Got an invalid client socket on connect! Skipping..."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -194,34 +154,35 @@ void TNetwork::TCPServerMain() {
|
|||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
error(("fatal: ") + std::string(e.what()));
|
error(("fatal: ") + std::string(e.what()));
|
||||||
}
|
}
|
||||||
} while (client);
|
} while (client.Socket);
|
||||||
|
|
||||||
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
||||||
|
|
||||||
|
CloseSocketProper(client.Socket);
|
||||||
|
#ifdef WIN32
|
||||||
CloseSocketProper(client);
|
CloseSocketProper(client);
|
||||||
#endif
|
WSACleanup();
|
||||||
|
#endif // WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef GetObject //Fixes Windows
|
#undef GetObject //Fixes Windows
|
||||||
|
|
||||||
#include "rapidjson/document.h"
|
#include "Json.h"
|
||||||
#include "rapidjson/stringbuffer.h"
|
|
||||||
#include "rapidjson/writer.h"
|
|
||||||
namespace json = rapidjson;
|
namespace json = rapidjson;
|
||||||
|
|
||||||
void TNetwork::Identify(SOCKET TCPSock) {
|
void TNetwork::Identify(const TConnection& client) {
|
||||||
RegisterThreadAuto();
|
RegisterThreadAuto();
|
||||||
char Code;
|
char Code;
|
||||||
if (recv(TCPSock, &Code, 1, 0) != 1) {
|
if (recv(client.Socket, &Code, 1, 0) != 1) {
|
||||||
CloseSocketProper(TCPSock);
|
CloseSocketProper(client.Socket);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Code == 'C') {
|
if (Code == 'C') {
|
||||||
Authentication(TCPSock);
|
Authentication(client);
|
||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(TCPSock);
|
HandleDownload(client.Socket);
|
||||||
} else {
|
} else {
|
||||||
CloseSocketProper(TCPSock);
|
CloseSocketProper(client.Socket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,17 +205,21 @@ void TNetwork::HandleDownload(SOCKET TCPSock) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::Authentication(SOCKET TCPSock) {
|
void TNetwork::Authentication(const TConnection& ClientConnection) {
|
||||||
auto Client = CreateClient(TCPSock);
|
auto Client = CreateClient(ClientConnection.Socket);
|
||||||
|
char AddrBuf[30];
|
||||||
|
inet_ntoa(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)->sin_addr);
|
||||||
|
auto str = inet_ntop(AF_INET, static_cast<const void*>(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)), AddrBuf, sizeof(struct sockaddr_in));
|
||||||
|
Client->SetIdentifier("ip", str);
|
||||||
|
|
||||||
std::string Rc;
|
std::string Rc;
|
||||||
info("Identifying new client...");
|
info("Identifying new ClientConnection...");
|
||||||
|
|
||||||
Rc = TCPRcv(*Client);
|
Rc = TCPRcv(*Client);
|
||||||
|
|
||||||
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
||||||
Rc = Rc.substr(2);
|
Rc = Rc.substr(2);
|
||||||
if (Rc.length() > 4 || Rc != Application::ClientVersion()) {
|
if (Rc.length() > 4 || Rc != Application::ClientVersionString()) {
|
||||||
ClientKick(*Client, "Outdated Version!");
|
ClientKick(*Client, "Outdated Version!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -273,20 +238,39 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto RequestString = R"({"key":")" + Rc + "\"}";
|
||||||
|
|
||||||
|
auto Target = "/pkToUser";
|
||||||
|
unsigned int ResponseCode = 0;
|
||||||
if (!Rc.empty()) {
|
if (!Rc.empty()) {
|
||||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), "/pkToUser", {}, R"({"key":")" + Rc + "\"}", true);
|
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
json::Document AuthResponse;
|
json::Document AuthResponse;
|
||||||
AuthResponse.Parse(Rc.c_str());
|
AuthResponse.Parse(Rc.c_str());
|
||||||
if (Rc == "-1" || AuthResponse.HasParseError()) {
|
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
|
||||||
ClientKick(*Client, "Invalid key! Please restart your game.");
|
ClientKick(*Client, "Invalid key! Please restart your game.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AuthResponse.IsObject()) {
|
if (!AuthResponse.IsObject()) {
|
||||||
ClientKick(*Client, "Backend returned invalid auth response format.");
|
if (Rc == "0") {
|
||||||
error("Backend returned invalid auth response format. This should never happen.");
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
|
Sentry.SetContext("auth",
|
||||||
|
{ { "response-body", Rc },
|
||||||
|
{ "key", RequestString } });
|
||||||
|
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||||
|
Sentry.Log(SentryLevel::Info, "default", "backend returned 0 instead of json (" + std::to_string(ResponseCode) + ")");
|
||||||
|
} else { // Rc != "0"
|
||||||
|
ClientKick(*Client, "Backend returned invalid auth response format.");
|
||||||
|
error("Backend returned invalid auth response format. This should never happen.");
|
||||||
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
|
Sentry.SetContext("auth",
|
||||||
|
{ { "response-body", Rc },
|
||||||
|
{ "key", RequestString } });
|
||||||
|
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
||||||
|
Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +281,9 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
Client->SetRoles(AuthResponse["roles"].GetString());
|
Client->SetRoles(AuthResponse["roles"].GetString());
|
||||||
Client->SetIsGuest(AuthResponse["guest"].GetBool());
|
Client->SetIsGuest(AuthResponse["guest"].GetBool());
|
||||||
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
|
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
|
||||||
Client->AddIdentifier(ID.GetString());
|
auto Raw = std::string(ID.GetString());
|
||||||
|
auto SepIndex = Raw.find(':');
|
||||||
|
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ClientKick(*Client, "Invalid authentication data!");
|
ClientKick(*Client, "Invalid authentication data!");
|
||||||
@@ -305,19 +291,16 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
||||||
debug("There are " + std::to_string(mServer.ClientCount()) + " known clients");
|
|
||||||
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
|
||||||
std::shared_ptr<TClient> Cl;
|
std::shared_ptr<TClient> Cl;
|
||||||
{
|
{
|
||||||
ReadLock Lock(mServer.GetClientMutex());
|
ReadLock Lock(mServer.GetClientMutex());
|
||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
Cl = ClientPtr.lock();
|
Cl = ClientPtr.lock();
|
||||||
} else return true;
|
} else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
info("Client Iteration: Name -> " + Cl->GetName() + ", Guest -> " + std::to_string(Cl->IsGuest()) + ", Roles -> " + Cl->GetRoles());
|
|
||||||
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
|
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
|
||||||
info("New client matched with current iteration");
|
|
||||||
info("Old client (" + Cl->GetName() + ") kicked: Reconnecting");
|
|
||||||
CloseSocketProper(Cl->GetTCPSock());
|
CloseSocketProper(Cl->GetTCPSock());
|
||||||
Cl->SetStatus(-2);
|
Cl->SetStatus(-2);
|
||||||
return false;
|
return false;
|
||||||
@@ -328,11 +311,10 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
|
|
||||||
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
|
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
|
||||||
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
|
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
|
||||||
std::string Type = Res.type().name();
|
if (Res.type() == typeid(int) && std::any_cast<int>(Res)) {
|
||||||
if (Type.find("int") != std::string::npos && std::any_cast<int>(Res)) {
|
|
||||||
ClientKick(*Client, "you are not allowed on the server!");
|
ClientKick(*Client, "you are not allowed on the server!");
|
||||||
return;
|
return;
|
||||||
} else if (Type.find("string") != std::string::npos) {
|
} else if (Res.type() == typeid(std::string)) {
|
||||||
ClientKick(*Client, std::any_cast<std::string>(Res));
|
ClientKick(*Client, std::any_cast<std::string>(Res));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -354,8 +336,7 @@ std::shared_ptr<TClient> TNetwork::CreateClient(SOCKET TCPSock) {
|
|||||||
bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
||||||
if (!IsSync) {
|
if (!IsSync) {
|
||||||
if (c.IsSyncing()) {
|
if (c.IsSyncing()) {
|
||||||
//std::unique_lock Lock(c.MissedPacketQueueMutex());
|
if (!Data.empty()) {
|
||||||
if(!Data.empty()) {
|
|
||||||
if (Data.at(0) == 'O' || Data.at(0) == 'A' || Data.at(0) == 'C' || Data.at(0) == 'E') {
|
if (Data.at(0) == 'O' || Data.at(0) == 'A' || Data.at(0) == 'C' || Data.at(0) == 'E') {
|
||||||
c.EnqueuePacket(Data);
|
c.EnqueuePacket(Data);
|
||||||
}
|
}
|
||||||
@@ -364,7 +345,6 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int32_t Size, Sent;
|
int32_t Size, Sent;
|
||||||
std::string Send(4, 0);
|
std::string Send(4, 0);
|
||||||
Size = int32_t(Data.size());
|
Size = int32_t(Data.size());
|
||||||
@@ -379,37 +359,33 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
|||||||
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
|
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
|
||||||
#endif //WIN32
|
#endif //WIN32
|
||||||
if (Temp == 0) {
|
if (Temp == 0) {
|
||||||
debug("send() == 0: " + std::string(std::strerror(errno)));
|
debug("send() == 0: " + GetPlatformAgnosticErrorString());
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (Temp < 0) {
|
} else if (Temp < 0) {
|
||||||
debug("send() < 0: " + std::string(std::strerror(errno))); //TODO fix it was spamming yet everyone stayed on the server
|
debug("send() < 0: " + GetPlatformAgnosticErrorString()); //TODO fix it was spamming yet everyone stayed on the server
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
CloseSocketProper(c.GetTCPSock());
|
CloseSocketProper(c.GetTCPSock());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Sent += Temp;
|
Sent += Temp;
|
||||||
|
c.UpdatePingTime();
|
||||||
} while (Sent < Size);
|
} while (Sent < Size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
||||||
if (BytesRcv == 0) {
|
if (BytesRcv == 0) {
|
||||||
debug("(TCP) Connection closing...");
|
trace("(TCP) Connection closing...");
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (BytesRcv < 0) {
|
} else if (BytesRcv < 0) {
|
||||||
#ifdef WIN32
|
debug("(TCP) recv() failed: " + GetPlatformAgnosticErrorString());
|
||||||
debug(("(TCP) recv failed with error: ") + std::to_string(WSAGetLastError()));
|
|
||||||
#else // unix
|
|
||||||
debug(("(TCP) recv failed with error: ") + std::string(strerror(errno)));
|
|
||||||
#endif // WIN32
|
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
info(("Closing socket in CheckBytes, BytesRcv < 0"));
|
|
||||||
CloseSocketProper(c.GetTCPSock());
|
CloseSocketProper(c.GetTCPSock());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -425,22 +401,13 @@ std::string TNetwork::TCPRcv(TClient& c) {
|
|||||||
do {
|
do {
|
||||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
|
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
|
||||||
if (!CheckBytes(c, Temp)) {
|
if (!CheckBytes(c, Temp)) {
|
||||||
#ifdef DEBUG
|
|
||||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < 4)"));
|
|
||||||
#endif // DEBUG
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
BytesRcv += Temp;
|
BytesRcv += Temp;
|
||||||
} while (size_t(BytesRcv) < sizeof(Header));
|
} while (size_t(BytesRcv) < sizeof(Header));
|
||||||
memcpy(&Header, &Data[0], sizeof(Header));
|
memcpy(&Header, &Data[0], sizeof(Header));
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
//debug(std::string(__func__) + (": expecting ") + std::to_string(Header) + (" bytes."));
|
|
||||||
#endif // DEBUG
|
|
||||||
if (!CheckBytes(c, BytesRcv)) {
|
if (!CheckBytes(c, BytesRcv)) {
|
||||||
#ifdef DEBUG
|
|
||||||
error(std::string(__func__) + (": failed on CheckBytes"));
|
|
||||||
#endif // DEBUG
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (Header < 100 * MB) {
|
if (Header < 100 * MB) {
|
||||||
@@ -454,29 +421,15 @@ std::string TNetwork::TCPRcv(TClient& c) {
|
|||||||
do {
|
do {
|
||||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
|
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
|
||||||
if (!CheckBytes(c, Temp)) {
|
if (!CheckBytes(c, Temp)) {
|
||||||
#ifdef DEBUG
|
|
||||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < Header)"));
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
|
||||||
//debug(std::string(__func__) + (": Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
|
||||||
#endif // DEBUG
|
|
||||||
BytesRcv += Temp;
|
BytesRcv += Temp;
|
||||||
} while (BytesRcv < Header);
|
} while (BytesRcv < Header);
|
||||||
#ifdef DEBUG
|
|
||||||
//debug(std::string(__func__) + (": finished recv with Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
|
||||||
#endif // DEBUG
|
|
||||||
std::string Ret(Data.data(), Header);
|
std::string Ret(Data.data(), Header);
|
||||||
|
|
||||||
if (Ret.substr(0, 4) == "ABG:") {
|
if (Ret.substr(0, 4) == "ABG:") {
|
||||||
Ret = DeComp(Ret.substr(4));
|
Ret = DeComp(Ret.substr(4));
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
|
||||||
//debug("Parsing from " + c->GetName() + " -> " +std::to_string(Ret.size()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return Ret;
|
return Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,10 +439,15 @@ void TNetwork::ClientKick(TClient& c, const std::string& R) {
|
|||||||
// TODO handle
|
// TODO handle
|
||||||
}
|
}
|
||||||
c.SetStatus(-2);
|
c.SetStatus(-2);
|
||||||
CloseSocketProper(c.GetTCPSock());
|
|
||||||
|
if (c.GetTCPSock())
|
||||||
|
CloseSocketProper(c.GetTCPSock());
|
||||||
|
|
||||||
|
if (c.GetDownSock())
|
||||||
|
CloseSocketProper(c.GetDownSock());
|
||||||
}
|
}
|
||||||
void TNetwork::Looper(const std::weak_ptr<TClient>& c){
|
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
||||||
while(!c.expired()) {
|
while (!c.expired()) {
|
||||||
auto Client = c.lock();
|
auto Client = c.lock();
|
||||||
if (Client->GetStatus() < 0) {
|
if (Client->GetStatus() < 0) {
|
||||||
debug("client status < 0, breaking client loop");
|
debug("client status < 0, breaking client loop");
|
||||||
@@ -521,7 +479,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +493,7 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
|||||||
OnConnect(c);
|
OnConnect(c);
|
||||||
RegisterThread("(" + std::to_string(c.lock()->GetID()) + ") \"" + c.lock()->GetName() + "\"");
|
RegisterThread("(" + std::to_string(c.lock()->GetID()) + ") \"" + c.lock()->GetName() + "\"");
|
||||||
|
|
||||||
std::thread QueueSync(&TNetwork::Looper, this ,c);
|
std::thread QueueSync(&TNetwork::Looper, this, c);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (c.expired())
|
if (c.expired())
|
||||||
@@ -553,7 +511,8 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
|||||||
}
|
}
|
||||||
TServer::GlobalParser(c, res, mPPSMonitor, *this);
|
TServer::GlobalParser(c, res, mPPSMonitor, *this);
|
||||||
}
|
}
|
||||||
if(QueueSync.joinable())QueueSync.join();
|
if (QueueSync.joinable())
|
||||||
|
QueueSync.join();
|
||||||
|
|
||||||
if (!c.expired()) {
|
if (!c.expired()) {
|
||||||
auto Client = c.lock();
|
auto Client = c.lock();
|
||||||
@@ -574,7 +533,8 @@ void TNetwork::UpdatePlayer(TClient& Client) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
Packet = Packet.substr(0, Packet.length() - 1);
|
Packet = Packet.substr(0, Packet.length() - 1);
|
||||||
(void)Respond(Client, Packet, true);
|
Client.EnqueuePacket(Packet);
|
||||||
|
//(void)Respond(Client, Packet, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked) {
|
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked) {
|
||||||
@@ -586,7 +546,7 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked
|
|||||||
TClient::TSetOfVehicleData VehicleData;
|
TClient::TSetOfVehicleData VehicleData;
|
||||||
{ // Vehicle Data Lock Scope
|
{ // Vehicle Data Lock Scope
|
||||||
auto LockedData = c.GetAllCars();
|
auto LockedData = c.GetAllCars();
|
||||||
VehicleData = LockedData.VehicleData;
|
VehicleData = *LockedData.VehicleData;
|
||||||
} // End Vehicle Data Lock Scope
|
} // End Vehicle Data Lock Scope
|
||||||
for (auto& v : VehicleData) {
|
for (auto& v : VehicleData) {
|
||||||
Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v.ID());
|
Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v.ID());
|
||||||
@@ -689,19 +649,29 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
||||||
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/')));
|
info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
|
||||||
|
|
||||||
if (!std::filesystem::exists(Name)) {
|
if (!fs::path(UnsafeName).has_filename()) {
|
||||||
if (!TCPSend(c, "CO")) {
|
if (!TCPSend(c, "CO")) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
warn("File " + Name + " could not be accessed!");
|
warn("File " + UnsafeName + " is not a file!");
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
if (!TCPSend(c, "AG")) {
|
auto FileName = fs::path(UnsafeName).filename().string();
|
||||||
|
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(FileName)) {
|
||||||
|
if (!TCPSend(c, "CO")) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
warn("File " + UnsafeName + " could not be accessed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TCPSend(c, "AG")) {
|
||||||
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
///Wait for connections
|
///Wait for connections
|
||||||
@@ -718,14 +688,14 @@ void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Size = size_t(std::filesystem::file_size(Name)), MSize = Size / 2;
|
size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2;
|
||||||
|
|
||||||
std::thread SplitThreads[2] {
|
std::thread SplitThreads[2] {
|
||||||
std::thread([&] {
|
std::thread([&] {
|
||||||
SplitLoad(c, 0, MSize, false, Name);
|
SplitLoad(c, 0, MSize, false, FileName);
|
||||||
}),
|
}),
|
||||||
std::thread([&] {
|
std::thread([&] {
|
||||||
SplitLoad(c, MSize, Size, true, Name);
|
SplitLoad(c, MSize, Size, true, FileName);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -755,7 +725,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
if (Diff > Split) {
|
if (Diff > Split) {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(Data, Split);
|
f.read(Data, Split);
|
||||||
if (!TCPSendRaw(TCPSock, Data, Split)) {
|
if (!TCPSendRaw(c, TCPSock, Data, Split)) {
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
break;
|
break;
|
||||||
@@ -764,7 +734,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
} else {
|
} else {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(Data, Diff);
|
f.read(Data, Diff);
|
||||||
if (!TCPSendRaw(TCPSock, 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;
|
||||||
@@ -776,16 +746,17 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
f.close();
|
f.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::TCPSendRaw(SOCKET C, char* Data, int32_t Size) {
|
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
||||||
intmax_t Sent = 0;
|
intmax_t Sent = 0;
|
||||||
do {
|
do {
|
||||||
intmax_t Temp = send(C, &Data[Sent], int(Size - Sent), 0);
|
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
|
||||||
if (Temp < 1) {
|
if (Temp < 1) {
|
||||||
info("Socket Closed! " + std::to_string(C));
|
info("Socket Closed! " + std::to_string(socket));
|
||||||
CloseSocketProper(C);
|
CloseSocketProper(socket);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Sent += Temp;
|
Sent += Temp;
|
||||||
|
C.UpdatePingTime();
|
||||||
} while (Sent < Size);
|
} while (Sent < Size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -836,12 +807,13 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
ReadLock Lock(mServer.GetClientMutex());
|
ReadLock Lock(mServer.GetClientMutex());
|
||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
client = ClientPtr.lock();
|
client = ClientPtr.lock();
|
||||||
} else return true;
|
} else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
TClient::TSetOfVehicleData VehicleData;
|
TClient::TSetOfVehicleData VehicleData;
|
||||||
{ // Vehicle Data Lock Scope
|
{ // Vehicle Data Lock Scope
|
||||||
auto LockedData = client->GetAllCars();
|
auto LockedData = client->GetAllCars();
|
||||||
VehicleData = LockedData.VehicleData;
|
VehicleData = *LockedData.VehicleData;
|
||||||
} // End Vehicle Data Lock Scope
|
} // End Vehicle Data Lock Scope
|
||||||
if (client != LockedClient) {
|
if (client != LockedClient) {
|
||||||
for (auto& v : VehicleData) {
|
for (auto& v : VehicleData) {
|
||||||
@@ -851,7 +823,6 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res = Respond(*LockedClient, v.Data(), true, true);
|
res = Respond(*LockedClient, v.Data(), true, true);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +848,8 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re
|
|||||||
ReadLock Lock(mServer.GetClientMutex());
|
ReadLock Lock(mServer.GetClientMutex());
|
||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
Client = ClientPtr.lock();
|
Client = ClientPtr.lock();
|
||||||
}else return true;
|
} else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (Self || Client.get() != c) {
|
if (Self || Client.get() != c) {
|
||||||
if (Client->IsSynced() || Client->IsSyncing()) {
|
if (Client->IsSynced() || Client->IsSyncing()) {
|
||||||
@@ -886,7 +858,7 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re
|
|||||||
if (Data.length() > 400) {
|
if (Data.length() > 400) {
|
||||||
std::string CMP(Comp(Data));
|
std::string CMP(Comp(Data));
|
||||||
Client->EnqueuePacket("ABG:" + CMP);
|
Client->EnqueuePacket("ABG:" + CMP);
|
||||||
}else{
|
} else {
|
||||||
Client->EnqueuePacket(Data);
|
Client->EnqueuePacket(Data);
|
||||||
}
|
}
|
||||||
//ret = SendLarge(*Client, Data);
|
//ret = SendLarge(*Client, Data);
|
||||||
@@ -930,31 +902,17 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const {
|
|||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize));
|
sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize));
|
||||||
#ifdef WIN32
|
|
||||||
if (sendOk == -1) {
|
if (sendOk == -1) {
|
||||||
debug(("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
|
debug("(UDP) sendto() failed: " + GetPlatformAgnosticErrorString());
|
||||||
if (Client.GetStatus() > -1)
|
if (Client.GetStatus() > -1)
|
||||||
Client.SetStatus(-1);
|
Client.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
} else if (sendOk == 0) {
|
} else if (sendOk == 0) {
|
||||||
debug(("(UDP) sendto returned 0"));
|
debug(("(UDP) sendto() returned 0"));
|
||||||
if (Client.GetStatus() > -1)
|
if (Client.GetStatus() > -1)
|
||||||
Client.SetStatus(-1);
|
Client.SetStatus(-1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else // unix
|
|
||||||
if (sendOk == -1) {
|
|
||||||
debug(("(UDP) Send Failed Code : ") + std::string(strerror(errno)));
|
|
||||||
if (Client.GetStatus() > -1)
|
|
||||||
Client.SetStatus(-1);
|
|
||||||
return false;
|
|
||||||
} else if (sendOk == 0) {
|
|
||||||
debug(("(UDP) sendto returned 0"));
|
|
||||||
if (Client.GetStatus() > -1)
|
|
||||||
Client.SetStatus(-1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // WIN32
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,11 +926,7 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const {
|
|||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
|
||||||
if (Rcv == -1) {
|
if (Rcv == -1) {
|
||||||
#ifdef WIN32
|
error("(UDP) Error receiving from client! recvfrom() failed: " + GetPlatformAgnosticErrorString());
|
||||||
error(("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
|
|
||||||
#else // unix
|
|
||||||
error(("(UDP) Error receiving from Client! Code : ") + std::string(strerror(errno)));
|
|
||||||
#endif // WIN32
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return std::string(Ret.begin(), Ret.begin() + Rcv);
|
return std::string(Ret.begin(), Ret.begin() + Rcv);
|
||||||
|
|||||||
+4
-6
@@ -36,17 +36,15 @@ void TPPSMonitor::operator()() {
|
|||||||
ReadLock Lock(mServer.GetClientMutex());
|
ReadLock Lock(mServer.GetClientMutex());
|
||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
c = ClientPtr.lock();
|
c = ClientPtr.lock();
|
||||||
} else return true;
|
} else
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (c->GetCarCount() > 0) {
|
if (c->GetCarCount() > 0) {
|
||||||
C++;
|
C++;
|
||||||
V += c->GetCarCount();
|
V += c->GetCarCount();
|
||||||
}
|
}
|
||||||
if (!c->IsSynced() || c->IsSyncing()) {
|
|
||||||
c->UpdatePingTime();
|
|
||||||
}
|
|
||||||
// kick on "no ping"
|
// kick on "no ping"
|
||||||
if (c->SecondsSinceLastPing() > 60 && c->IsSynced() && !c->IsSyncing()) {
|
if (c->SecondsSinceLastPing() > (20 * 60)) {
|
||||||
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
|
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
|
||||||
TimedOutClients.push_back(c);
|
TimedOutClients.push_back(c);
|
||||||
}
|
}
|
||||||
@@ -54,7 +52,7 @@ void TPPSMonitor::operator()() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
for (auto& ClientToKick : TimedOutClients) {
|
for (auto& ClientToKick : TimedOutClients) {
|
||||||
Network().ClientKick(*ClientToKick, "Timeout (no ping for >60 seconds)");
|
Network().ClientKick(*ClientToKick, "Timeout (no ping for way too long)");
|
||||||
}
|
}
|
||||||
TimedOutClients.clear();
|
TimedOutClients.clear();
|
||||||
if (C == 0 || mInternalPPS == 0) {
|
if (C == 0 || mInternalPPS == 0) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ TResourceManager::TResourceManager() {
|
|||||||
++i;
|
++i;
|
||||||
File = File.substr(i,pos-i);
|
File = File.substr(i,pos-i);
|
||||||
}
|
}
|
||||||
mTrimmedList += File + ';';
|
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
|
||||||
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
|
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
|
||||||
mMaxModSize += size_t(fs::file_size(entry.path()));
|
mMaxModSize += size_t(fs::file_size(entry.path()));
|
||||||
mModsLoaded++;
|
mModsLoaded++;
|
||||||
|
|||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
#include "TSentry.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include <sentry.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// compile-time length of a string/array
|
||||||
|
template <size_t N>
|
||||||
|
constexpr size_t ConstexprLength(char const (&)[N]) {
|
||||||
|
return N - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSentry::TSentry() {
|
||||||
|
if constexpr (ConstexprLength(SECRET_SENTRY_URL) == 0) {
|
||||||
|
mValid = false;
|
||||||
|
} else {
|
||||||
|
mValid = true;
|
||||||
|
sentry_options_t* options = sentry_options_new();
|
||||||
|
sentry_options_set_dsn(options, SECRET_SENTRY_URL);
|
||||||
|
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersionString();
|
||||||
|
sentry_options_set_symbolize_stacktraces(options, true);
|
||||||
|
sentry_options_set_release(options, ReleaseString.c_str());
|
||||||
|
sentry_options_set_max_breadcrumbs(options, 10);
|
||||||
|
sentry_init(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TSentry::~TSentry() {
|
||||||
|
if (mValid) {
|
||||||
|
sentry_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::PrintWelcome() {
|
||||||
|
if (mValid) {
|
||||||
|
info("Sentry started");
|
||||||
|
} else {
|
||||||
|
info("Sentry disabled in unofficial build");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::SetupUser() {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sentry_value_t user = sentry_value_new_object();
|
||||||
|
if (Application::Settings.Key.size() == 36) {
|
||||||
|
sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str()));
|
||||||
|
} else {
|
||||||
|
sentry_value_set_by_key(user, "id", sentry_value_new_string("unauthenticated"));
|
||||||
|
}
|
||||||
|
sentry_set_user(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::Log(SentryLevel level, const std::string& logger, const std::string& text) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetContext("threads", { { "thread-name", ThreadName(true) } });
|
||||||
|
auto Msg = sentry_value_new_message_event(sentry_level_t(level), logger.c_str(), text.c_str());
|
||||||
|
sentry_capture_event(Msg);
|
||||||
|
sentry_remove_transaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::LogError(const std::string& text, const std::string& file, const std::string& line) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetTransaction(file + ":" + line);
|
||||||
|
Log(SentryLevel::Error, "default", file + ": " + text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto ctx = sentry_value_new_object();
|
||||||
|
for (const auto& pair : map) {
|
||||||
|
std::string key = pair.first;
|
||||||
|
if (key == "type") {
|
||||||
|
// `type` is reserved
|
||||||
|
key = "_type";
|
||||||
|
}
|
||||||
|
sentry_value_set_by_key(ctx, key.c_str(), sentry_value_new_string(pair.second.c_str()));
|
||||||
|
}
|
||||||
|
sentry_set_context(context_name.c_str(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::LogException(const std::exception& e, const std::string& file, const std::string& line) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetTransaction(file + ":" + line);
|
||||||
|
Log(SentryLevel::Fatal, "exceptions", std::string(e.what()) + " @ " + file + ":" + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetTransaction(file + ":" + line + ":" + function);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "\"" << condition_string << "\" failed @ " << file << ":" << line;
|
||||||
|
Log(SentryLevel::Fatal, "asserts", ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto crumb = sentry_value_new_breadcrumb("default", (msg + " @ " + file + ":" + line).c_str());
|
||||||
|
sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error"));
|
||||||
|
sentry_add_breadcrumb(crumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSentry::SetTransaction(const std::string& id) {
|
||||||
|
if (!mValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sentry_set_transaction(id.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> TSentry::CreateExclusiveContext() {
|
||||||
|
return std::unique_lock<std::mutex>(mMutex);
|
||||||
|
}
|
||||||
+89
-45
@@ -7,16 +7,14 @@
|
|||||||
#include <any>
|
#include <any>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#undef GetObject //Fixes Windows
|
#undef GetObject // Fixes Windows
|
||||||
|
|
||||||
#include "rapidjson/document.h"
|
#include "Json.h"
|
||||||
#include "rapidjson/stringbuffer.h"
|
|
||||||
#include "rapidjson/writer.h"
|
|
||||||
|
|
||||||
namespace json = rapidjson;
|
namespace json = rapidjson;
|
||||||
|
|
||||||
TServer::TServer(int argc, char** argv) {
|
TServer::TServer(int argc, char** argv) {
|
||||||
info("BeamMP Server v" + Application::ServerVersion());
|
info("BeamMP Server v" + Application::ServerVersionString());
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
Application::Settings.CustomIP = argv[1];
|
Application::Settings.CustomIP = argv[1];
|
||||||
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
||||||
@@ -67,7 +65,7 @@ size_t TServer::ClientCount() const {
|
|||||||
|
|
||||||
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
|
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
|
||||||
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
||||||
abort();
|
//abort();
|
||||||
}
|
}
|
||||||
if (Packet.substr(0, 4) == "ABG:") {
|
if (Packet.substr(0, 4) == "ABG:") {
|
||||||
Packet = DeComp(Packet.substr(4));
|
Packet = DeComp(Packet.substr(4));
|
||||||
@@ -92,9 +90,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
}
|
}
|
||||||
switch (Code) {
|
switch (Code) {
|
||||||
case 'H': // initial connection
|
case 'H': // initial connection
|
||||||
#ifdef DEBUG
|
trace(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
||||||
debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
|
||||||
#endif
|
|
||||||
if (!Network.SyncClient(Client)) {
|
if (!Network.SyncClient(Client)) {
|
||||||
// TODO handle
|
// TODO handle
|
||||||
}
|
}
|
||||||
@@ -108,7 +104,6 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
} else {
|
} else {
|
||||||
Network.UpdatePlayer(*LockedClient);
|
Network.UpdatePlayer(*LockedClient);
|
||||||
}
|
}
|
||||||
LockedClient->UpdatePingTime();
|
|
||||||
return;
|
return;
|
||||||
case 'O':
|
case 'O':
|
||||||
if (Packet.length() > 1000) {
|
if (Packet.length() > 1000) {
|
||||||
@@ -117,28 +112,27 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
ParseVehicle(*LockedClient, Packet, Network);
|
ParseVehicle(*LockedClient, Packet, Network);
|
||||||
return;
|
return;
|
||||||
case 'J':
|
case 'J':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||||
return;
|
return;
|
||||||
case 'C':
|
case 'C':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
||||||
break;
|
break;
|
||||||
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
||||||
|
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); // FIXME: this needs to be adjusted once lua is merged
|
||||||
if (std::any_cast<int>(Res))
|
if (std::any_cast<int>(Res))
|
||||||
break;
|
break;
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, Packet, true, true);
|
||||||
return;
|
return;
|
||||||
case 'E':
|
case 'E':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
HandleEvent(*LockedClient, Packet);
|
HandleEvent(*LockedClient, Packet);
|
||||||
return;
|
return;
|
||||||
|
case 'N':
|
||||||
|
trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
||||||
|
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -164,6 +158,29 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
|
|||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
|
||||||
|
rapidjson::Document Car;
|
||||||
|
Car.Parse(CarJson.c_str(), CarJson.size());
|
||||||
|
if (Car.HasParseError()) {
|
||||||
|
error("Failed to parse vehicle data -> " + CarJson);
|
||||||
|
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
||||||
|
|
||||||
|
if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUnicycle(c, CarJson)) {
|
||||||
|
c.SetUnicycleID(ID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Application::Settings.MaxCars > c.GetCarCount();
|
||||||
|
}
|
||||||
|
|
||||||
void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) {
|
void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) {
|
||||||
if (Pckt.length() < 4)
|
if (Pckt.length() < 4)
|
||||||
@@ -175,15 +192,19 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
std::string Data = Packet.substr(3), pid, vid;
|
std::string Data = Packet.substr(3), pid, vid;
|
||||||
switch (Code) { //Spawned Destroyed Switched/Moved NotFound Reset
|
switch (Code) { //Spawned Destroyed Switched/Moved NotFound Reset
|
||||||
case 's':
|
case 's':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
if (Data.at(0) == '0') {
|
if (Data.at(0) == '0') {
|
||||||
int CarID = c.GetOpenCarID();
|
int CarID = c.GetOpenCarID();
|
||||||
debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID));
|
debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID));
|
||||||
Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + Packet.substr(4);
|
|
||||||
|
std::string CarJson = Packet.substr(5);
|
||||||
|
Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + ":" + CarJson;
|
||||||
auto Res = TriggerLuaEvent(("onVehicleSpawn"), false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), CarID, Packet.substr(3) } }), true);
|
auto Res = TriggerLuaEvent(("onVehicleSpawn"), false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), CarID, Packet.substr(3) } }), true);
|
||||||
if (c.GetCarCount() >= Application::Settings.MaxCars || std::any_cast<int>(Res)) {
|
|
||||||
|
if (ShouldSpawn(c, CarJson, CarID) && std::any_cast<int>(Res) == 0) {
|
||||||
|
c.AddNewCar(CarID, Packet);
|
||||||
|
Network.SendToAll(nullptr, Packet, true, true);
|
||||||
|
} else {
|
||||||
if (!Network.Respond(c, Packet, true)) {
|
if (!Network.Respond(c, Packet, true)) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
@@ -192,16 +213,11 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
debug(c.GetName() + (" (force : car limit/lua) removed ID ") + std::to_string(CarID));
|
debug(c.GetName() + (" (force : car limit/lua) removed ID ") + std::to_string(CarID));
|
||||||
} else {
|
|
||||||
c.AddNewCar(CarID, Packet);
|
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'c':
|
case 'c':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
pid = Data.substr(0, Data.find('-'));
|
pid = Data.substr(0, Data.find('-'));
|
||||||
vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1);
|
vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1);
|
||||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||||
@@ -212,10 +228,17 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
auto Res = TriggerLuaEvent(("onVehicleEdited"), false, nullptr,
|
auto Res = TriggerLuaEvent(("onVehicleEdited"), false, nullptr,
|
||||||
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
|
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
|
||||||
true);
|
true);
|
||||||
if (!std::any_cast<int>(Res)) {
|
|
||||||
|
auto FoundPos = Packet.find('{');
|
||||||
|
FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this
|
||||||
|
if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos)))
|
||||||
|
&& std::any_cast<int>(Res) == 0) {
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, Packet, false, true);
|
||||||
Apply(c, VID, Packet);
|
Apply(c, VID, Packet);
|
||||||
} else {
|
} else {
|
||||||
|
if (c.GetUnicycleID() == VID) {
|
||||||
|
c.SetUnicycleID(-1);
|
||||||
|
}
|
||||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
||||||
if (!Network.Respond(c, Destroy, true)) {
|
if (!Network.Respond(c, Destroy, true)) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
@@ -225,9 +248,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'd':
|
case 'd':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
pid = Data.substr(0, Data.find('-'));
|
pid = Data.substr(0, Data.find('-'));
|
||||||
vid = Data.substr(Data.find('-') + 1);
|
vid = Data.substr(Data.find('-') + 1);
|
||||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||||
@@ -235,6 +256,9 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
VID = stoi(vid);
|
VID = stoi(vid);
|
||||||
}
|
}
|
||||||
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
||||||
|
if (c.GetUnicycleID() == VID) {
|
||||||
|
c.SetUnicycleID(-1);
|
||||||
|
}
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, Packet, true, true);
|
||||||
TriggerLuaEvent(("onVehicleDeleted"), false, nullptr,
|
TriggerLuaEvent(("onVehicleDeleted"), false, nullptr,
|
||||||
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID } }), false);
|
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID } }), false);
|
||||||
@@ -243,9 +267,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'r':
|
case 'r':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
Pos = int(Data.find('-'));
|
Pos = int(Data.find('-'));
|
||||||
pid = Data.substr(0, Pos++);
|
pid = Data.substr(0, Pos++);
|
||||||
vid = Data.substr(Pos, Data.find(':') - Pos);
|
vid = Data.substr(Pos, Data.find(':') - Pos);
|
||||||
@@ -264,23 +286,45 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 't':
|
case 't':
|
||||||
#ifdef DEBUG
|
trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
debug(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
#endif
|
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, Packet, false, true);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
#ifdef DEBUG
|
trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||||
warn(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
|
||||||
#endif // DEBUG
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
||||||
std::string Packet = pckt.substr(pckt.find('{')), VD = c.GetCarData(VID);
|
auto FoundPos = pckt.find('{');
|
||||||
|
if (FoundPos == std::string::npos) {
|
||||||
|
error("Malformed packet received, no '{' found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string Packet = pckt.substr(FoundPos);
|
||||||
|
std::string VD = c.GetCarData(VID);
|
||||||
|
if (VD.empty()) {
|
||||||
|
error("Tried to apply change to vehicle that does not exist");
|
||||||
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
|
Sentry.SetContext("vehicle-change",
|
||||||
|
{ { "packet", Packet },
|
||||||
|
{ "vehicle-id", std::to_string(VID) },
|
||||||
|
{ "client-car-count", std::to_string(c.GetCarCount()) } });
|
||||||
|
Sentry.LogError("attempt to apply change to nonexistent vehicle", _file_basename, _line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::string Header = VD.substr(0, VD.find('{'));
|
std::string Header = VD.substr(0, VD.find('{'));
|
||||||
VD = VD.substr(VD.find('{'));
|
|
||||||
|
FoundPos = VD.find('{');
|
||||||
|
if (FoundPos == std::string::npos) {
|
||||||
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
|
Sentry.SetContext("vehicle-change-packet",
|
||||||
|
{ { "packet", VD } });
|
||||||
|
Sentry.LogError("malformed packet", _file_basename, _line);
|
||||||
|
error("Malformed packet received, no '{' found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VD = VD.substr(FoundPos);
|
||||||
rapidjson::Document Veh, Pack;
|
rapidjson::Document Veh, Pack;
|
||||||
Veh.Parse(VD.c_str());
|
Veh.Parse(VD.c_str());
|
||||||
if (Veh.HasParseError()) {
|
if (Veh.HasParseError()) {
|
||||||
|
|||||||
+3
-7
@@ -1,18 +1,14 @@
|
|||||||
#include "VehicleData.h"
|
#include "VehicleData.h"
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
TVehicleData::TVehicleData(int ID, std::string Data)
|
TVehicleData::TVehicleData(int ID, std::string Data)
|
||||||
: mID(ID)
|
: mID(ID)
|
||||||
, mData(std::move(Data)) {
|
, mData(std::move(Data)) {
|
||||||
#ifdef DEBUG
|
trace("vehicle " + std::to_string(mID) + " constructed");
|
||||||
debug("vehicle " + std::to_string(mID) + " constructed");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TVehicleData::~TVehicleData() {
|
TVehicleData::~TVehicleData() {
|
||||||
#ifdef DEBUG
|
trace("vehicle " + std::to_string(mID) + " destroyed");
|
||||||
debug("vehicle " + std::to_string(mID) + " destroyed");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-6
@@ -1,4 +1,7 @@
|
|||||||
|
#include "TSentry.h"
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "CustomAssert.h"
|
||||||
#include "Http.h"
|
#include "Http.h"
|
||||||
#include "TConfig.h"
|
#include "TConfig.h"
|
||||||
#include "THeartbeatThread.h"
|
#include "THeartbeatThread.h"
|
||||||
@@ -7,6 +10,7 @@
|
|||||||
#include "TPPSMonitor.h"
|
#include "TPPSMonitor.h"
|
||||||
#include "TResourceManager.h"
|
#include "TResourceManager.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#ifdef __unix
|
#ifdef __unix
|
||||||
@@ -32,26 +36,43 @@ void UnixSignalHandler(int sig) {
|
|||||||
}
|
}
|
||||||
#endif // __unix
|
#endif // __unix
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
// this is provided by the build system, leave empty for source builds
|
||||||
|
// global, yes, this is ugly, no, it cant be done another way
|
||||||
|
TSentry Sentry {};
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) try {
|
||||||
#ifdef __unix
|
#ifdef __unix
|
||||||
#if DEBUG
|
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
|
||||||
info("registering handlers for SIGINT, SIGTERM, SIGPIPE");
|
|
||||||
#endif // DEBUG
|
|
||||||
signal(SIGPIPE, UnixSignalHandler);
|
signal(SIGPIPE, UnixSignalHandler);
|
||||||
signal(SIGTERM, UnixSignalHandler);
|
signal(SIGTERM, UnixSignalHandler);
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
signal(SIGINT, UnixSignalHandler);
|
signal(SIGINT, UnixSignalHandler);
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
#endif // __unix
|
#endif // __unix
|
||||||
|
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
|
|
||||||
bool Shutdown = false;
|
bool Shutdown = false;
|
||||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||||
|
Application::RegisterShutdownHandler([] { TriggerLuaEvent("onShutdown", false, nullptr, {}, true); });
|
||||||
|
|
||||||
TServer Server(argc, argv);
|
TServer Server(argc, argv);
|
||||||
[[maybe_unused]] TConfig Config("Server.cfg");
|
TConfig Config;
|
||||||
|
|
||||||
|
if (Config.Failed()) {
|
||||||
|
info("Closing in 10 seconds");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
RegisterThread("Main");
|
RegisterThread("Main");
|
||||||
|
|
||||||
|
trace("Running in debug mode on a debug build");
|
||||||
|
|
||||||
|
Sentry.SetupUser();
|
||||||
|
Sentry.PrintWelcome();
|
||||||
|
Application::CheckForUpdates();
|
||||||
TResourceManager ResourceManager;
|
TResourceManager ResourceManager;
|
||||||
TPPSMonitor PPSMonitor(Server);
|
TPPSMonitor PPSMonitor(Server);
|
||||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||||
@@ -64,4 +85,7 @@ int main(int argc, char** argv) {
|
|||||||
while (!Shutdown) {
|
while (!Shutdown) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
}
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error(e.what());
|
||||||
|
Sentry.LogException(e, _file_basename, _line);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user