Compare commits

..

73 Commits

Author SHA1 Message Date
Lion 6814cb6f4a add section about adding features 2022-08-08 19:53:02 +03:00
Lion Kortlepel 00b5db1153 add CONTRIBUTING.md 2022-08-08 19:29:17 +03:00
Lion a28b3e1532 Merge pull request #113 from Mack29446/master
Update README.md
2022-07-10 12:55:23 +02:00
Mackenzie b01bed2996 change dependency section 2022-07-10 11:51:19 +01:00
Mackenzie 7bdc2b304b refactor: master is considered unstable 2022-07-10 11:47:55 +01:00
Mackenzie b19bca4c82 fix numbering 2022-07-10 11:38:51 +01:00
Mackenzie 708848f275 Update README.md 2022-07-10 11:35:44 +01:00
Lion 05251efc06 Merge pull request #98 from BeamMP/rc-v3.0.2
RC v3.0.2
2022-07-09 22:55:16 +02:00
Lion Kortlepel f8d622352f generate toml from scratch 2022-07-09 22:42:38 +02:00
Lion Kortlepel 6c1d02a425 add fclose 2022-07-09 22:29:12 +02:00
Lion Kortlepel 38eeec39b4 another attempt to fix #105 2022-07-09 22:27:05 +02:00
Lion Kortlepel 696e080e1c fix #105 2022-07-09 22:01:53 +02:00
Lion Kortlepel 98681254e6 update toml11 2022-07-09 21:50:23 +02:00
Lion Kortlepel 420e6c3533 roll back to an ancient version of sentry
sentry-native deprecated compiling without error somewhere around one of
the next version
2022-07-09 21:40:19 +02:00
Lion Kortlepel 06f8ba5a0e update sentry-native to 0.4.18 2022-07-09 21:11:34 +02:00
Lion 28270072d4 Merge pull request #102 from Mack29446/master
update version number in git checkout command
2022-05-30 19:16:10 +02:00
Lion Kortlepel 758d5b2c96 Revert "let's try vcpkg"
This reverts commit 8d7505956d.
2022-05-29 14:37:50 +02:00
Lion Kortlepel 1970d97ea4 Revert "Remove unneeded submodules"
This reverts commit a5153e4bc1.
2022-05-29 14:37:41 +02:00
Lion Kortlepel d8526f0649 TNetwork::SplitLoad: Use managed memory 2022-05-29 14:30:57 +02:00
Lion Kortlepel a5153e4bc1 Remove unneeded submodules 2022-04-28 16:28:23 +02:00
Lion Kortlepel 8d7505956d let's try vcpkg 2022-04-28 16:26:30 +02:00
Lion Kortlepel 3b2016d09f Windows moment
Windows deprecated when
2022-04-28 14:59:41 +02:00
Lion Kortlepel ca52d233c0 Use another git commit id for vcpkg
this should really be done properly, yikes
2022-04-28 14:34:05 +02:00
Lion Kortlepel 34b39aad4d add message to shutdown suggesting Ctrl+C if it takes too long
This is an ongoing issue that needs to be resolved properly,
but I'm not sure what's hanging it.
2022-04-28 14:30:44 +02:00
Lion Kortlepel f4eb492d91 Actions: try to use master as vcpkgGitCommitId 2022-04-28 14:24:02 +02:00
Lion Kortlepel 7f41a2a574 Update Changelog to reflect latest changes 2022-04-28 14:18:10 +02:00
Lion Kortlepel 11c53e0b3a Remove unused error, as Sentry's code fails to build otherwise
Thanks, sentry.
2022-04-28 14:13:51 +02:00
Lion Kortlepel 974dda9f8b HTTPServer: Add config value to specify listen IP
Change default IP to localhost,
Set default SSL to false due to this.
2022-04-28 14:12:26 +02:00
Lion Kortlepel 0979c8b1e4 HTTPServer: Attempt to catch more errors 2022-04-28 14:04:54 +02:00
Lion Kortlepel 0761036c8c TConsole::StartLoggingToFile: implement 2022-04-28 13:46:25 +02:00
Lion Kortlepel 5ded713b4b Application::CheckForUpdates: only print status the first time 2022-04-28 13:33:38 +02:00
Lion Kortlepel 668cc496b6 update commandline, unused are now errors 2022-04-28 13:29:16 +02:00
Lion Kortlepel 056d20292a Make "unable to fetch version" a trace message 2022-04-28 13:14:28 +02:00
Mackenzie 58e2383b2e update version number in git checkout command 2022-04-13 19:48:12 +01:00
Lion Kortlepel d8c33c03ee start work on new logger 2022-04-05 22:27:45 +02:00
Lion Kortlepel 1bab3276e9 fix setsockopt SO_SNDTIMEO for windows, bump version number to 3.0.2, update commandline 2022-04-05 10:59:16 +02:00
Lion Kortlepel 4ff69528bd fix some missing declaration 2022-03-31 23:56:02 +02:00
Lion Kortlepel 5e4c7eac51 add send timeout to client tcp socket 2022-03-31 23:53:10 +02:00
Lion Kortlepel d01d79a49a update changelog 2022-03-31 20:27:08 +02:00
Lion Kortlepel d4b30a2583 CreateEventTimer: Implement CallStrategy
There are two CallStrategies:

- BestEffort (default): Will try to get your event to trigger at the specified
  interval, but will refuse to queue handlers if a handler takes too
  long.
- Precise: Will enqueue event handlers at the exact interval specified.
  Can lead to the queue filling up if the handler takes longer than the
  interval.
2022-03-31 20:13:59 +02:00
Lion Kortlepel 81dbf747d5 Kick client if we fail to send them a client event 2022-03-31 16:50:00 +02:00
Lion Kortlepel b97397132d TLuaEngine: improve result queue handling 2022-03-31 15:59:31 +02:00
Lion Kortlepel de82caef33 Add HideUpdateMessages setting ("ImScaredOfUpdates") and periodic update reminders (every 5th heartbeat) 2022-03-25 13:34:28 +01:00
Lion Kortlepel f8c58f363a Change default MaxPlayers to 8 2022-03-25 13:32:41 +01:00
Lion Kortlepel 71c2d4b859 Simplify "Backend heartbeat response" error (closes #97) 2022-03-25 12:55:35 +01:00
Lion a97763a94f Merge pull request #91 from BeamMP/rc-v3.0.1
Release Candidate v3.0.1
2022-03-10 17:03:43 +01:00
Lion Kortlepel daa674f448 add infinite snowmen bug to changelog 2022-03-10 12:31:02 +01:00
Lion Kortlepel a85fef15c2 only use sentry if URL is specified, possibly fix stupid microsoft compiler error
hey @microsoft, maybe don't have a limit on the size of obj files.
2022-03-10 12:23:24 +01:00
Lion Kortlepel 359faee696 Fix Unicycle "infinite spawning" bug 2022-03-10 12:22:11 +01:00
Lion Kortlepel 299004b14e fix msvc /bigobj issue 2022-03-10 12:21:08 +01:00
Lion Kortlepel b09f5a401d update lionkor/commandline to 1.0.0 (adds cursor left- and right
movement)
2022-03-10 12:19:01 +01:00
Lion Kortlepel 8ce3be03a3 make bigobj a linker flag? 2022-02-17 12:00:03 +01:00
Lion Kortlepel 687b4e4235 add bigobj flag 2022-02-17 11:47:28 +01:00
Lion Kortlepel 86ad28abdc Microsoft (R) Windows (TM) needs deprecation 2022-02-17 11:29:51 +01:00
Lion Kortlepel a982d54202 add debug build to windows github actions 2022-02-17 11:13:07 +01:00
Lion Kortlepel 5553aca0bb set heartbeat status properly 2022-02-12 22:08:49 +01:00
Lion Kortlepel beaea4f624 Fix crash when giving commandline arguments with no console initialized 2022-02-11 11:57:01 +01:00
Lion a4f07c9a4d Merge pull request #86 from BeamMP/rc-v3.0.1
Release Candidate v3.0.1
2022-02-11 11:07:39 +01:00
Lion Kortlepel dada8fe6bf update changelog 2022-02-11 11:01:39 +01:00
Lion Kortlepel 36853ca683 add MSG_NOSIGNAL to all calls to send() to get rid of useless SIGPIPE signals 2022-02-11 10:59:17 +01:00
Lion Kortlepel d969c4a2c2 Use nlohmann/json for IsUnicycle 2022-02-11 10:36:10 +01:00
Lion Kortlepel f26ca6b40d add nlohmann/json
this will conflict with a change in the http branch, but i dont care
right now
2022-02-11 10:19:10 +01:00
Lion b72de4bd0a Merge pull request #82 from BeamMP/rc-v3.0.1
Release Candidate v3.0.1
2022-02-04 04:37:09 +01:00
Lion Kortlepel 9494bc70fb fix changelog 2022-02-03 19:46:31 +01:00
Lion Kortlepel 5c44a307bc update changelog 2022-02-03 19:03:21 +01:00
Lion Kortlepel 754053e73f Use yield() where possible
Replaced calls of this_thread::sleep_* with this_thread::yield(), which
yields the thread to the OS' scheduler.
2022-02-03 18:57:52 +01:00
Lion Kortlepel 29f8d29e33 Move commandline initialization after cwd setting
This fixes an issue where the log file is written to the original
directory, even if --working-directory=path was used. This can obviously
be pretty bad.
2022-02-03 18:31:18 +01:00
Lion Kortlepel e043361abb update libraries 2022-02-03 18:30:00 +01:00
Lion Kortlepel fca5bbcec9 UpdateCheck: Try all URLs 2022-01-21 14:26:42 +01:00
Lion Kortlepel 2a588954be advance to 3.0.1 2022-01-20 21:31:00 +01:00
Lion Kortlepel cd4129e05d add api-v header to heartbeat post 2022-01-20 16:09:08 +01:00
Lion Kortlepel c42c748b37 start fixing backend heartbeat 2022-01-20 15:46:13 +01:00
Lion 179b33a7ab Merge pull request #76 from BeamMP/patch-http-server-default
HTTP Server disabled by default
2021-12-23 14:28:33 +01:00
28 changed files with 571 additions and 239 deletions
+12 -1
View File
@@ -20,7 +20,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb' vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
@@ -44,4 +44,15 @@ jobs:
name: BeamMP-Server.exe name: BeamMP-Server.exe
path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe
- name: Build debug
working-directory: ${{github.workspace}}/build-windows
shell: bash
run: |
cmake --build . --config Debug
- name: Archive debug artifacts
uses: actions/upload-artifact@v2
with:
name: BeamMP-Server-debug.exe
path: ${{github.workspace}}/build-windows/Debug/BeamMP-Server.exe
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb' vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
+4 -1
View File
@@ -21,4 +21,7 @@
url = https://github.com/nih-at/libzip url = https://github.com/nih-at/libzip
[submodule "deps/cpp-httplib"] [submodule "deps/cpp-httplib"]
path = deps/cpp-httplib path = deps/cpp-httplib
url = https://github.com/yhirose/cpp-httplib url = https://github.com/yhirose/cpp-httplib
[submodule "deps/json"]
path = deps/json
url = https://github.com/nlohmann/json
+25 -16
View File
@@ -8,12 +8,15 @@ project(BeamMP-Server
HOMEPAGE_URL https://beammp.com HOMEPAGE_URL https://beammp.com
LANGUAGES CXX C) LANGUAGES CXX C)
set(HTTPLIB_REQUIRE_OPENSSL ON)
include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include") include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include")
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include")
include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp")
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include") include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include")
include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib") include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib")
include_directories("${PROJECT_SOURCE_DIR}/deps/json/single_include")
include_directories("${PROJECT_SOURCE_DIR}/deps") include_directories("${PROJECT_SOURCE_DIR}/deps")
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT) add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
@@ -34,13 +37,33 @@ if (WIN32)
endif() endif()
include_directories("include/sentry-native/include") include_directories("include/sentry-native/include")
set(SENTRY_BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
if (MSVC) if (MSVC)
set(SENTRY_BUILD_RUNTIMESTATIC ON) set(SENTRY_BUILD_RUNTIMESTATIC ON)
endif() endif()
set(SENTRY_BACKEND breakpad) 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 "")
set(SENTRY_BACKEND none)
else()
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
message(STATUS "Sentry URL is length ${URL_LEN}")
set(SENTRY_BACKEND breakpad)
endif()
add_subdirectory("deps/sentry-native") add_subdirectory("deps/sentry-native")
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
endif ()
message(STATUS "Adding local source dependencies")
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
add_subdirectory(deps)
message(STATUS "Setting compiler flags") message(STATUS "Setting compiler flags")
if (WIN32) if (WIN32)
@@ -58,21 +81,7 @@ elseif (UNIX)
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
add_subdirectory(deps)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
+111
View File
@@ -0,0 +1,111 @@
# Contributing to BeamMP-Server
Unlike other parts of BeamMP, the BeamMP-Server does not have any dependency to the BeamNG.drive game.
To contribute *C++ code*, you'll need a MacOS, Linux or Windows PC, and intermediate to advanced knowledge of C++.
For reference, you should know be reasonably comfortable with the STL, the concept of RAII, templates, and generally know how to read & write post-C++17 code. To contribute anything else, you won't need most of this (though it'd be helpful to have some vocabulary about computers).
# Ways to Contribute
## Bug Reports
If you work with BeamMP-Server, either by simply using it, or even writing plugins for it, and you run into any issues, we definitely want to know about it. Please use [GitHub issues](https://github.com/BeamMP/BeamMP-Server/issues) and select the "Bug" template, read it, and fill it out accordingly.
## Bug Fixes
If you are interested in fixing bugs, check out the [GitHub issues](https://github.com/BeamMP/BeamMP-Server/issues). There, you can pick any issue that has nobody assigned to it. For example, some bugs which we definitely need some help with are marked with the "help wanted" tag.
Once you picked a bug, you need to reproduce it. Start by following the instructions in the bug report, and don't be afraid to ask for more information or clarification on the issue itself.
Refer to [getting started with the codebase](#getting-started-with-the-codebase) for more information on how to build the server. You can also ask on our [Discord server](https://discord.gg/beammp), or on IRC ([irc.libera.chat](https://web.libera.chat/), join `#beammp`).
## Features
If you want to add new features, please make an issue for it first or ask on our [Discord server](https://discord.gg/beammp), or on IRC ([irc.libera.chat](https://web.libera.chat/), join `#beammp`).
You need to make sure the feature isn't being worked on by someone else, and aligns with the vision we have for the server.
# Git Guidelines
**Read this carefully. Failing to follow these rules results in your changes not being accepted**. This applies for outside contributors, members of the BeamMP development team ("BeamMP Developers"), project owners, maintainers, frequent contributors, and literally everyone else. **It applies to everyone**.
## How to Commit
Commit messages **MUST** (mandatory):
- start with a **lower case action verb in present tense**, for example `add`, `fix`, `implement`, `refactor`, `remove`, `rename`. *Counter examples (these are bad): ~~`Fixed`, `fixing`, `added`, `removing`~~*.
- not have a first line much longer than 70 characters.
- explain briefly the changes made.
- reference the issue by number, if there is an issue the commit addresses, like so: `#<number>`. Example: `#123`.
If any of these are not followed, **your changes will not be accepted.**
Commit messages **SHOULD** (optional, "nice to have"):
- only address one "atomic" change.
- have an empty second line, and the subsequent lines explaining the changes in more detail (if more detail is available).
Commits may be squashed (via a Git "interactive rebase") in order to satisfy these rules, but history that is >1h old should not be rewritten if possible. Force pushes are ugly ;)
## Pulling, Merging
Do **NOT** pull with merge. This is the default git behavior for `git pull`, but creates ugly and unnecessary commit messages like `"merge origin/master into master"`. Instead, pull with rebase, for example via `git pull -r`. If you get conflicts, resolve them properly.
The only acceptable merge commits are those which actually merge functionally different branches into each other, for example for merging one feature branch into another.
## Branches
### Which branch should I base my work on?
Each *feature* or *bug-fix* is implemented on a new Git branch, branched off of the branch it should be based on. The `master` branch is usually stable, so we don't do development on it. It is always a safe bet to branch off of `master`, but it may be more work to merge later. Branches to base your work on are usually branches like `rc-v3.3.0`, when the latest public version is `3.2.0`, for example. These can often be found in Pull-Requests on GitHub which are tagged `Release Candidate`.
## Unit tests & CI/CD
We use GitHub Actions, which runs our unit-tests. PR's which fail these tests, or even fail any of our actions (which run automatically), will not be merged and require further changes until they compile, link, and all tests pass properly. If you have issues with this, feel free to ask in our [Discord server](https://discord.gg/beammp), or on IRC ([irc.libera.chat](https://web.libera.chat/), join `#beammp`)
### What should I call by branch?
Keep branch names **unique**, **descriptive**, and **shorter than 30 characters**. Names must be in present-tense, such as `fix-xyz`, **not** ~~`fixing-xyz`~~.
We generally use *feature branches*, so we keep one branch per feature or fix.
For example:
- You want to fix issue number #123? You could call the branch `fix-123`.
- You want to add a feature described in issue #456? You could call the branch `implement-456`.
- You want to add a feature or fix a bug that has no issue? You should probably make an issue for it first! Or, if you're not ready for that yet, you could call it by the feature name or bug description, for example for a bug that makes cars disappear: `fix-disappearing-cars`.
## Pull Requests, Code Review
Once you are ready to show what you did, and get feedback on it, you open a Pull-Request on GitHub. Please make sure to pick the right branches, and a descriptive title. Mention any related issues with `#<issue number>`, for example `#123`.
Make sure to explain what the PR does, what it fixes, and what needs to still be done (if anything).
A BeamMP-Developer must review your code in detail, and leave a review. If this takes too long, feel free to @ a maintainer/developer, or leave another comment on the PR. It helps to say something like "Ready for review", for example.
# Getting Started with the Codebase
1. Look at current Pull-Requests, look at the git branches, or ask in our [Discord server](https://discord.gg/beammp), or on IRC ([irc.libera.chat](https://web.libera.chat/), join `#beammp`), in order to find out which branch you should work on to minimize conflict. See [this section on branches](#branches) for more info.
2. Fork the repository (with that branch) on GitHub. GitHub, by default, gives you only the `master` branch when forking, so make sure you fork with other branches enabled when you want to work on a branch that isn't master (it's a checkbox when you fork).
3. Clone the fork to your local machine.
4. Check out the branch you want to work on (see 1.).
5. Run `git submodule update --init --recursive`.
6. Make a new branch for your feature or fix from the branch you are on. You can do this via `git checkout -b <branch name>`. See [this section on branches](#branches) for more info on branch naming.
7. Install all dependencies. Those are usually listed in the `README.md` in the branch you're in, or, more reliably, in one of the files in `.github/workflows` (if you can read `yaml`).
8. Run CMake to configure the project. You can find tutorials on this online. You will want to tell CMake to build with `CMAKE_BUILD_TYPE=Debug`, for example by passing it to CMake via the commandline switch `-DCMAKE_BUILD_TYPE=Debug`. You may also want to turn off sentry by setting `SENTRY_BACKEND=none` (for example via commandline switch `-DSENTRY_BACKEND=none`). An example invocation on Linux with GNU make would be
`cmake . -DCMAKE_BUILD_TYPE=Debug -DSENTRY_BACKEND=none` .
9. Build the `BeamMP-Server` target to build the BeamMP-Server, or the `BeamMP-Server-tests` target to build the unit-tests (does not include a working server). In the example from 8. (on Linux), you could build with `make BeamMP-Server`, `make -j BeamMP-Server` or `cmake --build . --parallel --target BeamMP-Server` . Or, on Windows, (in Visual Studio), you would just press some big green "run" or "debug" button.
10. When making changes, refer to [this section on how to commit properly](#how-to-commit). Not following those guidelines will result in your changes being rejected, so please take a look.
11. Make sure to add Unit-tests with `doctest` if you build new stuff. You can find examples all over the latest version of the codebase (search for `TEST_CASE`).
# Code Guidelines
## Formatting
1. Use `clang-format` to format your code before committig. A `.clang-format` file is provided in the root of the repository.
2. All identifiers, type names, function names, etc. should be `PascalCase`. Type names may also have the `T` prefix, although this is not enforced (for example `TNetwork`).
## Modular code
Write code that is modular and testable. Generally, if you can write a good unit-test for it, it's modular. If you can't, it's not.
Don't overdo it though - sometimes its okay to just write code, do the job, be done with it. You'll get feedback on this in the code review for your PR.
+21
View File
@@ -1,3 +1,24 @@
# v3.0.2
- ADDED Periodic update message if a new server is released
- ADDED Config setting for the IP the http server listens on
- CHANGED Default MaxPlayers to 8
- CHANGED Default http server listen IP to localhost
- FIXED `MP.CreateEventTimer` filling up the queue (see <https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302>)
- FIXED `MP.TriggerClientEvent` not kicking the client if it failed
- FIXED Lua result queue handling not checking all results
- FIXED bug which caused ServerConfig.toml to generate incorrectly
# v3.0.1
- ADDED Backup URLs to UpdateCheck (will fail less often now)
- ADDED console cursor left and right movement (with arrow keys) and working HOME and END key (via github.com/lionkor/commandline)
- FIXED infinite snowmen / infinite unicycle spawning bug
- FIXED a bug where, when run with --working-directory, the Server.log would still be in the original directory
- FIXED a bug which could cause the plugin reload thread to spin at 100% if the reloaded plugin's didn't terminate
- FIXED an issue which would cause servers to crash on mod download via SIGPIPE on POSIX
- FIXED an issue which would cause servers to crash when checking if a vehicle is a unicycle
# v3.0.0 # v3.0.0
- CHANGED entire plugin Lua implementation (rewrite) - CHANGED entire plugin Lua implementation (rewrite)
+27 -35
View File
@@ -4,9 +4,9 @@
[![CMake Linux Build](https://github.com/BeamMP/BeamMP-Server/workflows/CMake%20Linux%20Build/badge.svg?branch=master)](https://github.com/BeamMP/BeamMP-Server/actions?query=workflow%3A%22CMake+Linux+Build%22) [![CMake Linux Build](https://github.com/BeamMP/BeamMP-Server/workflows/CMake%20Linux%20Build/badge.svg?branch=master)](https://github.com/BeamMP/BeamMP-Server/actions?query=workflow%3A%22CMake+Linux+Build%22)
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 through which all clients communicate. You can write Lua mods for the server, there are detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com).
**For Linux, you __need__ the runtime dependencies, listed below under "prerequisites".** **For Linux, you __need__ the runtime dependencies, listed below under "[prerequisites](#prerequisites)".**
## Support + Contact ## Support + Contact
@@ -14,6 +14,7 @@ Feel free to ask any questions via the following channels:
- **IRC**: `#beammp` on [irc.libera.chat](https://web.libera.chat/) - **IRC**: `#beammp` on [irc.libera.chat](https://web.libera.chat/)
- **Discord**: [click for invite](https://discord.gg/beammp) - **Discord**: [click for invite](https://discord.gg/beammp)
- **BeamMP Forum**: [BeamMP Forum Support](https://forum.beammp.com/c/support/33)
## Minimum Requirements ## Minimum Requirements
@@ -28,13 +29,13 @@ These values are guesstimated and are subject to change with each release.
## Contributing ## 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. TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" label or with nobody assigned.
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. To contribute, look at the active [issues](https://github.com/BeamMP/BeamMP-Server/issues). Any issues that have the "help wanted" label or don't have anyone assigned 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. 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++. 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
@@ -42,7 +43,7 @@ We only allow building unmodified (original) source code for public use. `master
## Supported Operating Systems ## Supported Operating Systems
The code itself supports (latest stable) Linux and Windows. In terms of actual build support, for now we usually only distribute windows binaries and sometimes linux. For any other distro or OS, you just have to find the same libraries listed in the Linux Build [Prerequisites](#prerequisites) further down the page, and it should build fine. We don't currently support any big-endian architectures. The code itself supports (latest stable) Linux and Windows. In terms of actual build support, for now we usually only distribute Windows binaries and sometimes Linux. For any other distro or OS, you just have to find the same libraries listed in the Linux Build [Prerequisites](#prerequisites) further down the page, and it should build fine. We don't currently support any big-endian architectures.
Recommended compilers: MSVC, GCC, CLANG. Recommended compilers: MSVC, GCC, CLANG.
@@ -52,7 +53,7 @@ You can find precompiled binaries under [Releases](https://github.com/BeamMP/Bea
**__Do not compile from `master`. Always build from a release tag, i.e. `tags/v2.3.3`!__** **__Do not compile from `master`. Always build from a release tag, i.e. `tags/v2.3.3`!__**
Currently only linux and windows are supported (generally). See [Releases](https://github.com/BeamMP/BeamMP-Server/releases/) for official binary releases. On systems to which we do not provide binaries (so anything but windows), you are allowed to compile the program and use it. Other restrictions, such as not being allowed to distribute those binaries, still apply (see [copyright notice](#copyright)). Currently only Linux and Windows are supported (generally). See [Releases](https://github.com/BeamMP/BeamMP-Server/releases/) for official binary releases. On systems to which we do not provide binaries (so anything but windows), you are allowed to compile the program and use it. Other restrictions, such as not being allowed to distribute those binaries, still apply (see [copyright notice](#copyright)).
### Prerequisites ### Prerequisites
@@ -60,7 +61,7 @@ Currently only linux and windows are supported (generally). See [Releases](https
Please use the prepackaged binaries in [Releases](https://github.com/BeamMP/BeamMP-Server/releases/). Please use the prepackaged binaries in [Releases](https://github.com/BeamMP/BeamMP-Server/releases/).
Dependencies for **windows** can be installed with `vcpkg`. Dependencies for **Windows** can be installed with `vcpkg`.
These are: These are:
``` ```
lua lua
@@ -73,19 +74,15 @@ curl
#### Linux #### Linux
These package names are in the debian / ubuntu style. Feel free to PR your own guide for a different distro. Runtime dependencies - you want to find packages for:
- libz
- rapidjson
- lua5.3
- ssl / openssl
- websocketpp
- curl (with ssl support)
Runtime dependencies for **linux** are (debian/ubuntu): Build-time dependencies are:
```
libz-dev
rapidjson-dev
liblua5.3
libssl-dev
libwebsocketpp-dev
libcurl4-openssl-dev
```
Build-time dependencies for **linux** are:
``` ```
git git
make make
@@ -93,29 +90,24 @@ cmake
g++ g++
``` ```
For other distributions (e.g. Arch) you want to find packages for:
- libz
- rapidjson
- lua5.3
- ssl / openssl
- websocketpp
- curl (with ssl support)
- \+ the build time dependencies from above
### How to build ### How to build
On windows, use git-bash for these commands. On Linux, these should work in your shell. On Windows, use git-bash for these commands. On Linux, these should work in your shell.
1. Make sure you have all [prerequisites](#prerequisites) installed 1. Make sure you have all [prerequisites](#prerequisites) installed
2. Clone the repository in a location of your choice with `git clone --recurse-submodules https://github.com/BeamMP/BeamMP-Server`. 2. Clone the repository in a location of your choice with `git clone --recurse-submodules https://github.com/BeamMP/BeamMP-Server`.
3. Ensure that all submodules are initialized by running `git submodule update --init --recursive`. Then change into the cloned directory by running `cd BeamMP-Server`. 3. Change into the BeamMP-Server directory by running `cd BeamMP-Server`.
4. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v2.3.3` for version 2.3.3. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags). 4. Checkout the branch of the release you want to compile, for example `git checkout tags/v3.0.2` for version 3.0.2. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
5. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`) 5. Ensure that all submodules are initialized by running `git submodule update --init --recursive`
6. Run `make` 6. Run `cmake . -DCMAKE_BUILD_TYPE=Release` (with `.`)
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. Run `make`
8. 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-installation) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
*tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.* *tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*
## Support
The BeamMP project is supported by community donations via our [Patreon](https://www.patreon.com/BeamMP). This brings perks such as Patreon-only channels on our Discord, early access to new updates, and more server keys.
## Copyright ## Copyright
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor). Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor).
Vendored Submodule
+1
Submodule deps/json added at eb21824147
Vendored
+1 -1
+1 -1
View File
@@ -92,7 +92,7 @@ private:
std::queue<std::string> mPacketsSync; std::queue<std::string> mPacketsSync;
std::unordered_map<std::string, std::string> mIdentifiers; std::unordered_map<std::string, std::string> mIdentifiers;
bool mIsGuest = false; bool mIsGuest = false;
std::mutex mVehicleDataMutex; mutable std::mutex mVehicleDataMutex;
TSetOfVehicleData mVehicleData; TSetOfVehicleData mVehicleData;
std::string mName = "Unknown Client"; std::string mName = "Unknown Client";
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) }; SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
+14 -6
View File
@@ -7,6 +7,7 @@ extern TSentry Sentry;
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <filesystem>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -43,7 +44,7 @@ public:
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" }; std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" }; std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
bool HTTPServerEnabled { false }; bool HTTPServerEnabled { false };
int MaxPlayers { 10 }; int MaxPlayers { 8 };
bool Private { true }; bool Private { true };
int MaxCars { 1 }; int MaxCars { 1 };
bool DebugModeEnabled { false }; bool DebugModeEnabled { false };
@@ -52,7 +53,9 @@ public:
bool SendErrors { true }; bool SendErrors { true };
bool SendErrorsMessageEnabled { true }; bool SendErrorsMessageEnabled { true };
int HTTPServerPort { 8080 }; int HTTPServerPort { 8080 };
bool HTTPServerUseSSL { true }; std::string HTTPServerIP { "127.0.0.1" };
bool HTTPServerUseSSL { false };
bool HideUpdateMessages { false };
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } [[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
}; };
@@ -74,10 +77,15 @@ public:
static TSettings Settings; static TSettings Settings;
static std::vector<std::string> GetBackendUrlsInOrder() {
return {
"backend.beammp.com",
"backup1.beammp.com",
"backup2.beammp.com"
};
}
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 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 void CheckForUpdates();
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str); static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
@@ -114,7 +122,7 @@ private:
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 { 3, 0, 0 }; static inline Version mVersion { 3, 0, 2 };
}; };
std::string ThreadName(bool DebugModeOverride = false); std::string ThreadName(bool DebugModeOverride = false);
+1 -1
View File
@@ -25,7 +25,7 @@ constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 };
namespace Http { namespace Http {
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
namespace Status { namespace Status {
std::string ToString(int code); std::string ToString(int code);
} }
+3
View File
@@ -15,6 +15,7 @@ public:
void WriteRaw(const std::string& str); void WriteRaw(const std::string& str);
void InitializeLuaConsole(TLuaEngine& Engine); void InitializeLuaConsole(TLuaEngine& Engine);
void BackupOldLog(); void BackupOldLog();
void StartLoggingToFile();
Commandline& Internal() { return mCommandline; } Commandline& Internal() { return mCommandline; }
private: private:
@@ -37,4 +38,6 @@ private:
bool mFirstTime { true }; bool mFirstTime { true };
std::string mStateId; std::string mStateId;
const std::string mDefaultStateId = "BEAMMP_SERVER_CONSOLE"; const std::string mDefaultStateId = "BEAMMP_SERVER_CONSOLE";
std::ofstream mLogFileStream;
std::mutex mLogFileStreamMtx;
}; };
+21 -4
View File
@@ -6,6 +6,7 @@
#include <condition_variable> #include <condition_variable>
#include <filesystem> #include <filesystem>
#include <initializer_list> #include <initializer_list>
#include <list>
#include <lua.hpp> #include <lua.hpp>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -72,6 +73,18 @@ private:
class TLuaEngine : IThreaded { class TLuaEngine : IThreaded {
public: public:
enum CallStrategy : int {
BestEffort,
Precise,
};
struct QueuedFunction {
std::string FunctionName;
std::shared_ptr<TLuaResult> Result;
std::vector<TLuaArgTypes> Args;
std::string EventName; // optional, may be empty
};
TLuaEngine(); TLuaEngine();
~TLuaEngine() noexcept { ~TLuaEngine() noexcept {
beammp_debug("Lua Engine terminated"); beammp_debug("Lua Engine terminated");
@@ -145,10 +158,11 @@ public:
return Results; // return Results; //
} }
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS); void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy);
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
sol::state_view GetStateForPlugin(const fs::path& PluginPath); sol::state_view GetStateForPlugin(const fs::path& PluginPath);
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath); TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
void AddResultToCheck(const std::shared_ptr<TLuaResult>& Result);
static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND";
@@ -165,6 +179,7 @@ private:
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
void RegisterEvent(const std::string& EventName, const std::string& FunctionName); void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
void AddPath(const fs::path& Path); // to be added to path and cpath void AddPath(const fs::path& Path); // to be added to path and cpath
void operator()() override; void operator()() override;
@@ -187,7 +202,7 @@ private:
std::thread mThread; std::thread mThread;
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue; std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue;
std::recursive_mutex mStateExecuteQueueMutex; std::recursive_mutex mStateExecuteQueueMutex;
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> mStateFunctionQueue; std::vector<QueuedFunction> mStateFunctionQueue;
std::mutex mStateFunctionQueueMutex; std::mutex mStateFunctionQueueMutex;
std::condition_variable mStateFunctionQueueCond; std::condition_variable mStateFunctionQueueCond;
TLuaEngine* mEngine; TLuaEngine* mEngine;
@@ -201,6 +216,7 @@ private:
std::chrono::high_resolution_clock::time_point LastCompletion {}; std::chrono::high_resolution_clock::time_point LastCompletion {};
std::string EventName; std::string EventName;
TLuaStateId StateId; TLuaStateId StateId;
CallStrategy Strategy;
bool Expired(); bool Expired();
void Reset(); void Reset();
}; };
@@ -217,8 +233,9 @@ private:
std::recursive_mutex mLuaEventsMutex; std::recursive_mutex mLuaEventsMutex;
std::vector<TimedEvent> mTimedEvents; std::vector<TimedEvent> mTimedEvents;
std::recursive_mutex mTimedEventsMutex; std::recursive_mutex mTimedEventsMutex;
std::queue<std::shared_ptr<TLuaResult>> mResultsToCheck; std::list<std::shared_ptr<TLuaResult>> mResultsToCheck;
std::recursive_mutex mResultsToCheckMutex; std::mutex mResultsToCheckMutex;
std::condition_variable mResultsToCheckCond;
}; };
// std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait); // std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
+1
View File
@@ -27,6 +27,7 @@ void TClient::ClearCars() {
int TClient::GetOpenCarID() const { int TClient::GetOpenCarID() const {
int OpenID = 0; int OpenID = 0;
bool found; bool found;
std::unique_lock lock(mVehicleDataMutex);
do { do {
found = true; found = true;
for (auto& v : mVehicleData) { for (auto& v : mVehicleData) {
+31 -17
View File
@@ -97,28 +97,42 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status
void Application::CheckForUpdates() { void Application::CheckForUpdates() {
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting); Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
static bool FirstTime = true;
// checks current version against latest version // checks current version against latest version
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); for (const auto& url : GetBackendUrlsInOrder()) {
bool Matches = std::regex_match(Response, VersionRegex); auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s");
if (Matches) { bool Matches = std::regex_match(Response, VersionRegex);
auto MyVersion = ServerVersion(); if (Matches) {
auto RemoteVersion = Version(VersionStrToInts(Response)); auto MyVersion = ServerVersion();
if (IsOutdated(MyVersion, RemoteVersion)) { auto RemoteVersion = Version(VersionStrToInts(Response));
std::string RealVersionString = RemoteVersion.AsString(); if (IsOutdated(MyVersion, RemoteVersion)) {
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET)); std::string RealVersionString = RemoteVersion.AsString();
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION IS OUT! Please update to the new version (v" + RealVersionString + ") of the BeamMP-Server! Download it here: https://beammp.com/! For a guide on how to update, visit: https://wiki.beammp.com/en/home/server-maintenance#updating-the-server" + std::string(ANSI_RESET));
} else {
if (FirstTime) {
beammp_info("Server up-to-date!");
}
}
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
break;
} else { } else {
beammp_info("Server up-to-date!"); if (FirstTime) {
beammp_debug("Failed to fetch version from: " + url);
beammp_trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } });
Sentry.LogError("failed to get server version", _file_basename, _line);
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
}
} }
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
} else {
beammp_warn("Unable to fetch version from backend.");
beammp_trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } });
Sentry.LogError("failed to get server version", _file_basename, _line);
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
} }
if (Application::GetSubsystemStatuses().at("UpdateCheck") == Application::Status::Bad) {
if (FirstTime) {
beammp_warn("Unable to fetch version info from backend.");
}
}
FirstTime = false;
} }
// thread name stuff // thread name stuff
+16 -4
View File
@@ -4,6 +4,7 @@
#include "Common.h" #include "Common.h"
#include "CustomAssert.h" #include "CustomAssert.h"
#include "LuaAPI.h" #include "LuaAPI.h"
#include "httplib.h"
#include <map> #include <map>
#include <random> #include <random>
@@ -33,17 +34,20 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
} }
} }
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status) { std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) {
httplib::SSLClient client(host, port); httplib::SSLClient client(host, port);
client.set_read_timeout(std::chrono::seconds(10));
beammp_assert(client.is_valid());
client.enable_server_certificate_verification(false); client.enable_server_certificate_verification(false);
client.set_address_family(AF_INET); client.set_address_family(AF_INET);
auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str()); auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
if (res) { if (res) {
if (status) { if (status) {
*status = res->status; *status = res->status;
} }
return res->body; return res->body;
} else { } else {
beammp_debug("POST failed: " + httplib::to_string(res.error()));
return Http::ErrorString; return Http::ErrorString;
} }
} }
@@ -289,7 +293,7 @@ Http::Server::THttpServerInstance::THttpServerInstance() {
mThread.detach(); mThread.detach();
} }
void Http::Server::THttpServerInstance::operator()() { void Http::Server::THttpServerInstance::operator()() try {
beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort)); beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort));
std::unique_ptr<httplib::Server> HttpLibServerInstance; std::unique_ptr<httplib::Server> HttpLibServerInstance;
if (Application::Settings.HTTPServerUseSSL) { if (Application::Settings.HTTPServerUseSSL) {
@@ -366,6 +370,14 @@ void Http::Server::THttpServerInstance::operator()() {
HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) { HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) {
res.set_content(std::string(Magic), "text/plain"); res.set_content(std::string(Magic), "text/plain");
}); });
HttpLibServerInstance->set_logger([](const httplib::Request& Req, const httplib::Response& Res) {
beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status));
});
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good); Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
HttpLibServerInstance->listen("0.0.0.0", Application::Settings.HTTPServerPort); auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort);
if (!ret) {
beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it.");
}
} catch (const std::exception& e) {
beammp_error("Failed to start http server. Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it. Error: " + std::string(e.what()));
} }
+2 -1
View File
@@ -112,7 +112,8 @@ bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName,
} }
auto c = MaybeClient.value().lock(); auto c = MaybeClient.value().lock();
if (!Engine->Network().Respond(*c, Packet, true)) { if (!Engine->Network().Respond(*c, Packet, true)) {
beammp_lua_error("Respond failed"); beammp_lua_error("Respond failed, dropping client " + std::to_string(PlayerID));
Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
return false; return false;
} }
} }
+41 -38
View File
@@ -17,15 +17,19 @@ static constexpr std::string_view StrName = "Name";
static constexpr std::string_view StrDescription = "Description"; static constexpr std::string_view StrDescription = "Description";
static constexpr std::string_view StrResourceFolder = "ResourceFolder"; static constexpr std::string_view StrResourceFolder = "ResourceFolder";
static constexpr std::string_view StrAuthKey = "AuthKey"; static constexpr std::string_view StrAuthKey = "AuthKey";
// Misc
static constexpr std::string_view StrSendErrors = "SendErrors"; static constexpr std::string_view StrSendErrors = "SendErrors";
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage"; static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled"; static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
// HTTP // HTTP
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath"; static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
static constexpr std::string_view StrSSLCertPath = "SSLCertPath"; static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort"; static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP";
TConfig::TConfig(const std::string& ConfigFileName) TConfig::TConfig(const std::string& ConfigFileName)
: mConfigFileName(ConfigFileName) { : mConfigFileName(ConfigFileName) {
@@ -58,8 +62,8 @@ void SetComment(CommentsT& Comments, const std::string& Comment) {
* whether it is in TConfig.cpp or the configuration file. * whether it is in TConfig.cpp or the configuration file.
*/ */
void TConfig::FlushToFile() { void TConfig::FlushToFile() {
auto data = toml::parse<toml::preserve_comments>(mConfigFileName); // auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
data["General"] = toml::table(); auto data = toml::value {};
data["General"][StrAuthKey.data()] = Application::Settings.Key; data["General"][StrAuthKey.data()] = Application::Settings.Key;
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server"); SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled; data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
@@ -71,19 +75,39 @@ void TConfig::FlushToFile() {
data["General"][StrMap.data()] = Application::Settings.MapName; data["General"][StrMap.data()] = Application::Settings.MapName;
data["General"][StrDescription.data()] = Application::Settings.ServerDesc; data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
data["General"][StrResourceFolder.data()] = Application::Settings.Resource; data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
data["General"][StrSendErrors.data()] = Application::Settings.SendErrors; // Misc
SetComment(data["General"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here"); data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
data["General"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled; SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless.");
SetComment(data["General"][StrSendErrorsMessageEnabled.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`"); data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors;
SetComment(data["Misc"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`");
// HTTP
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath; data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath; data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort; data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
SetComment(data["HTTP"][StrHTTPServerIP.data()].comments(), " Which IP to listen on. Pick 0.0.0.0 for a public-facing server with no specific IP, and 127.0.0.1 or 'localhost' for a local server.");
data["HTTP"][StrHTTPServerIP.data()] = Application::Settings.HTTPServerIP;
data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL; data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL;
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to keep enabled. With SSL the server will serve https and requires valid key and cert files"); SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to have enabled for servers which face the internet. With SSL the server will serve https and requires valid key and cert files");
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled; data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server"); SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
std::ofstream Stream(mConfigFileName); std::stringstream Ss;
Stream << data << std::flush; Ss << "# 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"
<< data;
auto File = std::fopen(mConfigFileName.c_str(), "w+");
if (!File) {
beammp_error("Failed to create/write to config file: " + GetPlatformAgnosticErrorString());
throw std::runtime_error("Failed to create/write to config file");
}
auto Str = Ss.str();
auto N = std::fwrite(Str.data(), sizeof(char), Str.size(), File);
if (N != Str.size()) {
beammp_error("Failed to write to config file properly, config file might be misshapen");
}
std::fclose(File);
} }
void TConfig::CreateConfigFile(std::string_view name) { void TConfig::CreateConfigFile(std::string_view name) {
@@ -98,32 +122,7 @@ void TConfig::CreateConfigFile(std::string_view name) {
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what())); beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
} }
{ // create file context
std::ofstream ofs(name.data());
}
FlushToFile(); FlushToFile();
size_t FileSize = fs::file_size(name);
std::fstream ofs { std::string(name), std::ios::in | std::ios::out };
if (ofs.good()) {
std::string Contents {};
Contents.resize(FileSize);
ofs.readsome(Contents.data(), FileSize);
ofs.seekp(0);
ofs << "# This is the BeamMP-Server config file.\n"
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
<< '\n'
<< Contents;
beammp_error("There was no \"" + std::string(mConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
mFailed = true;
ofs.close();
} else {
beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
Application::SetSubsystemStatus("Config", Application::Status::Bad);
mFailed = true;
}
} }
void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) { void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) {
@@ -158,12 +157,15 @@ void TConfig::ParseFromFile(std::string_view name) {
TryReadValue(data, "General", StrDescription, Application::Settings.ServerDesc); TryReadValue(data, "General", StrDescription, Application::Settings.ServerDesc);
TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource); TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource);
TryReadValue(data, "General", StrAuthKey, Application::Settings.Key); TryReadValue(data, "General", StrAuthKey, Application::Settings.Key);
TryReadValue(data, "General", StrSendErrors, Application::Settings.SendErrors); // Misc
TryReadValue(data, "General", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled); TryReadValue(data, "Misc", StrSendErrors, Application::Settings.SendErrors);
TryReadValue(data, "Misc", StrHideUpdateMessages, Application::Settings.HideUpdateMessages);
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
// HTTP // HTTP
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath); TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath); TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort); TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort);
TryReadValue(data, "HTTP", StrHTTPServerIP, Application::Settings.HTTPServerIP);
TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled); TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled);
TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL); TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL);
} catch (const std::exception& err) { } catch (const std::exception& err) {
@@ -202,6 +204,7 @@ void TConfig::PrintDebug() {
beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\""); beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\"");
beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\""); beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\"");
beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\""); beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\"");
beammp_debug(std::string(StrHTTPServerIP) + ": \"" + Application::Settings.HTTPServerIP + "\"");
// special! // special!
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + ""); beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
} }
+12 -5
View File
@@ -97,6 +97,17 @@ void TConsole::BackupOldLog() {
} }
} }
void TConsole::StartLoggingToFile() {
mLogFileStream.open("Server.log");
Application::Console().Internal().on_write = [this](const std::string& ToWrite) {
// TODO: Sanitize by removing all ansi escape codes (vt100)
std::unique_lock Lock(mLogFileStreamMtx);
mLogFileStream.write(ToWrite.c_str(), ToWrite.size());
mLogFileStream.write("\n", 1);
mLogFileStream.flush();
};
}
void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) { void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) {
if (!mIsLuaConsole) { if (!mIsLuaConsole) {
if (!mLuaEngine) { if (!mLuaEngine) {
@@ -369,10 +380,6 @@ TConsole::TConsole() {
mCommandline.set_history_limit(20); mCommandline.set_history_limit(20);
mCommandline.set_prompt("> "); mCommandline.set_prompt("> ");
BackupOldLog(); BackupOldLog();
bool success = mCommandline.enable_write_to_file("Server.log");
if (!success) {
beammp_error("unable to open file for writing: \"Server.log\"");
}
mCommandline.on_command = [this](Commandline& c) { mCommandline.on_command = [this](Commandline& c) {
try { try {
auto cmd = c.get_command(); auto cmd = c.get_command();
@@ -386,7 +393,7 @@ TConsole::TConsole() {
} else { } else {
auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" }); auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" });
while (!Future->Ready) { while (!Future->Ready) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // TODO: Add a timeout std::this_thread::yield(); // TODO: Add a timeout
} }
if (Future->Error) { if (Future->Error) {
beammp_lua_error(Future->ErrorMessage); beammp_lua_error(Future->ErrorMessage);
+67 -25
View File
@@ -19,7 +19,9 @@ void THeartbeatThread::operator()() {
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
bool isAuth = false; bool isAuth = false;
size_t UpdateReminderCounter = 0;
while (!mShutdown) { while (!mShutdown) {
++UpdateReminderCounter;
Body = GenerateCall(); Body = GenerateCall();
// a hot-change occurs when a setting has changed, to update the backend of that change. // a hot-change occurs when a setting has changed, to update the backend of that change.
auto Now = std::chrono::high_resolution_clock::now(); auto Now = std::chrono::high_resolution_clock::now();
@@ -54,43 +56,83 @@ void THeartbeatThread::operator()() {
auto Target = "/heartbeat"; auto Target = "/heartbeat";
unsigned int ResponseCode = 0; 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) || ResponseCode != 200) { json::Document Doc;
beammp_trace("got " + T + " from backend"); bool Ok = false;
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); for (const auto& Url : Application::GetBackendUrlsInOrder()) {
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
std::this_thread::sleep_for(std::chrono::milliseconds(500)); beammp_trace(T);
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); Doc.Parse(T.data(), T.size());
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { if (Doc.HasParseError() || !Doc.IsObject()) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); beammp_debug("Failed to contact backend at " + Url + " (this is not an error).");
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad); beammp_trace("Response was: " + T);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); Sentry.SetContext("JSON Response", { { "reponse", T } });
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); SentryReportError(Url + Target, ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { } else if (ResponseCode != 200) {
beammp_warn("Backend system refused server! Server will not show in the public server list."); SentryReportError(Url + Target, ResponseCode);
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
isAuth = false;
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
} else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
}
} else { } else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); // all ok
Ok = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::string Status {};
std::string Code {};
std::string Message {};
const auto StatusKey = "status";
const auto CodeKey = "code";
const auto MessageKey = "msg";
if (Ok) {
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
Status = Doc[StatusKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
Code = Doc[CodeKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
Message = Doc[MessageKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } });
Ok = false;
}
if (!Ok) {
beammp_error("Missing/invalid json members in backend response");
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
} }
} else { } else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good); if (!Application::Settings.Private) {
beammp_warn("Backend failed to respond to a heartbeat. Your server may temporarily disappear from the server list. This is not an error, and will likely resolve itself soon. Direct connect will still work.");
}
} }
if (!isAuth) { if (Ok && !isAuth) {
if (T == "2000") { if (Status == "2000") {
beammp_info(("Authenticated!")); beammp_info(("Authenticated!"));
isAuth = true; isAuth = true;
} else if (T == "200") { } else if (Status == "200") {
beammp_info(("Resumed authenticated session!")); beammp_info(("Resumed authenticated session!"));
isAuth = true; isAuth = true;
} else {
if (Message.empty()) {
Message = "Backend didn't provide a reason";
}
beammp_error("Backend REFUSED the auth key. " + Message);
} }
} }
if (isAuth) {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
}
if (!Application::Settings.HideUpdateMessages && UpdateReminderCounter % 5) {
Application::CheckForUpdates();
}
} }
} }
+96 -48
View File
@@ -44,7 +44,7 @@ void TLuaEngine::operator()() {
CollectAndInitPlugins(); CollectAndInitPlugins();
// now call all onInit's // now call all onInit's
auto Futures = TriggerEvent("onInit", ""); auto Futures = TriggerEvent("onInit", "");
WaitForAll(Futures); WaitForAll(Futures, std::chrono::seconds(5));
for (const auto& Future : Futures) { for (const auto& Future : Futures) {
if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) { if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) {
beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage); beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage);
@@ -54,31 +54,20 @@ void TLuaEngine::operator()() {
auto ResultCheckThread = std::thread([&] { auto ResultCheckThread = std::thread([&] {
RegisterThread("ResultCheckThread"); RegisterThread("ResultCheckThread");
while (!mShutdown) { while (!mShutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::unique_lock Lock(mResultsToCheckMutex); std::unique_lock Lock(mResultsToCheckMutex);
mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20));
if (!mResultsToCheck.empty()) { if (!mResultsToCheck.empty()) {
auto Res = mResultsToCheck.front(); mResultsToCheck.remove_if([](const std::shared_ptr<TLuaResult>& Ptr) -> bool {
mResultsToCheck.pop(); if (Ptr->Ready) {
Lock.unlock(); return true;
} else if (Ptr->Error) {
size_t Waited = 0; if (Ptr->ErrorMessage != BeamMPFnNotFoundError) {
while (!Res->Ready) { beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage);
std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
Waited++; return true;
if (Waited > 250) {
// FIXME: This should *eventually* timeout.
// beammp_lua_error(Res->Function + " in " + Res->StateId + " took >1s to respond, not printing possible errors");
Lock.lock();
mResultsToCheck.push(Res);
Lock.unlock();
break;
} }
} return false;
if (Res->Error) { });
if (Res->ErrorMessage != BeamMPFnNotFoundError) {
beammp_lua_error(Res->Function + ": " + Res->ErrorMessage);
}
}
} }
} }
}); });
@@ -86,27 +75,37 @@ void TLuaEngine::operator()() {
auto Before = std::chrono::high_resolution_clock::now(); auto Before = std::chrono::high_resolution_clock::now();
while (!mShutdown) { while (!mShutdown) {
if (mLuaStates.size() == 0) { if (mLuaStates.size() == 0) {
std::this_thread::sleep_for(std::chrono::seconds(500)); std::this_thread::sleep_for(std::chrono::seconds(100));
} }
{ // Timed Events Scope { // Timed Events Scope
std::unique_lock Lock(mTimedEventsMutex); std::unique_lock Lock(mTimedEventsMutex);
for (auto& Timer : mTimedEvents) { for (auto& Timer : mTimedEvents) {
if (Timer.Expired()) { if (Timer.Expired()) {
auto LastCompletionBeforeReset = Timer.LastCompletion;
Timer.Reset(); Timer.Reset();
auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId); auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId);
std::unique_lock StateLock(mLuaStatesMutex); std::unique_lock StateLock(mLuaStatesMutex);
std::unique_lock Lock2(mResultsToCheckMutex); std::unique_lock Lock2(mResultsToCheckMutex);
for (auto& Handler : Handlers) { for (auto& Handler : Handlers) {
auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCall(Handler, {}); auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCallFromCustomEvent(Handler, {}, Timer.EventName, Timer.Strategy);
mResultsToCheck.push(Res); if (Res) {
mResultsToCheck.push_back(Res);
mResultsToCheckCond.notify_one();
} else {
// "revert" reset
Timer.LastCompletion = LastCompletionBeforeReset;
// beammp_trace("Reverted reset of \"" + Timer.EventName + "\" timer");
// no need to try to enqueue more handlers for this event (they will all fail)
break;
}
} }
} }
} }
} }
std::chrono::high_resolution_clock::duration Diff; const auto Expected = std::chrono::milliseconds(10);
if ((Diff = std::chrono::high_resolution_clock::now() - Before) if (auto Diff = std::chrono::high_resolution_clock::now() - Before;
< std::chrono::milliseconds(10)) { Diff < Expected) {
std::this_thread::sleep_for(Diff); std::this_thread::sleep_for(Expected - Diff);
} else { } else {
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind"); beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
} }
@@ -149,6 +148,12 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) {
return ""; return "";
} }
void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
std::unique_lock Lock(mResultsToCheckMutex);
mResultsToCheck.push_back(Result);
mResultsToCheckCond.notify_one();
}
void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) { void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) {
for (const auto& Result : Results) { for (const auto& Result : Results) {
bool Cancelled = false; bool Cancelled = false;
@@ -157,8 +162,10 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
ms += 10; ms += 10;
if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) { if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) {
beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms)"); beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms).");
Cancelled = true; Cancelled = true;
} else if (ms > 1000 * 60) {
beammp_lua_warn("'" + Result->Function + "' in '" + Result->StateId + "' is taking very long. The event it's handling is too important to discard the result of this handler, but may block this event and possibly the whole lua state.");
} }
} }
if (Cancelled) { if (Cancelled) {
@@ -176,7 +183,8 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) { void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) {
std::unique_lock Lock2(mResultsToCheckMutex); std::unique_lock Lock2(mResultsToCheckMutex);
for (const auto& Result : Results) { for (const auto& Result : Results) {
mResultsToCheck.push(Result); mResultsToCheck.push_back(Result);
mResultsToCheckCond.notify_one();
} }
} }
@@ -504,11 +512,27 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
return Lua_GetPlayerIdentifiers(ID); return Lua_GetPlayerIdentifiers(ID);
}); });
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep); MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
MPTable.set_function("CreateEventTimer", [&](const std::string& EventName, size_t IntervalMS) { // const std::string& EventName, size_t IntervalMS, int strategy
MPTable.set_function("CreateEventTimer", [&](sol::variadic_args Args) {
if (Args.size() < 2 || Args.size() > 3) {
beammp_lua_error("CreateEventTimer expects 2 or 3 arguments.");
}
if (Args.get_type(0) != sol::type::string) {
beammp_lua_error("CreateEventTimer expects 1st argument to be a string");
}
if (Args.get_type(1) != sol::type::number) {
beammp_lua_error("CreateEventTimer expects 2nd argument to be a number");
}
if (Args.size() == 3 && Args.get_type(2) != sol::type::number) {
beammp_lua_error("CreateEventTimer expects 3rd argument to be a number (MP.CallStrategy)");
}
auto EventName = Args.get<std::string>(0);
auto IntervalMS = Args.get<size_t>(1);
CallStrategy Strategy = Args.size() > 2 ? Args.get<CallStrategy>(2) : CallStrategy::BestEffort;
if (IntervalMS < 25) { if (IntervalMS < 25) {
beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly."); beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly.");
} }
mEngine->CreateEventTimer(EventName, mStateId, IntervalMS); mEngine->CreateEventTimer(EventName, mStateId, IntervalMS, Strategy);
}); });
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) { MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
mEngine->CancelEventTimers(EventName, mStateId); mEngine->CancelEventTimers(EventName, mStateId);
@@ -528,6 +552,10 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
"Name", 5, "Name", 5,
"Description", 6); "Description", 6);
MPTable.create_named("CallStrategy",
"BestEffort", CallStrategy::BestEffort,
"Precise", CallStrategy::Precise);
auto FSTable = StateView.create_named_table("FS"); auto FSTable = StateView.create_named_table("FS");
FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory); FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory);
FSTable.set_function("Exists", &LuaAPI::FS::Exists); FSTable.set_function("Exists", &LuaAPI::FS::Exists);
@@ -550,12 +578,34 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
return Result; return Result;
} }
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
// TODO: Document all this
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
if (Strategy == CallStrategy::BestEffort) {
Iter = std::find_if(mStateFunctionQueue.begin(), mStateFunctionQueue.end(),
[&EventName](const QueuedFunction& Element) {
return Element.EventName == EventName;
});
}
if (Iter == mStateFunctionQueue.end()) {
auto Result = std::make_shared<TLuaResult>();
Result->StateId = mStateId;
Result->Function = FunctionName;
std::unique_lock Lock(mStateFunctionQueueMutex);
mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName });
mStateFunctionQueueCond.notify_all();
return Result;
} else {
return nullptr;
}
}
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) { std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
auto Result = std::make_shared<TLuaResult>(); auto Result = std::make_shared<TLuaResult>();
Result->StateId = mStateId; Result->StateId = mStateId;
Result->Function = FunctionName; Result->Function = FunctionName;
std::unique_lock Lock(mStateFunctionQueueMutex); std::unique_lock Lock(mStateFunctionQueueMutex);
mStateFunctionQueue.push({ FunctionName, Result, Args }); mStateFunctionQueue.push_back({ FunctionName, Result, Args, "" });
mStateFunctionQueueCond.notify_all(); mStateFunctionQueueCond.notify_all();
return Result; return Result;
} }
@@ -618,12 +668,13 @@ void TLuaEngine::StateThreadData::operator()() {
std::chrono::milliseconds(500), std::chrono::milliseconds(500),
[&]() -> bool { return !mStateFunctionQueue.empty(); }); [&]() -> bool { return !mStateFunctionQueue.empty(); });
if (NotExpired) { if (NotExpired) {
auto FnNameResultPair = std::move(mStateFunctionQueue.front()); auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
mStateFunctionQueue.pop(); mStateFunctionQueue.erase(mStateFunctionQueue.begin());
Lock.unlock(); Lock.unlock();
auto& FnName = std::get<0>(FnNameResultPair); auto& FnName = TheQueuedFunction.FunctionName;
auto& Result = std::get<1>(FnNameResultPair); auto& Result = TheQueuedFunction.Result;
auto Args = std::get<2>(FnNameResultPair); auto Args = TheQueuedFunction.Args;
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
Result->StateId = mStateId; Result->StateId = mStateId;
sol::state_view StateView(mState); sol::state_view StateView(mState);
auto Fn = StateView[FnName]; auto Fn = StateView[FnName];
@@ -671,13 +722,14 @@ void TLuaEngine::StateThreadData::operator()() {
} }
} }
void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) { void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy) {
std::unique_lock Lock(mTimedEventsMutex); std::unique_lock Lock(mTimedEventsMutex);
TimedEvent Event { TimedEvent Event {
std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) }, std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) },
std::chrono::high_resolution_clock::now(), std::chrono::high_resolution_clock::now(),
EventName, EventName,
StateId StateId,
Strategy
}; };
mTimedEvents.push_back(std::move(Event)); mTimedEvents.push_back(std::move(Event));
beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval"); beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval");
@@ -705,6 +757,7 @@ void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) {
void TLuaResult::WaitUntilReady() { void TLuaResult::WaitUntilReady() {
while (!Ready) { while (!Ready) {
std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
@@ -762,12 +815,7 @@ void TPluginMonitor::operator()() {
auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path()); auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path());
auto Res = mEngine.EnqueueScript(StateID, Chunk); auto Res = mEngine.EnqueueScript(StateID, Chunk);
// TODO: call onInit // TODO: call onInit
while (!Res->Ready) { mEngine.AddResultToCheck(Res);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (Res->Error) {
beammp_lua_error(Res->ErrorMessage);
}
} else { } else {
// TODO: trigger onFileChanged event // TODO: trigger onFileChanged event
beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet"); beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet");
+29 -9
View File
@@ -154,6 +154,19 @@ void TNetwork::TCPServerMain() {
beammp_warn(("Got an invalid client socket on connect! Skipping...")); beammp_warn(("Got an invalid client socket on connect! Skipping..."));
continue; continue;
} }
// set timeout (DWORD, aka uint32_t)
uint32_t SendTimeoutMS = 30 * 1000;
#if defined(BEAMMP_WINDOWS)
int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&SendTimeoutMS), sizeof(SendTimeoutMS));
#else // POSIX
struct timeval optval;
optval.tv_sec = (int)(SendTimeoutMS / 1000);
optval.tv_usec = (SendTimeoutMS % 1000) * 1000;
int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<void*>(&optval), sizeof(optval));
#endif
if (ret < 0) {
throw std::runtime_error("setsockopt recv timeout: " + GetPlatformAgnosticErrorString());
}
std::thread ID(&TNetwork::Identify, this, client); std::thread ID(&TNetwork::Identify, this, client);
ID.detach(); // TODO: Add to a queue and attempt to join periodically ID.detach(); // TODO: Add to a queue and attempt to join periodically
} catch (const std::exception& e) { } catch (const std::exception& e) {
@@ -187,7 +200,11 @@ void TNetwork::Identify(const TConnection& client) {
} else if (Code == 'D') { } else if (Code == 'D') {
HandleDownload(client.Socket); HandleDownload(client.Socket);
} else if (Code == 'P') { } else if (Code == 'P') {
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
send(client.Socket, "P", 1, MSG_NOSIGNAL);
#else
send(client.Socket, "P", 1, 0); send(client.Socket, "P", 1, 0);
#endif
CloseSocketProper(client.Socket); CloseSocketProper(client.Socket);
return; return;
} else { } else {
@@ -510,6 +527,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
} }
} }
} }
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) { void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger // TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
if (c.expired() || c.lock()->GetTCPSock() == -1) { if (c.expired() || c.lock()->GetTCPSock() == -1) {
@@ -738,11 +756,11 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) { void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) {
std::ifstream f(Name.c_str(), std::ios::binary); std::ifstream f(Name.c_str(), std::ios::binary);
uint32_t Split = 0x7735940; // 125MB uint32_t Split = 0x7735940; // 125MB
char* Data; std::vector<char> Data;
if (Size > Split) if (Size > Split)
Data = new char[Split]; Data.resize(Split);
else else
Data = new char[Size]; Data.resize(Size);
SOCKET TCPSock; SOCKET TCPSock;
if (D) if (D)
TCPSock = c.GetDownSock(); TCPSock = c.GetDownSock();
@@ -753,8 +771,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
size_t Diff = Size - Sent; size_t Diff = Size - Sent;
if (Diff > Split) { if (Diff > Split) {
f.seekg(Sent, std::ios_base::beg); f.seekg(Sent, std::ios_base::beg);
f.read(Data, Split); f.read(Data.data(), Split);
if (!TCPSendRaw(c, TCPSock, Data, Split)) { if (!TCPSendRaw(c, TCPSock, Data.data(), Split)) {
if (c.GetStatus() > -1) if (c.GetStatus() > -1)
c.SetStatus(-1); c.SetStatus(-1);
break; break;
@@ -762,8 +780,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
Sent += Split; Sent += Split;
} else { } else {
f.seekg(Sent, std::ios_base::beg); f.seekg(Sent, std::ios_base::beg);
f.read(Data, Diff); f.read(Data.data(), Diff);
if (!TCPSendRaw(c, TCPSock, Data, int32_t(Diff))) { if (!TCPSendRaw(c, TCPSock, Data.data(), int32_t(Diff))) {
if (c.GetStatus() > -1) if (c.GetStatus() > -1)
c.SetStatus(-1); c.SetStatus(-1);
break; break;
@@ -771,14 +789,16 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
Sent += Diff; Sent += Diff;
} }
} }
delete[] Data;
f.close();
} }
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) { bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
intmax_t Sent = 0; intmax_t Sent = 0;
do { do {
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), MSG_NOSIGNAL);
#else
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0); intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
#endif
if (Temp < 1) { if (Temp < 1) {
beammp_info("Socket Closed! " + std::to_string(socket)); beammp_info("Socket Closed! " + std::to_string(socket));
CloseSocketProper(socket); CloseSocketProper(socket);
+2 -2
View File
@@ -21,8 +21,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
void TPPSMonitor::operator()() { void TPPSMonitor::operator()() {
RegisterThread("PPSMonitor"); RegisterThread("PPSMonitor");
while (!mNetwork) { while (!mNetwork) {
// hard spi // hard(-ish) spin
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::yield();
} }
beammp_debug("PPSMonitor starting"); beammp_debug("PPSMonitor starting");
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
+14 -16
View File
@@ -7,14 +7,14 @@
#include <any> #include <any>
#include <sstream> #include <sstream>
#include <nlohmann/json.hpp>
#include "LuaAPI.h" #include "LuaAPI.h"
#undef GetObject // Fixes Windows #undef GetObject // Fixes Windows
#include "Json.h" #include "Json.h"
namespace json = rapidjson;
TServer::TServer(const std::vector<std::string_view>& Arguments) { TServer::TServer(const std::vector<std::string_view>& Arguments) {
beammp_info("BeamMP Server v" + Application::ServerVersionString()); beammp_info("BeamMP Server v" + Application::ServerVersionString());
Application::SetSubsystemStatus("Server", Application::Status::Starting); Application::SetSubsystemStatus("Server", Application::Status::Starting);
@@ -171,27 +171,25 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
} }
} }
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) { bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
rapidjson::Document Car; try {
Car.Parse(CarJson.c_str(), CarJson.size()); auto Car = nlohmann::json::parse(CarJson);
if (Car.HasParseError()) { const std::string jbm = "jbm";
beammp_error("Failed to parse vehicle data -> " + CarJson); if (Car.contains(jbm) && Car[jbm].is_string() && Car[jbm] == "unicycle") {
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") { return true;
return true; }
} catch (const std::exception& e) {
beammp_error("Failed to parse vehicle data as json for client " + std::to_string(c.GetID()) + ": '" + CarJson + "'");
} }
return false; return false;
} }
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) { bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
if (IsUnicycle(c, CarJson) && c.GetUnicycleID() < 0) {
if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) {
return true;
}
if (IsUnicycle(c, CarJson)) {
c.SetUnicycleID(ID); c.SetUnicycleID(ID);
return true; return true;
} else {
return c.GetCarCount() < Application::Settings.MaxCars;
} }
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) {
+15 -5
View File
@@ -78,10 +78,6 @@ int main(int argc, char** argv) {
int BeamMPServerMain(MainArguments Arguments) { int BeamMPServerMain(MainArguments Arguments) {
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
Application::InitializeConsole(); Application::InitializeConsole();
Application::SetSubsystemStatus("Main", Application::Status::Starting);
SetupSignalHandlers();
ArgsParser Parser; ArgsParser Parser;
Parser.RegisterArgument({ "help" }, ArgsParser::NONE); Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
Parser.RegisterArgument({ "version" }, ArgsParser::NONE); Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
@@ -122,14 +118,21 @@ int BeamMPServerMain(MainArguments Arguments) {
} }
} }
Application::SetSubsystemStatus("Main", Application::Status::Starting);
Application::Console().StartLoggingToFile();
SetupSignalHandlers();
bool Shutdown = false; bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Application::RegisterShutdownHandler([&Shutdown] {
beammp_info("If this takes too long, you can press Ctrl+C repeatedly to force a shutdown.");
Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown); Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown);
Shutdown = true; Shutdown = true;
}); });
Application::RegisterShutdownHandler([] { Application::RegisterShutdownHandler([] {
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", ""); auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", "");
TLuaEngine::WaitForAll(Futures); TLuaEngine::WaitForAll(Futures, std::chrono::seconds(5));
}); });
TServer Server(Arguments.List); TServer Server(Arguments.List);
@@ -168,6 +171,10 @@ int BeamMPServerMain(MainArguments Arguments) {
Application::SetSubsystemStatus("Main", Application::Status::Good); Application::SetSubsystemStatus("Main", Application::Status::Good);
RegisterThread("Main(Waiting)"); RegisterThread("Main(Waiting)");
std::set<std::string> IgnoreSubsystems {
"UpdateCheck" // Ignore as not to confuse users (non-vital system)
};
bool FullyStarted = false; bool FullyStarted = false;
while (!Shutdown) { while (!Shutdown) {
if (!FullyStarted) { if (!FullyStarted) {
@@ -176,6 +183,9 @@ int BeamMPServerMain(MainArguments Arguments) {
std::string SystemsBadList {}; std::string SystemsBadList {};
auto Statuses = Application::GetSubsystemStatuses(); auto Statuses = Application::GetSubsystemStatuses();
for (const auto& NameStatusPair : Statuses) { for (const auto& NameStatusPair : Statuses) {
if (IgnoreSubsystems.count(NameStatusPair.first) > 0) {
continue; // ignore
}
if (NameStatusPair.second == Application::Status::Starting) { if (NameStatusPair.second == Application::Status::Starting) {
FullyStarted = false; FullyStarted = false;
} else if (NameStatusPair.second == Application::Status::Bad) { } else if (NameStatusPair.second == Application::Status::Bad) {