Compare commits

..

166 Commits

Author SHA1 Message Date
Lion Kortlepel
588242822c CMake: Remove socketio link, forgot 2021-09-09 12:33:59 +03:00
Lion Kortlepel
58da200901 Client: fix socklen_t compile error 2021-09-09 12:32:33 +03:00
Lion Kortlepel
f4ccf6c177 add sentry native db folder to gitignore 2021-09-09 12:26:26 +03:00
Lion Kortlepel
27103a73a9 remove socket.io module 2021-09-09 12:26:03 +03:00
Lion Kortlepel
2727f90430 Remove Socket.io for now
it is being built every time and we dont need it
2021-09-09 12:25:08 +03:00
Lion Kortlepel
2a96546c8c Lua: Add GetPluginName, GetPluginPath 2021-09-09 12:15:57 +03:00
Lion Kortlepel
6462636b29 Multiple merge fixes, rebase, working Https::GET 2021-09-09 12:15:55 +03:00
Lion Kortlepel
5742ab0dad possible windows compiler fix 2021-09-09 12:15:19 +03:00
Lion Kortlepel
0087205d55 fix issues caused by rebase 2021-09-09 12:15:17 +03:00
Lion Kortlepel
d16843e45d TNetwork: clarify error messages 2021-09-09 12:14:32 +03:00
Lion Kortlepel
24516dbfd7 TNetwork: setsockopt: cast optval to void* 2021-09-09 12:14:32 +03:00
Lion Kortlepel
a311d58e11 TNetwork: reuseaddr instead of reuseport 2021-09-09 12:14:32 +03:00
Lion Kortlepel
1444d91e7e Common: missed semicolon 2021-09-09 12:14:32 +03:00
Lion Kortlepel
1e2f060107 improve error reporting, remove duplicate code 2021-09-09 12:14:32 +03:00
Lion Kortlepel
fdb5da2ed6 CMake: remove mentions of luasocket again 2021-09-09 12:14:32 +03:00
Lion Kortlepel
de57613326 remove luasocket-cmake 2021-09-09 12:14:32 +03:00
Lion Kortlepel
b49782e8a3 Common: Add sstream include for std::stringstream 2021-09-09 12:14:32 +03:00
Lion Kortlepel
ff80b4cf63 CMake: include luasocket after finding lua 2021-09-09 12:14:32 +03:00
Lion Kortlepel
4c23b78f84 add luasocket 2021-09-09 12:14:32 +03:00
Lion Kortlepel
aca3c52c20 remove luasocket again 2021-09-09 12:14:32 +03:00
Lion Kortlepel
51d6c4fb0a add CMakeLists for lib/ 2021-09-09 12:14:32 +03:00
Lion Kortlepel
2af9491fd6 add luasocket submodule 2021-09-09 12:14:32 +03:00
Lion Kortlepel
95c036836e add ws2tcpip.h 2021-09-09 12:14:32 +03:00
Lion Kortlepel
a7f2f85e45 fix version printing 2021-09-09 12:14:32 +03:00
Lion Kortlepel
42c5aaad5a use inet_ntop instead of inet_ntoa (STILL BROKEN THOUGH) 2021-09-09 12:14:32 +03:00
Lion Kortlepel
88684bd9af clarify installation 2021-09-09 12:14:32 +03:00
Lion Kortlepel
c6457f7df4 Add Settings enum, better print 2021-09-09 12:14:32 +03:00
Lion Kortlepel
ba3fd0e144 add GetServerVersion 2021-09-09 12:14:30 +03:00
Lion Kortlepel
e3b6fd7998 use fake version for lua update for now 2021-09-09 12:13:45 +03:00
Lion Kortlepel
943159cd40 Lua: add onShutdown 2021-09-09 12:13:37 +03:00
Lion Kortlepel
9423831937 add ip to identifiers, changed value format 2021-09-09 12:13:34 +03:00
Lion Kortlepel
95188042c5 fix luatable in GetPlayerIdentifiers 2021-09-09 12:12:54 +03:00
Lion Kortlepel
a0a7b8ecce fix comment 2021-09-09 12:12:54 +03:00
Lion Kortlepel
53617abae4 Add printRaw
Same as print() but does not prefix with time, date, filename, etc.
Use with care.
2021-09-09 12:12:54 +03:00
Lion Kortlepel
853b078124 add MP.HttpsGET, MP.HttpsPOST 2021-09-09 12:12:54 +03:00
Anonymous-275
549517c518 TODO edit 2021-09-09 12:12:54 +03:00
Anonymous-275
2be4b8fd91 Fully working lua_Register 2021-09-09 12:12:54 +03:00
Lion Kortlepel
2cfb27820a switch to toml11
it's better, believe me
2021-09-09 12:12:54 +03:00
Anonymous-275
1ff12cb2bf simpler lua_Register 2021-09-09 12:12:54 +03:00
Lion Kortlepel
518cb0664e rebase 2021-09-09 12:12:54 +03:00
Lion Kortlepel
80432eb718 implement GetOSName, start working on HttpsPOST 2021-09-09 12:12:54 +03:00
Anonymous-275
b1caf5c29a lua Register 2021-09-09 12:12:54 +03:00
Lion Kortlepel
950cee9fd0 README: fix git submodule update command 2021-09-09 12:12:54 +03:00
Lion Kortlepel
046097579e README: ensure that submodules are initialized recursively 2021-09-09 12:12:53 +03:00
Lion Kortlepel
cacdc004da Sentry: remove url length print 2021-09-08 19:55:03 +03:00
Lion Kortlepel
8250d5876f bump to 2.3.1, fix sentry
Fix sentry url length print

remove quotes

github actions is cursed

add debug print

test action

Dont use curl on windows

I dont know why the windows build doesnt report to sentry, so ill try
this.

Change timeout to 20 minutes instead of 5

this is a hacky workaround anyways, so i really dont see why it should
only be 5. 5 is barely enough.

temporarily enable debug mode on sentry

CMake: Use breakpad on windows instead of crashpad

CMake: Sentry: use inproc backend

Since cmake refuses to set my variables, I will do it this way.

I am so tired of this github workflow garbage

Sentry: disable debug again, set
sentry_options_set_symbolize_stacktraces to true, fix memory leak

Sentry: hotfix: dont free options

somehow that causes it to crash, and i cannot be bothered to find out
why right now
2021-09-08 19:55:03 +03:00
Lion Kortlepel
a7b02c459e Actions: update release action 2021-09-08 19:55:03 +03:00
Lion Kortlepel
0d5ef404f6 Sentry: ensure user is set up before version check 2021-09-08 19:55:03 +03:00
Lion Kortlepel
07cf7d7c21 Actions: Replace " with ' might fix the sentry issue 2021-09-08 19:55:03 +03:00
Lion Kortlepel
809a851c71 add trace() as DEBUG debug()
Replace DEBUG debug() with trace() everywhere
2021-09-08 19:55:03 +03:00
Lion Kortlepel
fe36191baf fix github actions
Possible fix for sentry url not showing up in windows build

possible fix for sentry, again

add static_assert in attempt to fix issue with sentry

use target_compile_definitions instead of add_compile_definitions
2021-09-08 19:55:03 +03:00
Lion Kortlepel
7d137eb496 Common: Make threadNameMap static
good practice
2021-09-08 19:55:03 +03:00
Lion Kortlepel
fd6234bd21 Minor fixes 2021-09-08 19:55:03 +03:00
Lion Kortlepel
9f0b057c14 TNetwork: Fix crash when auth response is not a JSON object 2021-09-08 19:55:03 +03:00
Lion Kortlepel
0143748953 TLuaFile: Remove RegisterThread calls since it overrides thread names
This is due to the horrible design of TLuaFile. Everything may be called
at any time from any thread. FIXME.
2021-09-08 19:55:03 +03:00
Lion Kortlepel
8ec90d5186 Add Defer<FnT> type to defer actions to the end of scope. 2021-09-08 19:55:03 +03:00
Lion Kortlepel
d054214b7f Various fixes 2021-09-08 19:55:03 +03:00
Lion Kortlepel
003a8269aa Fix url 2021-09-08 19:55:03 +03:00
Lion Kortlepel
59b1b45625 Sentry: don't report id=authkey unless it's likely to be valid 2021-09-08 19:55:03 +03:00
Lion Kortlepel
15e5cee166 Common: fix compile error 2021-09-08 19:55:03 +03:00
Lion Kortlepel
12123582ad add non-working GET for version 2021-09-08 19:55:03 +03:00
Lion Kortlepel
3fb227e468 TNetwork: Fix crash on wrong backend response 2021-09-08 19:55:03 +03:00
Lion Kortlepel
31e9004011 CMakeLists: Win32: Set runtime linking mode before compiling sentry
in an attempt to fix windows compile issue
2021-09-08 19:55:03 +03:00
Lion Kortlepel
f98c8dabb0 Http: add Status::ToString method, use to report errors to sentry for custom fingerprint 2021-09-08 19:55:03 +03:00
Lion Kortlepel
e8665bfb72 CMakeLists: build runtime static on msvc 2021-09-08 19:55:03 +03:00
Lion Kortlepel
3d13381abd CMakeLists: build sentry static 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5352e4ff03 CMakeLists: link against sentry on windows 2021-09-08 19:55:03 +03:00
Lion Kortlepel
c571e218c7 Compat: add back types 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5725717e29 possible windows compiler fix, again 2021-09-08 19:55:03 +03:00
Lion Kortlepel
7f5447f25e Sentry: add debug prints for hard-debug mode 2021-09-08 19:55:03 +03:00
Lion Kortlepel
b33b396089 Sentry: add debug, remove wrong exception to sentry logging 2021-09-08 19:55:03 +03:00
Lion Kortlepel
ff3cbebac0 Sentry: more macro replacements 2021-09-08 19:55:03 +03:00
Lion Kortlepel
0f9a994c10 Sentry: Fix compile error in AssertNotReachable, release version 2021-09-08 19:55:03 +03:00
Lion Kortlepel
c4b72be50a debug prints 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5a3140c84a THeartbeatThread: fix error reporting 2021-09-08 19:55:03 +03:00
Lion Kortlepel
bea8006a26 Sentry: reword 2021-09-08 19:55:03 +03:00
Lion Kortlepel
a2dc42c5f5 THeartbeatThread: fix missing response code 2021-09-08 19:55:03 +03:00
Lion Kortlepel
4b92532203 Sentry: sort by response code 2021-09-08 19:55:03 +03:00
Lion Kortlepel
683e13a4a0 CustomAssert: fix build error in release mode 2021-09-08 19:55:03 +03:00
Lion Kortlepel
9d6dbefb9d Sentry: add request headers 2021-09-08 19:55:03 +03:00
Lion Kortlepel
981b56b846 CustomAssert: fix macro in release being borked 2021-09-08 19:55:03 +03:00
Lion Kortlepel
9f52ab2e54 Senty: add threadname to context 2021-09-08 19:55:03 +03:00
Lion Kortlepel
8fada3ac04 Sentry: add multiple more logging mechanisms, add [CHAT] 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5330013dc3 possible compiler fix by not using SD_BOTH 2021-09-08 19:55:03 +03:00
Lion Kortlepel
c7e0461a86 possible compiler fix 2021-09-08 19:55:03 +03:00
Lion Kortlepel
032c1daf30 possible fix for windows compile issue 2021-09-08 19:55:03 +03:00
Lion Kortlepel
d1efebe068 handle Rc == "0" case, add sentry info event 2021-09-08 19:55:03 +03:00
Lion Kortlepel
c77e2b3fd8 possible fix for windows build 2 2021-09-08 19:55:03 +03:00
Lion Kortlepel
e92847e628 possible windows compiler fix 2021-09-08 19:55:03 +03:00
Lion Kortlepel
afb18ccff7 THeartbeatThread: fix isAuth being in the wrong scope 2021-09-08 19:55:03 +03:00
Lion Kortlepel
4659a9362d Fix missing semi
didnt do this in a while, wow
2021-09-08 19:55:03 +03:00
Lion Kortlepel
fe6e1e6266 Heartbeat: Try backup1 and backup2, refactor sentry reporting 2021-09-08 19:55:03 +03:00
Lion Kortlepel
c0faff5b05 THeartbeatThread: remove second try to heartbeat url 2021-09-08 19:55:03 +03:00
Lion Kortlepel
f4ffa2cdda Sentry: remove authkey, use id instead 2021-09-08 19:55:03 +03:00
Lion Kortlepel
1409d4ef80 Sentry: use locked contexts to send data to avoid races 2021-09-08 19:55:03 +03:00
Lion Kortlepel
51e662fdda Compat: fix clash with socket() macro, Sentry: Add LogDebug 2021-09-08 19:55:03 +03:00
Lion Kortlepel
72950fdab2 CMakeLists: attempt to use curl on windows instead of winhttp for sentry transport 2021-09-08 19:55:03 +03:00
Lion Kortlepel
b9f594896a Sentry: setup user after config init so that we can sent the authkey 2021-09-08 19:55:03 +03:00
Lion Kortlepel
8551e56e42 Sentry: users: add authkey, username 2021-09-08 19:55:03 +03:00
Lion Kortlepel
f4fc182d5e movre TSentry include up, possibly fixing windows actions issue with
macros

im really starting to appreciate that windows API includes clash with
each other because of macros, its super fun
2021-09-08 19:55:03 +03:00
Lion Kortlepel
ee1e948a65 Sentry: remove IP from user data 2021-09-08 19:55:03 +03:00
Lion Kortlepel
e3081a971e CMakeLists, Actions: Introduce env secret url 2021-09-08 19:55:03 +03:00
Lion Kortlepel
7c5bf9473e CMakeLists: fix include paths 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5a44a8e9c5 Sentry: init before setting user 2021-09-08 19:55:03 +03:00
Lion Kortlepel
d1a0eaffab Sentry: move print as it was blocking (?) 2021-09-08 19:55:03 +03:00
Lion Kortlepel
10322bf24e THeartbeatThread: Use Target var in both places 2021-09-08 19:55:03 +03:00
Lion Kortlepel
969cd93358 rename Sentry to TSentry
windows filenames arent case-sensitive so it doesnt know which
sentry.h we mean.
2021-09-08 19:55:03 +03:00
Lion Kortlepel
b3a8b1a682 CMakeLists: move sentry back in, add C to languages 2021-09-08 19:55:03 +03:00
Lion Kortlepel
2774a73d83 Sentry: move welcome message to after version print 2021-09-08 19:55:03 +03:00
Lion Kortlepel
739eaad199 Sentry: print welcome string on startup 2021-09-08 19:55:03 +03:00
Lion Kortlepel
b53b72d604 Sentry: Build sentry before starting project definition
since Sentry is C and we're CXX
2021-09-08 19:55:03 +03:00
Lion Kortlepel
85fd9e9ee3 Sentry: add loads more information 2021-09-08 19:55:03 +03:00
Lion Kortlepel
57e6e98423 Sentry: discern between auth.* and backend.* errors
TNetwork: error for CheckBytes is now warn
2021-09-08 19:55:03 +03:00
Lion Kortlepel
e3c6bdb50d add changelog file 2021-09-08 19:55:03 +03:00
Lion Kortlepel
1f89b202b4 git: properly remove curl (woops) 2021-09-08 19:55:03 +03:00
Lion Kortlepel
0a31107e56 Sentry: add x-upstream 2021-09-08 19:55:03 +03:00
Lion Kortlepel
9237f0dd43 Http: add debug print 2021-09-08 19:55:03 +03:00
Lion Kortlepel
ce834a634c Actions: add curl 2021-09-08 19:55:03 +03:00
Lion Kortlepel
f65607cb00 README: update to include new dependency 2021-09-08 19:55:03 +03:00
Lion Kortlepel
b0475f262f remove curl submodule, add it as an external dependency 2021-09-08 19:55:03 +03:00
Lion Kortlepel
cee824ad46 Actions: possible fix for the cmake build error C/CXX issue on GH actions 2021-09-08 19:55:03 +03:00
Lion Kortlepel
530e977d9d README: fix git submodule update command 2021-09-08 19:55:03 +03:00
Lion Kortlepel
807cb68f0f README: ensure that submodules are initialized recursively 2021-09-08 19:55:03 +03:00
Lion Kortlepel
3850740ded Actions: checkout submodules recursively 2021-09-08 19:55:03 +03:00
Lion Kortlepel
cfc7b302fe fix include path, libcurl library path 2021-09-08 19:55:03 +03:00
Lion Kortlepel
a7c28a8d0d fix curl, print segfault 2021-09-08 19:55:03 +03:00
Lion Kortlepel
9fe1a94d42 CURL::libcurl instead of libcurl 2021-09-08 19:55:03 +03:00
Lion Kortlepel
1eee56a666 Maybe curl works now? 2021-09-08 19:55:03 +03:00
Lion Kortlepel
28fe6e9634 add curl as in-tree dependency, add sentry ok/not ok notice on startup 2021-09-08 19:55:03 +03:00
Lion Kortlepel
4512f44ea1 bump version to 2.3.0 for next release 2021-09-08 19:55:03 +03:00
Lion Kortlepel
3efd31bc84 add curl to dependencies 2021-09-08 19:55:03 +03:00
Lion Kortlepel
5684134894 Sentry: report any issue with backend.beammp or auth.beammp responses 2021-09-08 19:55:03 +03:00
Lion Kortlepel
e6c97de3c4 CMakeLists: improve documentation, emit warning if no Sentry URL is
supplied
2021-09-08 19:55:03 +03:00
Lion Kortlepel
f550d0bba9 add todo 2021-09-08 19:55:03 +03:00
Lion Kortlepel
2b4fec6d11 Sentry: implement basic exception reporting, error breadcrumbs 2021-09-08 19:55:03 +03:00
Lion Kortlepel
550c658ac5 update gitignore 2021-09-08 19:55:03 +03:00
Lion Kortlepel
da41862f49 working sentry-native 2021-09-08 19:55:03 +03:00
Lion Kortlepel
d5769ce9be add sentry as submodule 2021-09-08 19:55:03 +03:00
Lion
4bf9244005 Update README.md 2021-08-30 18:42:48 +03:00
Lion
89f63024ab Update README.md 2021-08-30 18:42:48 +03:00
Lion
f258678751 Update README.md 2021-08-30 18:42:48 +03:00
Lion
4d2f68068d Add issue templates 2021-08-02 16:23:42 +03:00
Anonymous-275
9f636345ef Removed abort that causes server crash 2021-08-01 15:42:23 +03:00
Lion Kortlepel
3d0d5e9e4c minor fixes, version bump 2021-07-31 21:57:06 +03:00
Lion Kortlepel
a1ca8e0576 bump vcpkg version 2021-07-31 12:34:25 +03:00
Lion Kortlepel
b22f21a6b2 Revert "remove vcpkg commit id"
This reverts commit fef069c9df72a0473c6eed6b767199633327e4db.
2021-07-31 12:34:25 +03:00
Lion Kortlepel
531a901431 remove vcpkg commit id
there's no need for it and it makes runs fail if not updated
2021-07-31 12:34:25 +03:00
Lion Kortlepel
46f778bd01 bump version to 2.1.4 2021-07-29 13:34:28 +02:00
Lion Kortlepel
f3b6eea418 update vcpkg 2021-07-29 13:34:28 +02:00
Lion Kortlepel
bceb3aefe4 TServer: Avoid blindly using std::string::find's result 2021-07-29 13:34:28 +02:00
Lion Kortlepel
6c72432992 possible fix #37 2021-07-29 13:34:28 +02:00
Lion
17d3f303ca Update README.md 2021-07-21 00:18:36 +02:00
Lion
2cbcf96282 Readme: clarify build instructions are for linux, too 2021-07-18 21:28:58 +02:00
Lion Kortlepel
7d4fd44dbf print heartbeat on heartbeat 2021-07-15 01:04:06 +02:00
Lion Kortlepel
71c2af1224 TNetwork: kick everyone before shutdown
in the future this can be used to show a message to the clients that the
server is shutting down
2021-07-11 20:27:35 +02:00
Lion Kortlepel
2e112fc5f1 fix typo that prevented TCP thread from shutting down properly
sometimes, i guess
2021-07-11 20:00:31 +02:00
Lion
96c93a6aa6 Update README.md
Remove unnecessary and wrong step 4 :)
2021-07-11 00:30:14 +02:00
Lion
9dbb91fd84 Update README.md 2021-07-11 00:29:27 +02:00
Lion
26c33ae2fb README: Add clarification on build steps 2021-07-11 00:28:21 +02:00
Lion Kortlepel
3eb943309e bump version number 2021-07-10 19:20:42 +02:00
Lion Kortlepel
3c8e8399cb Fix issue with not cancelling events on linux (fix #29) 2021-07-04 00:10:10 +02:00
Lion Kortlepel
5b500a3da5 bump version number 2021-07-03 01:04:55 +02:00
Lion Kortlepel
ade19123b7 change default map to new gridmap 2021-07-03 01:01:56 +02:00
Lion Kortlepel
5ee18e0576 bump in-server version number 2021-07-02 00:01:58 +02:00
Lion
77d23caa63 Update README.md 2021-06-28 11:39:12 +02:00
Lion
79856cde8e Update README.md 2021-06-28 11:38:59 +02:00
42 changed files with 1434 additions and 696 deletions

29
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug] Enter issue title here"
labels: bug
assignees: ''
---
**Fill out general information**
OS (windows, linux, ...):
BeamMP-Server Version:
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Do x ...
2. Do y ...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Logs**
Please attach the `Server.log` from the run in which the issue appeared, preferably with Debug turned on in the `ServerConfig.toml`.
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature Request]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. For example: "I'm always frustrated when ...".
**Describe the solution you'd like**
A clear and concise description of what you want to happen. Also supply OS information if relevant, for example "*On Linux*, I would like to be able to...".
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context about the feature request here.

View File

@@ -12,12 +12,15 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
submodules: 'recursive'
- name: Install Dependencies
env:
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
run: |
echo ${#beammp_sentry_url}
sudo apt-get update
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get install -y libboost1.70-dev libboost1.70
@@ -27,7 +30,9 @@ jobs:
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-linux
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
env:
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
- name: Build
working-directory: ${{github.workspace}}/build-linux

View File

@@ -12,15 +12,15 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
submodules: 'recursive'
- name: Restore artifacts, or run vcpkg, build and cache artifacts
uses: lukka/run-vcpkg@main
id: runvcpkg
with:
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment
@@ -29,7 +29,9 @@ jobs:
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-windows
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
env:
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
- name: Build
working-directory: ${{github.workspace}}/build-windows

View File

@@ -37,12 +37,12 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
submodules: 'recursive'
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev
sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get install -y libboost1.70-dev libboost1.70
@@ -52,14 +52,15 @@ jobs:
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-linux
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10
env:
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=g++-10 -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
- name: Build
working-directory: ${{github.workspace}}/build-linux
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
@@ -78,15 +79,15 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
submodules: 'recursive'
- name: Restore artifacts, or run vcpkg, build and cache artifacts
uses: lukka/run-vcpkg@main
id: runvcpkg
with:
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment
@@ -95,7 +96,9 @@ jobs:
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-windows
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
env:
beammp_sentry_url: ${{ secrets.BEAMMP_SECRET_SENTRY_URL }}
run: cmake $GITHUB_WORKSPACE -DSENTRY_BACKEND=breakpad -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBEAMMP_SECRET_SENTRY_URL="$beammp_sentry_url"
- name: Build
working-directory: ${{github.workspace}}/build-windows

3
.gitignore vendored
View File

@@ -1,7 +1,10 @@
.idea/
.sentry-native/
*.orig
*.toml
boost_*
Resources
run-in-env.sh
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##

12
.gitmodules vendored
View File

@@ -1,15 +1,15 @@
[submodule "include/commandline"]
path = include/commandline
url = https://github.com/lionkor/commandline
[submodule "socket.io-client-cpp"]
path = socket.io-client-cpp
url = https://github.com/socketio/socket.io-client-cpp
[submodule "asio"]
path = asio
url = https://github.com/chriskohlhoff/asio
[submodule "rapidjson"]
path = rapidjson
url = https://github.com/Tencent/rapidjson
[submodule "include/tomlplusplus"]
path = include/tomlplusplus
url = https://github.com/marzer/tomlplusplus
[submodule "include/toml11"]
path = include/toml11
url = https://github.com/ToruNiina/toml11
[submodule "include/sentry-native"]
path = include/sentry-native
url = https://github.com/getsentry/sentry-native

View File

@@ -1,10 +1,36 @@
cmake_minimum_required(VERSION 3.0)
project(Server)
message(STATUS "You can find build instructions and a list of dependencies in the README at \
https://github.com/BeamMP/BeamMP-Server")
project(BeamMP-Server
DESCRIPTION "Server for BeamMP - The Multiplayer Mod for BeamNG.drive"
HOMEPAGE_URL https://beammp.com
LANGUAGES CXX C)
if (WIN32)
# this has to happen before sentry, so that crashpad on windows links with these settings.
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif()
include_directories("include/sentry-native/include")
set(SENTRY_BUILD_SHARED_LIBS OFF)
if (MSVC)
set(SENTRY_BUILD_RUNTIMESTATIC ON)
endif()
set(SENTRY_BACKEND breakpad)
add_subdirectory("include/sentry-native")
message(STATUS "Setting compiler flags")
if (WIN32)
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
include_directories(${VcpkgRoot}/include)
@@ -15,21 +41,36 @@ elseif (UNIX)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
if (SANITIZE)
message(STATUS "sanitize is ON")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,thread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLanAGS} -fsanitize=undefined,thread")
endif (SANITIZE)
endif ()
message(STATUS "Checking for Sentry URL")
# this is set by the build system.
# IMPORTANT: if you're building from source, just leave this empty
if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL)
message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \
This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.")
set(BEAMMP_SECRET_SENTRY_URL "")
else()
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
message(STATUS "Sentry URL is length ${URL_LEN}")
endif()
message(STATUS "Adding local source dependencies")
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
include_directories("asio/asio/include")
include_directories("rapidjson/include")
include_directories("websocketpp")
add_subdirectory("socket.io-client-cpp")
add_subdirectory("include/commandline")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
message(STATUS "Looking for Boost")
find_package(Boost REQUIRED COMPONENTS system thread)
add_executable(BeamMP-Server
@@ -46,23 +87,55 @@ add_executable(BeamMP-Server
include/TResourceManager.h src/TResourceManager.cpp
include/THeartbeatThread.h src/THeartbeatThread.cpp
include/Http.h src/Http.cpp
#include/SocketIO.h src/SocketIO.cpp
include/TSentry.h src/TSentry.cpp
include/TPPSMonitor.h src/TPPSMonitor.cpp
include/TNetwork.h src/TNetwork.cpp)
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}")
target_include_directories(BeamMP-Server PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/commandline")
message(STATUS "Looking for Lua")
find_package(Lua REQUIRED)
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src" "include/tomlplusplus")
target_include_directories(BeamMP-Server PUBLIC
${Boost_INCLUDE_DIRS}
${LUA_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
"include/tomlplusplus"
"include/sentry-native/include"
"include/curl/include")
message(STATUS "Looking for SSL")
find_package(OpenSSL REQUIRED)
message(STATUS "CURL IS ${CURL_LIBRARIES}")
if (UNIX)
target_link_libraries(BeamMP-Server z pthread stdc++fs ${LUA_LIBRARIES} crypto ${OPENSSL_LIBRARIES} commandline sioclient_tls)
target_link_libraries(BeamMP-Server
z
pthread
stdc++fs
${LUA_LIBRARIES}
crypto
${OPENSSL_LIBRARIES}
commandline
sentry)
elseif (WIN32)
include(FindLua)
message(STATUS "Looking for libz")
find_package(ZLIB REQUIRED)
message(STATUS "Looking for RapidJSON")
find_package(RapidJSON CONFIG REQUIRED)
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls)
target_link_libraries(BeamMP-Server PRIVATE
ws2_32
ZLIB::ZLIB
${LUA_LIBRARIES}
${OPENSSL_LIBRARIES}
commandline
sentry)
endif ()

65
Changelog.md Normal file
View File

@@ -0,0 +1,65 @@
# v2.3.0
- ADDED version check - the server will now let you know when a new release is out
- ADDED logging of various errors, crashes and exceptions to the backend
- ADDED chat messages are now logged to the server console as [CHAT]
- ADDED debug message telling you when the server heartbeats to the backend
- REMOVED various [DEBUG] messages which were confusing (such as "breaking client loop")
- FIXED various crashes and issues with handling unexpected backend responses
- FIXED minor bugs due to code correctness
# v2.2.0
- FIXED major security flaw
- FIXED minor bugs
# v2.1.4
- ADDED debug heartbeat print
- ADDED kicking every player before shutdown
- FIXED rare bug which led to violent crash
- FIXED minor bugs
# v2.1.3
- FIXED Lua events not cancelling properly on Linux
# v2.1.2
- CHANGED default map to gridmap v2
- FIXED version number display
# v2.1.1
# v2.1.0 (pre-v2.1.1)
# v2.0.4 (pre-v2.1.0)
- REMOVED boost as a runtime dependency
- FIXED Lua plugins on Linux
- FIXED console history on Windows
- CHANGED to new config format TOML
# v2.0.3
- WORKAROUND for timeout bug / ghost player bug
- FIXED 100% CPU spin when stdin is /dev/null.
# v2.0.2
- ADDED fully new commandline
- ADDED new backend
- ADDED automated build system
- ADDED lua GetPlayerIdentifiers
- ADDED lots of debug info
- ADDED better POSTing and GETing
- ADDED a license
- FIXED ghost players in player list issue
- FIXED ghost vehicle after joining issue
- FIXED missing vehicle after joining issue
- FIXED a lot of desync issues
- FIXED some memory leaks
- FIXED various crashes
- FIXED various data-races
- FIXED some linux-specific crashes
- FIXED some linux-specific issues
- FIXED bug which caused kicking to be logged as leaving
- FIXED various internal developer quality-of-life things

View File

@@ -11,7 +11,7 @@ The server is the point throug which all clients communicate. You can write lua
These values are guesstimated and are subject to change with each release.
* RAM: 50+ MiB usable (not counting OS overhead)
* CPU: Any Hz, preferably multicore
* CPU: >1GHz, preferably multicore
* OS: Windows, Linux (theoretically any POSIX)
* GPU: None
* HDD: 10 MiB + Mods/Plugins
@@ -61,26 +61,34 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
- `make`
- `cmake`
- `g++`
- `libcurl4-openssl-dev` or similar (search for `libcurl` and look for one with `-dev`)
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
- `liblua5.3`
- `liblua5.3-dev`
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
You can also use any version of `libluajit`, but the same applies regarding the version.
- `libz-dev`
- `rapidjson-dev`
- `libopenssl-dev`
- `libopenssl-dev` or `libssl-dev`
**If** you're building it from source, you'll need `libboost1.70-dev` as well.
**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
If you can't find this version of boost (only 1.6x, for example), you can either update to a newer version of your distro, build boost yourself, or use an unstable rolling release (like Debian `sid` aka `unstable`).
In the end you should end up with a command something like this:
```sh
sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev libboost1.71-all-dev
```
### How to build
On windows. use git-bash for these commands.
On windows, use git-bash for these commands. On Linux, these should work in your shell.
1. Make sure you have all [prerequisites](#prerequisites) installed
2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server`
3. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20.
4. `cd` into it with `cd BeamMP-Server`
2. Clone the repository in a location of your choice with `git clone --recurse-submodules https://github.com/BeamMP/BeamMP-Server`.
3. Ensure that all submodules are initialized by running `git submodule update --init --recursive`. Then change into the cloned directory by running `cd BeamMP-Server`.
4. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
5. Run `cmake .` (with `.`)
6. Run `make`
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.

View File

@@ -12,6 +12,17 @@
class TServer;
#ifdef WIN32
// for socklen_t
#include <WS2tcpip.h>
#endif // WIN32
struct TConnection final {
SOCKET Socket;
struct sockaddr SockAddr;
socklen_t SockAddrLen;
};
class TClient final {
public:
using TSetOfVehicleData = std::vector<TVehicleData>;
@@ -30,7 +41,7 @@ public:
TVehicleDataLockPair GetAllCars();
void SetName(const std::string& Name) { mName = Name; }
void SetRoles(const std::string& Role) { mRole = Role; }
void AddIdentifier(const std::string& ID) { mIdentifiers.insert(ID); };
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
std::string GetCarData(int Ident);
void SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
@@ -38,7 +49,7 @@ public:
void SetStatus(int Status) { mStatus = Status; }
// locks
void DeleteCar(int Ident);
[[nodiscard]] std::set<std::string> GetIdentifiers() const { return mIdentifiers; }
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
[[nodiscard]] sockaddr_in GetUDPAddr() const { return mUDPAddress; }
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
@@ -78,7 +89,7 @@ private:
bool mIsSyncing = false;
mutable std::mutex mMissedPacketsMutex;
std::queue<std::string> mPacketsSync;
std::set<std::string> mIdentifiers;
std::unordered_map<std::string, std::string> mIdentifiers;
bool mIsGuest = false;
std::mutex mVehicleDataMutex;
TSetOfVehicleData mVehicleData;

View File

@@ -1,13 +1,30 @@
#pragma once
#include "TSentry.h"
extern TSentry Sentry;
#include <array>
#include <atomic>
#include <cstring>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <sstream>
#include "Compat.h"
#include "TConsole.h"
struct Version {
uint8_t major;
uint8_t minor;
uint8_t patch;
Version(uint8_t major, uint8_t minor, uint8_t patch);
Version(const std::array<uint8_t, 3>& v);
std::string AsString();
};
// static class handling application start, shutdown, etc.
// yes, static classes, singletons, globals are all pretty
// bad idioms. In this case we need a central way to access
@@ -21,7 +38,7 @@ public:
: ServerName("BeamMP Server")
, ServerDesc("BeamMP Default Description")
, Resource("Resources")
, MapName("/levels/gridmap/info.json")
, MapName("/levels/gridmap_v2/info.json")
, MaxPlayers(10)
, Private(false)
, MaxCars(1)
@@ -40,6 +57,7 @@ public:
std::string CustomIP;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
};
using TShutdownHandler = std::function<void()>;
// methods
@@ -50,26 +68,34 @@ public:
// Causes all threads to finish up and exit gracefull gracefully
static void GracefullyShutdown();
static TConsole& Console() { return *mConsole; }
static std::string ServerVersion() { return "2.1.1"; }
static std::string ClientVersion() { return "2.0"; }
static std::string ServerVersionString();
static const Version& ServerVersion() { return mVersion; }
static std::string ClientVersionString() { return "2.0"; }
static std::string PPS() { return mPPS; }
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
static inline TSettings Settings {};
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
static std::string GetBackendHostname() { return "backend.beammp.com"; }
static std::string GetBackup1Hostname() { return "backup1.beammp.com"; }
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
static void CheckForUpdates();
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
static bool IsOutdated(const Version& Current, const Version& Newest);
private:
static inline std::string mPPS;
static std::unique_ptr<TConsole> mConsole;
static inline std::mutex mShutdownHandlersMutex {};
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
static inline Version mVersion { 2, 4, 0 };
};
std::string ThreadName();
void RegisterThread(const std::string str);
std::string ThreadName(bool DebugModeOverride = false);
void RegisterThread(const std::string& str);
#define RegisterThreadAuto() RegisterThread(__func__)
#define KB 1024
@@ -108,7 +134,11 @@ void RegisterThread(const std::string str);
#define warn(x) Application::Console().Write(_this_location + std::string("[WARN] ") + (x))
#define info(x) Application::Console().Write(_this_location + std::string("[INFO] ") + (x))
#define error(x) Application::Console().Write(_this_location + std::string("[ERROR] ") + (x))
#define error(x) \
do { \
Application::Console().Write(_this_location + std::string("[ERROR] ") + (x)); \
Sentry.AddErrorBreadcrumb((x), _file_basename, _line); \
} while (false)
#define luaprint(x) Application::Console().Write(_this_location + std::string("[LUA] ") + (x))
#define debug(x) \
do { \
@@ -116,8 +146,22 @@ void RegisterThread(const std::string str);
Application::Console().Write(_this_location + std::string("[DEBUG] ") + (x)); \
} \
} while (false)
// trace() is a debug-build debug()
#if defined(DEBUG)
#define trace(x) \
do { \
if (Application::Settings.DebugModeEnabled) { \
Application::Console().Write(_this_location + std::string("[TRACE] ") + (x)); \
} \
} while (false)
#else
#define trace(x)
#endif // defined(DEBUG)
void LogChatMessage(const std::string& name, int id, const std::string& msg);
#define Biggest 30000
std::string Comp(std::string Data);
std::string DeComp(std::string Compressed);
std::string GetPlatformAgnosticErrorString();

View File

@@ -7,14 +7,15 @@
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
using SOCKET = int;
using DWORD = unsigned long;
using PDWORD = unsigned long*;
using LPDWORD = unsigned long*;
char _getch();
inline void CloseSocketProper(int socket) {
shutdown(socket, SHUT_RDWR);
close(socket);
inline void CloseSocketProper(int TheSocket) {
shutdown(TheSocket, SHUT_RDWR);
close(TheSocket);
}
#endif // unix
@@ -23,9 +24,10 @@ inline void CloseSocketProper(int socket) {
#ifdef WIN32
#include <conio.h>
#include <winsock2.h>
inline void CloseSocketProper(SOCKET socket) {
shutdown(socket, SD_BOTH);
closesocket(socket);
inline void CloseSocketProper(SOCKET TheSocket) {
shutdown(TheSocket, 2); // 2 == SD_BOTH
closesocket(TheSocket);
}
#endif // WIN32
@@ -33,4 +35,4 @@ inline void CloseSocketProper(SOCKET socket) {
#if !defined(WIN32) && !defined(__unix)
#error "OS not supported"
#endif
#endif

View File

@@ -40,7 +40,7 @@ static const char* const ANSI_WHITE_BOLD = "\u001b[37;1m";
static const char* const ANSI_BOLD = "\u001b[1m";
static const char* const ANSI_UNDERLINE = "\u001b[4m";
#if DEBUG
#ifdef DEBUG
#include <iostream>
inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line,
[[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) {
@@ -59,10 +59,15 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
#else
// In release build, these macros turn into NOPs. The compiler will optimize these out.
#define Assert(x) \
do { \
#define Assert(cond) \
do { \
bool result = (cond); \
if (!result) { \
Sentry.LogAssert(#cond, _file_basename, _line, __func__); \
} \
} while (false)
#define AssertNotReachable() \
do { \
#define AssertNotReachable() \
do { \
Sentry.LogAssert("code is unreachable", _file_basename, _line, __func__); \
} while (false)
#endif // DEBUG

18
include/Defer.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <functional>
template <typename FnT>
class Defer final {
public:
Defer(FnT fn)
: mFunction([&fn] { (void)fn(); }) { }
~Defer() {
if (mFunction) {
mFunction();
}
}
private:
std::function<void()> mFunction;
};

View File

@@ -4,6 +4,10 @@
#include <unordered_map>
namespace Http {
std::string GET(const std::string& host, int port, const std::string& target);
std::string POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);
}
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr);
namespace Status {
std::string ToString(int code);
}
const std::string ErrorString = "-1";
}

View File

@@ -1,69 +0,0 @@
#pragma once
#include <atomic>
#include <deque>
#include <mutex>
#include <sio_client.h>
#include <thread>
#include <memory>
/*
* We send relevant server events over socket.io to the backend.
*
* We send all events to `backend.beammp.com`, to the room `/key`
* where `key` is the currently active auth-key.
*/
enum class SocketIOEvent {
ConsoleOut,
CPUUsage,
MemoryUsage,
NetworkUsage,
PlayerList,
};
enum class SocketIORoom {
None,
Stats,
Player,
Info,
Console,
};
class SocketIO final {
private:
struct Event;
public:
enum class EventType {
};
// Singleton pattern
static SocketIO& Get();
void Emit(SocketIOEvent Event, const std::string& Data);
~SocketIO();
void SetAuthenticated(bool auth) { mAuthenticated = auth; }
private:
SocketIO() noexcept;
void ThreadMain();
struct Event {
std::string Name;
std::string Data;
};
bool mAuthenticated { false };
sio::client mClient;
std::thread mThread;
std::atomic_bool mCloseThread { false };
std::mutex mQueueMutex;
std::deque<Event> mQueue;
friend std::unique_ptr<SocketIO> std::make_unique<SocketIO>();
};

View File

@@ -8,13 +8,15 @@ class TConfig {
public:
explicit TConfig();
bool Failed() const { return mFailed; }
[[nodiscard]] bool Failed() const { return mFailed; }
void FlushToFile();
private:
void CreateConfigFile(std::string_view name);
void ParseFromFile(std::string_view name);
void PrintDebug();
void ParseOldFormat();
bool mFailed { false };

View File

@@ -18,4 +18,4 @@ private:
bool mShutdown = false;
TResourceManager& mResourceManager;
TServer& mServer;
};
};

View File

@@ -4,10 +4,11 @@
#include "IThreaded.h"
#include "TLuaFile.h"
#include "TServer.h"
#include <optional>
#include <lua.hpp>
#include <memory>
#include <optional>
#include <set>
#include <unordered_map>
class TLuaEngine : public IThreaded {
public:
@@ -25,14 +26,16 @@ public:
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
static std::unordered_map<std::string, lua_State*> mGlobals;
private:
void FolderList(const std::string& Path, bool HotSwap);
void RegisterFiles(const std::string& Path, bool HotSwap);
bool NewFile(const std::string& Path);
void RegisterFiles(const fs::path& Path, bool HotSwap);
bool IsNewFile(const std::string& Path);
TNetwork& mNetwork;
TServer& mServer;
std::string mPath;
bool mShutdown { false };
TSetOfLuaFile mLuaFiles;
std::mutex mListMutex;
};

View File

@@ -37,12 +37,13 @@ public:
~TLuaFile();
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
TLuaEngine& Engine() { return mEngine; }
[[nodiscard]] std::string GetPluginName() const;
[[nodiscard]] std::string GetFileName() const;
[[nodiscard]] const lua_State* GetState() const;
[[nodiscard]] bool GetStopThread() const { return mStopThread; }
[[nodiscard]] const TLuaEngine& Engine() const { return mEngine; }
[[nodiscard]] std::string GetRegistered(const std::string& Event) const;
std::string GetPluginPath() const;
std::string GetPluginName() const;
std::string GetFileName() const;
const lua_State* GetState() const;
bool GetStopThread() const { return mStopThread; }
const TLuaEngine& Engine() const { return mEngine; }
std::string GetRegistered(const std::string& Event) const;
private:
TLuaEngine& mEngine;
@@ -54,6 +55,7 @@ private:
bool mStopThread = false;
bool mConsole = false;
void Load();
std::mutex mInitMutex;
};
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);

View File

@@ -4,6 +4,8 @@
#include "TResourceManager.h"
#include "TServer.h"
struct TConnection;
class TNetwork {
public:
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
@@ -15,8 +17,8 @@ public:
std::string TCPRcv(TClient& c);
void ClientKick(TClient& c, const std::string& R);
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
void Identify(SOCKET TCPSock);
void Authentication(SOCKET TCPSock);
void Identify(const TConnection& client);
void Authentication(const TConnection& ClientConnection);
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
void SyncResources(TClient& c);
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data) const;

38
include/TSentry.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef SENTRY_H
#define SENTRY_H
#include <mutex>
#include <string>
#include <unordered_map>
enum class SentryLevel {
Debug = -1,
Info = 0,
Warning = 1,
Error = 2,
Fatal = 3,
};
// singleton, dont make this twice
class TSentry final {
public:
TSentry();
~TSentry();
void PrintWelcome();
void SetupUser();
void Log(SentryLevel level, const std::string& logger, const std::string& text);
void LogError(const std::string& text, const std::string& file, const std::string& line);
void SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map);
void LogException(const std::exception& e, const std::string& file, const std::string& line);
void LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function);
void AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line);
// cleared when Logged
void SetTransaction(const std::string& id);
[[nodiscard]] std::unique_lock<std::mutex> CreateExclusiveContext();
private:
bool mValid { true };
std::mutex mMutex;
};
#endif // SENTRY_H

1
include/sentry-native Submodule

Submodule include/sentry-native added at 521860373d

1
include/toml11 Submodule

Submodule include/toml11 added at 647381020e

View File

@@ -92,7 +92,6 @@ TClient::TClient(TServer& Server)
void TClient::UpdatePingTime() {
mLastPingTime = std::chrono::high_resolution_clock::now();
//debug(GetName() + ": " + std::string("ping time updated!: ") + ((SecondsSinceLastPing() == 0) ? "OK" : "ERR"));
}
int TClient::SecondsSinceLastPing() {
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(

View File

@@ -2,11 +2,17 @@
#include "TConsole.h"
#include <array>
#include <charconv>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <thread>
#include <zlib.h>
#include "CustomAssert.h"
#include "Http.h"
std::unique_ptr<TConsole> Application::mConsole = std::make_unique<TConsole>();
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
@@ -24,6 +30,57 @@ void Application::GracefullyShutdown() {
}
}
std::string Application::ServerVersionString() {
return mVersion.AsString();
}
std::array<uint8_t, 3> Application::VersionStrToInts(const std::string& str) {
std::array<uint8_t, 3> Version;
std::stringstream ss(str);
for (uint8_t& i : Version) {
std::string Part;
std::getline(ss, Part, '.');
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
}
return Version;
}
// FIXME: This should be used by operator< on Version
bool Application::IsOutdated(const Version& Current, const Version& Newest) {
if (Newest.major > Current.major) {
return true;
} else if (Newest.major == Current.major && Newest.minor > Current.minor) {
return true;
} else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) {
return true;
} else {
return false;
}
}
void Application::CheckForUpdates() {
// checks current version against latest version
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
bool Matches = std::regex_match(Response, VersionRegex);
if (Matches) {
auto MyVersion = ServerVersion();
auto RemoteVersion = Version(VersionStrToInts(Response));
if (IsOutdated(MyVersion, RemoteVersion)) {
std::string RealVersionString = RemoteVersion.AsString();
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
} else {
info("Server up-to-date!");
}
} else {
warn("Unable to fetch version from backend.");
trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } });
Sentry.LogError("failed to get server version", _file_basename, _line);
}
}
std::string Comp(std::string Data) {
std::array<char, Biggest> C {};
// obsolete
@@ -70,10 +127,12 @@ std::string DeComp(std::string Compressed) {
// thread name stuff
std::map<std::thread::id, std::string> threadNameMap;
static std::map<std::thread::id, std::string> threadNameMap {};
static std::mutex ThreadNameMapMutex {};
std::string ThreadName() {
if (Application::Settings.DebugModeEnabled) {
std::string ThreadName(bool DebugModeOverride) {
auto Lock = std::unique_lock(ThreadNameMapMutex);
if (DebugModeOverride || Application::Settings.DebugModeEnabled) {
auto id = std::this_thread::get_id();
if (threadNameMap.find(id) != threadNameMap.end()) {
// found
@@ -83,6 +142,60 @@ std::string ThreadName() {
return "";
}
void RegisterThread(const std::string str) {
void RegisterThread(const std::string& str) {
auto Lock = std::unique_lock(ThreadNameMapMutex);
threadNameMap[std::this_thread::get_id()] = str;
}
Version::Version(uint8_t major, uint8_t minor, uint8_t patch)
: major(major)
, minor(minor)
, patch(patch) { }
Version::Version(const std::array<uint8_t, 3>& v)
: Version(v[0], v[1], v[2]) {
}
std::string Version::AsString() {
std::stringstream ss {};
ss << int(major) << "." << int(minor) << "." << int(patch);
return ss.str();
}
void LogChatMessage(const std::string& name, int id, const std::string& msg) {
std::stringstream ss;
ss << "[CHAT] ";
if (id != -1) {
ss << "(" << id << ") <" << name << ">";
} else {
ss << name << "";
}
ss << msg;
}
std::string GetPlatformAgnosticErrorString() {
#ifdef WIN32
// This will provide us with the error code and an error message, all in one.
int err;
char msgbuf[256];
msgbuf[0] = '\0';
err = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msgbuf,
sizeof(msgbuf),
nullptr);
if (*msgbuf) {
return std::to_string(GetLastError()) + " - " + std::string(msgbuf);
} else {
return std::to_string(GetLastError());
}
#else // posix
return std::strerror(errno);
#endif
}

View File

@@ -7,6 +7,7 @@
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <map>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
@@ -14,47 +15,7 @@ namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
std::string Http::GET(const std::string& host, int port, const std::string& target) {
// FIXME: doesn't support https
// if it causes issues, yell at me and I'll fix it asap. - Lion
try {
net::io_context io;
tcp::resolver resolver(io);
beast::tcp_stream stream(io);
auto const results = resolver.resolve(host, std::to_string(port));
stream.connect(results);
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
req.set(http::field::host, host);
// tell the server what we are (boost beast)
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(stream, req);
// used for reading
beast::flat_buffer buffer;
http::response<http::string_body> response;
http::read(stream, buffer, response);
std::string result(response.body());
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
if (ec && ec != beast::errc::not_connected) {
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
}
return result;
} catch (const std::exception& e) {
Application::Console().Write(e.what());
return "-1";
}
}
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
try {
net::io_context io;
@@ -68,7 +29,7 @@ std::string Http::POST(const std::string& host, const std::string& target, const
decltype(resolver)::results_type results;
auto try_connect_with_protocol = [&](tcp protocol) {
try {
results = resolver.resolve(protocol, host, std::to_string(443));
results = resolver.resolve(protocol, host, std::to_string(port));
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
// FIXME: we could throw and crash, if we like
@@ -87,21 +48,18 @@ std::string Http::POST(const std::string& host, const std::string& target, const
//debug("IPv6 connect failed, trying IPv4");
bool ok = try_connect_with_protocol(tcp::v4());
if (!ok) {
//error("failed to resolve or connect in POST " + host + target);
Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target);
return "-1";
}
//}
stream.handshake(ssl::stream_base::client);
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
http::request<http::string_body> req { verb, target, 11 /* http 1.1 */ };
req.set(http::field::host, host);
if (!body.empty()) {
if (json) {
// FIXME: json is untested.
req.set(http::field::content_type, "application/json");
} else {
req.set(http::field::content_type, "application/x-www-form-urlencoded");
}
req.set(http::field::content_type, ContentType); // "application/json"
// "application/x-www-form-urlencoded"
req.set(http::field::content_length, std::to_string(body.size()));
req.body() = body;
// info("body is " + body + " (" + req.body() + ")");
@@ -112,6 +70,16 @@ std::string Http::POST(const std::string& host, const std::string& target, const
req.set(pair.first, pair.second);
}
std::unordered_map<std::string, std::string> request_data;
for (const auto& header : req.base()) {
// need to do explicit casts to convert string_view to string
// since string_view may not be null-terminated (and in fact isn't, here)
std::string KeyString(header.name_string());
std::string ValueString(header.value());
request_data[KeyString] = ValueString;
}
Sentry.SetContext("https-post-request-data", request_data);
std::stringstream oss;
oss << req;
@@ -125,6 +93,24 @@ std::string Http::POST(const std::string& host, const std::string& target, const
http::read(stream, buffer, response);
std::unordered_map<std::string, std::string> response_data;
response_data["reponse-code"] = std::to_string(response.result_int());
if (status) {
*status = response.result_int();
}
for (const auto& header : response.base()) {
// need to do explicit casts to convert string_view to string
// since string_view may not be null-terminated (and in fact isn't, here)
std::string KeyString(header.name_string());
std::string ValueString(header.value());
response_data[KeyString] = ValueString;
}
Sentry.SetContext("https-post-response-data", response_data);
if (status) {
*status = response.base().result_int();
}
std::stringstream result;
result << response;
@@ -135,11 +121,94 @@ std::string Http::POST(const std::string& host, const std::string& target, const
// info(result.str());
std::string debug_response_str;
std::getline(result, debug_response_str);
//debug("POST " + host + target + ": " + debug_response_str);
return std::string(response.body());
} catch (const std::exception& e) {
Application::Console().Write(e.what());
return "-1";
Application::Console().Write(__func__ + std::string(": ") + e.what());
return Http::ErrorString;
}
}
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
return GenericRequest(http::verb::get, host, port, target, {}, {}, {}, status);
}
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, const std::string& ContentType, unsigned int* status) {
return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status);
}
// RFC 2616, RFC 7231
static std::map<size_t, const char*> Map = {
{ 100, "Continue" },
{ 101, "Switching Protocols" },
{ 102, "Processing" },
{ 103, "Early Hints" },
{ 200, "OK" },
{ 201, "Created" },
{ 202, "Accepted" },
{ 203, "Non-Authoritative Information" },
{ 204, "No Content" },
{ 205, "Reset Content" },
{ 206, "Partial Content" },
{ 207, "Multi-Status" },
{ 208, "Already Reported" },
{ 226, "IM Used" },
{ 300, "Multiple Choices" },
{ 301, "Moved Permanently" },
{ 302, "Found" },
{ 303, "See Other" },
{ 304, "Not Modified" },
{ 305, "Use Proxy" },
{ 306, "(Unused)" },
{ 307, "Temporary Redirect" },
{ 308, "Permanent Redirect" },
{ 400, "Bad Request" },
{ 401, "Unauthorized" },
{ 402, "Payment Required" },
{ 403, "Forbidden" },
{ 404, "Not Found" },
{ 405, "Method Not Allowed" },
{ 406, "Not Acceptable" },
{ 407, "Proxy Authentication Required" },
{ 408, "Request Timeout" },
{ 409, "Conflict" },
{ 410, "Gone" },
{ 411, "Length Required" },
{ 412, "Precondition Failed" },
{ 413, "Payload Too Large" },
{ 414, "URI Too Long" },
{ 415, "Unsupported Media Type" },
{ 416, "Range Not Satisfiable" },
{ 417, "Expectation Failed" },
{ 421, "Misdirected Request" },
{ 422, "Unprocessable Entity" },
{ 423, "Locked" },
{ 424, "Failed Dependency" },
{ 425, "Too Early" },
{ 426, "Upgrade Required" },
{ 428, "Precondition Required" },
{ 429, "Too Many Requests" },
{ 431, "Request Header Fields Too Large" },
{ 451, "Unavailable For Legal Reasons" },
{ 500, "Internal Server Error" },
{ 501, "Not Implemented" },
{ 502, "Bad Gateway" },
{ 503, "Service Unavailable" },
{ 504, "Gateway Timeout" },
{ 505, "HTTP Version Not Supported" },
{ 506, "Variant Also Negotiates" },
{ 507, "Insufficient Storage" },
{ 508, "Loop Detected" },
{ 510, "Not Extended" },
{ 511, "Network Authentication Required" },
};
std::string Http::Status::ToString(int code) {
if (Map.find(code) != Map.end()) {
return Map.at(code);
} else {
return "Unassigned";
}
}

View File

@@ -1,98 +0,0 @@
#include "SocketIO.h"
#include "Common.h"
#include <iostream>
//TODO Default disabled with config option
static std::unique_ptr<SocketIO> SocketIOInstance = std::make_unique<SocketIO>();
SocketIO& SocketIO::Get() {
return *SocketIOInstance;
}
SocketIO::SocketIO() noexcept
: mThread([this] { ThreadMain(); }) {
mClient.socket()->on("network", [&](sio::event&e) {
if(e.get_message()->get_string() == "Welcome"){
info("SocketIO Authenticated!");
mAuthenticated = true;
}
});
mClient.socket()->on("welcome", [&](sio::event&) {
info("Got welcome from backend! Authenticating SocketIO...");
mClient.socket()->emit("onInitConnection", Application::Settings.Key);
});
mClient.set_logs_quiet();
mClient.set_reconnect_delay(10000);
mClient.connect(Application::GetBackendUrlForSocketIO());
}
SocketIO::~SocketIO() {
mCloseThread.store(true);
mThread.join();
}
static constexpr auto EventNameFromEnum(SocketIOEvent Event) {
switch (Event) {
case SocketIOEvent::CPUUsage:
return "cpu usage";
case SocketIOEvent::MemoryUsage:
return "memory usage";
case SocketIOEvent::ConsoleOut:
return "console out";
case SocketIOEvent::NetworkUsage:
return "network usage";
case SocketIOEvent::PlayerList:
return "player list";
default:
error("unreachable code reached (developer error)");
abort();
}
}
void SocketIO::Emit(SocketIOEvent Event, const std::string& Data) {
if (!mAuthenticated) {
debug("trying to emit a socket.io event when not yet authenticated");
return;
}
std::string EventName = EventNameFromEnum(Event);
debug("emitting event \"" + EventName + "\" with data: \"" + Data);
std::unique_lock Lock(mQueueMutex);
mQueue.push_back({EventName, Data });
debug("queue now has " + std::to_string(mQueue.size()) + " events");
}
void SocketIO::ThreadMain() {
while (!mCloseThread.load()) {
bool empty;
{ // queue lock scope
std::unique_lock Lock(mQueueMutex);
empty = mQueue.empty();
} // end queue lock scope
if (empty || !mClient.opened()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
} else {
Event TheEvent;
{ // queue lock scope
std::unique_lock Lock(mQueueMutex);
TheEvent = mQueue.front();
mQueue.pop_front();
} // end queue lock scope
debug("sending \"" + TheEvent.Name + "\" event");
mClient.socket()->emit(TheEvent.Name, TheEvent.Data);
debug("sent \"" + TheEvent.Name + "\" event");
}
}
// using std::cout as this happens during static destruction and the logger might be dead already
std::cout << "closing " + std::string(__func__) << std::endl;
mClient.sync_close();
mClient.clear_con_listeners();
std::cout << "closed" << std::endl;
}

View File

@@ -1,4 +1,6 @@
#include <toml.hpp> // header-only version of TOML++
#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT
#include <toml11/toml.hpp> // header-only version of TOML++
#include "TConfig.h"
#include <fstream>
@@ -32,6 +34,23 @@ TConfig::TConfig() {
}
}
void TConfig::FlushToFile() {
auto data = toml::parse(ConfigFileName);
data["General"] = toml::table();
data["General"][StrAuthKey.data()] = Application::Settings.Key;
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
data["General"][StrPrivate.data()] = Application::Settings.Private;
data["General"][StrPort.data()] = Application::Settings.Port;
data["General"][StrName.data()] = Application::Settings.ServerName;
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
data["General"][StrMap.data()] = Application::Settings.MapName;
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
std::ofstream Stream(ConfigFileName);
Stream << data << std::flush;
}
void TConfig::CreateConfigFile(std::string_view name) {
// build from old config Server.cfg
@@ -44,32 +63,31 @@ void TConfig::CreateConfigFile(std::string_view name) {
error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
}
toml::table tbl { {
{ // create file context
std::ofstream ofs(name.data());
}
{ "General",
toml::table { {
auto data = toml::parse<toml::preserve_comments>(name.data());
{ StrDebug, Application::Settings.DebugModeEnabled },
{ StrPrivate, Application::Settings.Private },
{ StrPort, Application::Settings.Port },
{ StrMaxCars, Application::Settings.MaxCars },
{ StrMaxPlayers, Application::Settings.MaxPlayers },
{ StrMap, Application::Settings.MapName },
{ StrName, Application::Settings.ServerName },
{ StrDescription, Application::Settings.ServerDesc },
{ StrResourceFolder, Application::Settings.Resource },
{ StrAuthKey, Application::Settings.Key },
data["General"] = toml::table();
data["General"][StrAuthKey.data()] = Application::Settings.Key;
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
data["General"][StrPrivate.data()] = Application::Settings.Private;
data["General"][StrPort.data()] = Application::Settings.Port;
data["General"][StrName.data()] = Application::Settings.ServerName;
data["General"][StrMaxCars.data()] = Application::Settings.MaxCars;
data["General"][StrMaxPlayers.data()] = Application::Settings.MaxPlayers;
data["General"][StrMap.data()] = Application::Settings.MapName;
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
} } },
} };
std::ofstream ofs { std::string(name) };
if (ofs.good()) {
ofs << "# This is the BeamMP-Server config file.\n"
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
<< '\n';
ofs << tbl << '\n';
ofs << data << '\n';
error("There was no \"" + std::string(ConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
mFailed = true;
} else {
@@ -80,58 +98,17 @@ void TConfig::CreateConfigFile(std::string_view name) {
void TConfig::ParseFromFile(std::string_view name) {
try {
toml::table FullTable = toml::parse_file(name);
toml::table GeneralTable = *FullTable["General"].as_table();
if (auto val = GeneralTable[StrDebug].value<bool>(); val.has_value()) {
Application::Settings.DebugModeEnabled = val.value();
} else {
throw std::runtime_error(std::string(StrDebug));
}
if (auto val = GeneralTable[StrPrivate].value<bool>(); val.has_value()) {
Application::Settings.Private = val.value();
} else {
throw std::runtime_error(std::string(StrPrivate));
}
if (auto val = GeneralTable[StrPort].value<int>(); val.has_value()) {
Application::Settings.Port = val.value();
} else {
throw std::runtime_error(std::string(StrPort));
}
if (auto val = GeneralTable[StrMaxCars].value<int>(); val.has_value()) {
Application::Settings.MaxCars = val.value();
} else {
throw std::runtime_error(std::string(StrMaxCars));
}
if (auto val = GeneralTable[StrMaxPlayers].value<int>(); val.has_value()) {
Application::Settings.MaxPlayers = val.value();
} else {
throw std::runtime_error(std::string(StrMaxPlayers));
}
if (auto val = GeneralTable[StrMap].value<std::string>(); val.has_value()) {
Application::Settings.MapName = val.value();
} else {
throw std::runtime_error(std::string(StrMap));
}
if (auto val = GeneralTable[StrName].value<std::string>(); val.has_value()) {
Application::Settings.ServerName = val.value();
} else {
throw std::runtime_error(std::string(StrName));
}
if (auto val = GeneralTable[StrDescription].value<std::string>(); val.has_value()) {
Application::Settings.ServerDesc = val.value();
} else {
throw std::runtime_error(std::string(StrDescription));
}
if (auto val = GeneralTable[StrResourceFolder].value<std::string>(); val.has_value()) {
Application::Settings.Resource = val.value();
} else {
throw std::runtime_error(std::string(StrResourceFolder));
}
if (auto val = GeneralTable[StrAuthKey].value<std::string>(); val.has_value()) {
Application::Settings.Key = val.value();
} else {
throw std::runtime_error(std::string(StrAuthKey));
}
toml::value data = toml::parse<toml::preserve_comments>(name.data());
Application::Settings.DebugModeEnabled = data["General"][StrDebug.data()].as_boolean();
Application::Settings.Private = data["General"][StrPrivate.data()].as_boolean();
Application::Settings.Port = data["General"][StrPort.data()].as_integer();
Application::Settings.MaxCars = data["General"][StrMaxCars.data()].as_integer();
Application::Settings.MaxPlayers = data["General"][StrMaxPlayers.data()].as_integer();
Application::Settings.MapName = data["General"][StrMap.data()].as_string();
Application::Settings.ServerName = data["General"][StrName.data()].as_string();
Application::Settings.ServerDesc = data["General"][StrDescription.data()].as_string();
Application::Settings.Resource = data["General"][StrResourceFolder.data()].as_string();
Application::Settings.Key = data["General"][StrAuthKey.data()].as_string();
} catch (const std::exception& err) {
error("Error parsing config file value: " + std::string(err.what()));
mFailed = true;

View File

@@ -26,9 +26,7 @@ void THeartbeatThread::operator()() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
#ifdef DEBUG
debug("heartbeat @ " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()));
#endif // DEBUG
debug("heartbeat (after " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()) + "s)");
Last = Body;
LastNormalUpdateTime = Now;
@@ -37,17 +35,39 @@ void THeartbeatThread::operator()() {
Body += "&pps=" + Application::PPS();
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
auto SentryReportError = [&](const std::string& transaction, int status) {
if (status < 0) {
status = 0;
}
if (T.substr(0, 2) != "20") {
//Backend system refused server startup!
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("heartbeat",
{ { "response-body", T },
{ "request-body", Body } });
Sentry.SetTransaction(transaction);
trace("sending log to sentry: " + std::to_string(status) + " for " + transaction);
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
};
auto Target = "/heartbeat";
unsigned int ResponseCode = 0;
T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
trace("got " + T + " from backend");
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
// TODO backup2 + HTTP flag (no TSL)
if (T.substr(0, 2) != "20") {
warn("Backend system refused server! Server might not show in the public list");
debug("server returned \"" + T + "\"");
isAuth = false;
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode);
if (T.substr(0, 2) != "20" || ResponseCode != 200) {
warn("Backend system refused server! Server will not show in the public server list.");
isAuth = false;
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
}
}
}
@@ -64,6 +84,7 @@ void THeartbeatThread::operator()() {
//SocketIO::Get().SetAuthenticated(isAuth);
}
}
std::string THeartbeatThread::GenerateCall() {
std::stringstream Ret;
@@ -73,8 +94,8 @@ std::string THeartbeatThread::GenerateCall() {
<< "&port=" << Application::Settings.Port
<< "&map=" << Application::Settings.MapName
<< "&private=" << (Application::Settings.Private ? "true" : "false")
<< "&version=" << Application::ServerVersion()
<< "&clientversion=" << Application::ClientVersion()
<< "&version=" << Application::ServerVersionString()
<< "&clientversion=" << Application::ClientVersionString()
<< "&name=" << Application::Settings.ServerName
<< "&modlist=" << mResourceManager.TrimmedList()
<< "&modstotalsize=" << mResourceManager.MaxModSize()

View File

@@ -6,6 +6,8 @@
namespace fs = std::filesystem;
std::unordered_map<std::string, lua_State*> TLuaEngine::mGlobals;
// necessary as lua relies on global state
TLuaEngine* TheEngine;
@@ -66,37 +68,47 @@ std::optional<std::reference_wrapper<TLuaFile>> TLuaEngine::GetScript(lua_State*
}
void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
auto Lock = std::unique_lock(mListMutex);
for (const auto& entry : fs::directory_iterator(Path)) {
auto pos = entry.path().filename().string().find('.');
if (pos == std::string::npos) {
RegisterFiles(entry.path().string(), HotSwap);
if (fs::is_directory(entry)) {
RegisterFiles(entry.path(), HotSwap);
}
}
}
void TLuaEngine::RegisterFiles(const std::string& Path, bool HotSwap) {
std::string Name = Path.substr(Path.find_last_of('\\') + 1);
void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
std::string Name = Path.filename().string();
if (!HotSwap)
info(("Loading plugin : ") + Name);
std::vector<fs::path> Entries;
for (const auto& entry : fs::directory_iterator(Path)) {
auto pos = entry.path().string().find((".lua"));
if (pos != std::string::npos && entry.path().string().length() - pos == 4) {
if (!HotSwap || NewFile(entry.path().string())) {
auto FileName = entry.path().string();
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
auto& Script = *ScriptToInsert;
mLuaFiles.insert(std::move(ScriptToInsert));
Script.Init(Name, FileName, fs::last_write_time(FileName));
if (HotSwap)
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
}
if (entry.path().extension() == ".lua") {
Entries.push_back(entry);
}
}
std::sort(Entries.begin(), Entries.end(), [](const fs::path& first, const fs::path& second) {
auto firstStr = first.string();
auto secondStr = second.string();
std::transform(firstStr.begin(), firstStr.end(), firstStr.begin(), ::tolower);
std::transform(secondStr.begin(), secondStr.end(), secondStr.begin(), ::tolower);
return firstStr < secondStr;
});
for (const fs::path& Entry : Entries) {
if (!HotSwap || IsNewFile(Entry.string())) {
auto FileName = Entry.string();
std::unique_ptr<TLuaFile> ScriptToInsert(new TLuaFile(*this));
auto& Script = *ScriptToInsert;
mLuaFiles.insert(std::move(ScriptToInsert));
Script.Init(Name, FileName, fs::last_write_time(FileName));
if (HotSwap)
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
}
}
}
bool TLuaEngine::NewFile(const std::string& Path) {
bool TLuaEngine::IsNewFile(const std::string& Path) {
for (auto& Script : mLuaFiles) {
if (Path == Script->GetFileName())
if (fs::absolute(Path) == fs::absolute(Script->GetFileName()))
return false;
}
return true;

View File

@@ -2,14 +2,45 @@
#include "Client.h"
#include "Common.h"
#include "CustomAssert.h"
#include "Defer.h"
#include "Http.h"
#include "TLuaEngine.h"
#include "TNetwork.h"
#include "TServer.h"
#include <functional>
#include <future>
#include <thread>
// TODO: REWRITE
namespace LuaTable {
void Begin(lua_State* L) {
lua_newtable(L);
}
void End(lua_State* L, const std::string& name) {
lua_setglobal(L, name.c_str());
}
void BeginEntry(lua_State* L, const std::string& name) {
lua_pushstring(L, name.c_str());
}
void EndEntry(lua_State* L) {
lua_settable(L, -3);
}
void InsertFunction(lua_State* L, const std::string& name, lua_CFunction func) {
BeginEntry(L, name);
lua_pushcfunction(L, func);
EndEntry(L);
}
void InsertInteger(lua_State* L, const std::string& name, lua_Integer i) {
BeginEntry(L, name);
lua_pushinteger(L, i);
EndEntry(L);
}
}
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
@@ -39,12 +70,13 @@ std::shared_ptr<TLuaArg> CreateArg(lua_State* L, int T, int S) {
}
return temp;
}
void ClearStack(lua_State* L) {
lua_settop(L, 0);
}
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
RegisterThread(lua->GetFileName());
std::lock_guard<std::mutex> lockGuard(lua->Lock);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return CallFunction(lua, R, arg); });
std::future<std::any> f1 = task.get_future();
@@ -74,15 +106,13 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
std::any R;
std::string Type;
int Ret = 0;
for (auto& Script : Engine().LuaFiles()) {
if (Script->IsRegistered(Event)) {
if (local) {
if (Script->GetPluginName() == Caller->GetPluginName()) {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (R.type() == typeid(int)) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
@@ -90,8 +120,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
}
} else {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (R.type() == typeid(int)) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
@@ -113,7 +142,13 @@ bool ConsoleCheck(lua_State* L, int r) {
bool CheckLua(lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
std::string msg = "Unknown";
if (lua_isstring(L, -1)) {
auto MsgMaybe = lua_tostring(L, -1);
if (MsgMaybe) {
msg = MsgMaybe;
}
}
auto MaybeS = Engine().GetScript(L);
if (MaybeS.has_value()) {
TLuaFile& S = MaybeS.value();
@@ -121,7 +156,7 @@ bool CheckLua(lua_State* L, int r) {
warn(a + " | " + msg);
return false;
}
// What the fuck, what do we do?!
// This should never happen since it's not directly called from "userspace" Lua.
AssertNotReachable();
}
return true;
@@ -130,7 +165,10 @@ bool CheckLua(lua_State* L, int r) {
int lua_RegisterEvent(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
if (!MaybeScript.has_value()) {
error("RegisterEvent: There is no script associated with this lua_State.");
return 0;
}
TLuaFile& Script = MaybeScript.value();
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
@@ -142,7 +180,10 @@ int lua_RegisterEvent(lua_State* L) {
int lua_TriggerEventL(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
if (!MaybeScript.has_value()) {
error("TriggerEvent: There is no script associated with this lua_State.");
return 0;
}
TLuaFile& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
@@ -158,7 +199,10 @@ int lua_TriggerEventL(lua_State* L) {
int lua_TriggerEventG(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
if (!MaybeScript.has_value()) {
error("TriggerGlobalEvent: There is no script associated with this lua_State.");
return 0;
}
TLuaFile& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
@@ -186,7 +230,7 @@ void ExecuteAsync(TLuaFile* lua, const std::string& FuncName) {
}
void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
RegisterThread(lua->GetFileName());
lua->SetStopThread(false);
int D = 1000 / U;
while (!lua->GetStopThread()) {
@@ -197,8 +241,11 @@ void CallAsync(TLuaFile* lua, const std::string& Func, int U) {
int lua_StopThread(lua_State* L) {
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
// ugly, but whatever, this is safe as fuck
if (!MaybeScript.has_value()) {
error("StopThread: There is no script associated with this lua_State.");
return 0;
}
// ugly, but whatever, this is very safe
MaybeScript.value().get().SetStopThread(true);
return 0;
}
@@ -212,7 +259,10 @@ int lua_CreateThread(lua_State* L) {
int U = int(lua_tointeger(L, 2));
if (U > 0 && U < 501) {
auto MaybeScript = Engine().GetScript(L);
Assert(MaybeScript.has_value());
if (!MaybeScript.has_value()) {
error("CreateThread: There is no script associated with this lua_State.");
return 0;
}
TLuaFile& Script = MaybeScript.value();
std::thread t1(CallAsync, &Script, STR, U);
t1.detach();
@@ -331,11 +381,11 @@ int lua_GetIdentifiers(lua_State* L) {
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
if (IDs.empty())
return 0;
lua_newtable(L);
for (const std::string& ID : IDs) {
lua_pushstring(L, ID.substr(0, ID.find(':')).c_str());
lua_pushstring(L, ID.c_str());
lua_settable(L, -3);
LuaTable::Begin(L);
for (const auto& Pair : IDs) {
LuaTable::BeginEntry(L, Pair.first);
lua_pushstring(L, Pair.second.c_str());
LuaTable::EndEntry(L);
}
} else
return 0;
@@ -400,7 +450,9 @@ int lua_sendChat(lua_State* L) {
if (lua_isstring(L, 2)) {
int ID = int(lua_tointeger(L, 1));
if (ID == -1) {
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
auto msg = std::string(lua_tostring(L, 2));
LogChatMessage("<Server> (to everyone) ", -1, msg);
std::string Packet = "C:Server: " + msg;
Engine().Network().SendToAll(nullptr, Packet, true, true);
} else {
auto MaybeClient = GetClient(Engine().Server(), ID);
@@ -408,7 +460,9 @@ int lua_sendChat(lua_State* L) {
auto c = MaybeClient.value().lock();
if (!c->IsSynced())
return 0;
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
auto msg = std::string(lua_tostring(L, 2));
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, msg);
std::string Packet = "C:Server: " + msg;
Engine().Network().Respond(*c, Packet, true);
} else
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
@@ -511,49 +565,49 @@ int lua_Set(lua_State* L) {
case 0: //debug
if (lua_isboolean(L, 2)) {
Application::Settings.DebugModeEnabled = lua_toboolean(L, 2);
info(Name + (" | Debug -> ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
info(Name + (" | Set `Debug` to ") + (Application::Settings.DebugModeEnabled ? "true" : "false"));
} else
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 0"));
break;
case 1: //private
if (lua_isboolean(L, 2)) {
Application::Settings.Private = lua_toboolean(L, 2);
info(Name + (" | Private -> ") + (Application::Settings.Private ? "true" : "false"));
info(Name + (" | Set `Private` to ") + (Application::Settings.Private ? "true" : "false"));
} else
SendError(Engine(), L, ("set invalid argument [2] expected boolean for ID : 1"));
break;
case 2: //max cars
if (lua_isnumber(L, 2)) {
Application::Settings.MaxCars = int(lua_tointeger(L, 2));
info(Name + (" | MaxCars -> ") + std::to_string(Application::Settings.MaxCars));
info(Name + (" | Set `MaxCars` to ") + std::to_string(Application::Settings.MaxCars));
} else
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 2"));
break;
case 3: //max players
if (lua_isnumber(L, 2)) {
Application::Settings.MaxPlayers = int(lua_tointeger(L, 2));
info(Name + (" | MaxPlayers -> ") + std::to_string(Application::Settings.MaxPlayers));
info(Name + (" | Set `MaxPlayers` to ") + std::to_string(Application::Settings.MaxPlayers));
} else
SendError(Engine(), L, ("set invalid argument [2] expected number for ID : 3"));
break;
case 4: //Map
if (lua_isstring(L, 2)) {
Application::Settings.MapName = lua_tostring(L, 2);
info(Name + (" | MapName -> ") + Application::Settings.MapName);
info(Name + (" | Set `Map` to ") + Application::Settings.MapName);
} else
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 4"));
break;
case 5: //Name
if (lua_isstring(L, 2)) {
Application::Settings.ServerName = lua_tostring(L, 2);
info(Name + (" | ServerName -> ") + Application::Settings.ServerName);
info(Name + (" | Set `Name` to ") + Application::Settings.ServerName);
} else
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 5"));
break;
case 6: //Desc
if (lua_isstring(L, 2)) {
Application::Settings.ServerDesc = lua_tostring(L, 2);
info(Name + (" | ServerDesc -> ") + Application::Settings.ServerDesc);
info(Name + (" | Set `Description` to ") + Application::Settings.ServerDesc);
} else
SendError(Engine(), L, ("set invalid argument [2] expected string for ID : 6"));
break;
@@ -561,12 +615,11 @@ int lua_Set(lua_State* L) {
warn(("Invalid config ID : ") + std::to_string(C));
break;
}
return 0;
}
extern "C" {
int lua_Print(lua_State* L) {
int InternalLuaPrint(lua_State* L, bool pretty) {
int Arg = lua_gettop(L);
std::string to_print;
for (int i = 1; i <= Arg; i++) {
@@ -599,14 +652,81 @@ int lua_Print(lua_State* L) {
to_print += "\t";
}
}
luaprint(to_print);
if (pretty) {
luaprint(to_print);
} else {
Application::Console().WriteRaw(to_print);
}
return 0;
}
int lua_Print(lua_State* L) {
return InternalLuaPrint(L, true);
}
int lua_PrintRaw(lua_State* L) {
return InternalLuaPrint(L, false);
}
}
int lua_TempFix(lua_State* L);
int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
auto MaybeClient = GetClient(Engine().Server(), ID);
if (!MaybeClient || MaybeClient.value().expired())
return 0;
std::string Ret;
auto c = MaybeClient.value().lock();
if (c->IsGuest()) {
Ret = "Guest-" + c->GetName();
} else
Ret = c->GetName();
lua_pushstring(L, Ret.c_str());
} else
SendError(Engine(), L, "GetDID not enough arguments");
return 1;
}
int lua_Registered(lua_State* L) {
lua_Debug info;
lua_getstack(L, 0, &info);
lua_getinfo(L, "n", &info);
if (auto it = TLuaEngine::mGlobals.find(info.name); it != TLuaEngine::mGlobals.end()) {
lua_getglobal(it->second, info.name);
if (lua_isfunction(it->second, -1)) {
lua_pcall(it->second, 0, 0, 0); //TODO revisit to allow arguments and return also we need to mutex this
}
return 0;
}
SendError(Engine(), L, "Cannot find global '" + std::string(info.name) + "\'");
return 0;
}
int lua_Register(lua_State* L) {
if (lua_isstring(L, 1)) {
std::string Name(lua_tolstring(L, 1, nullptr));
lua_getglobal(L, Name.c_str());
if (lua_isfunction(L, -1)) {
TLuaEngine::mGlobals.emplace(Name, L);
for (auto& Script : Engine().LuaFiles()) {
if (Script->GetState() != L) {
lua_register(Script->GetState(), Name.c_str(), lua_Registered);
}
}
} else {
SendError(Engine(), L, Name + " is not a global function!");
ClearStack(L);
}
} else {
SendError(Engine(), L, "Wrong arguments to `Register`, expected string");
}
return 0;
}
void TLuaFile::Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote) {
auto Lock = std::unique_lock(mInitMutex);
// set global engine for lua_* functions
if (!TheEngine) {
TheEngine = &mEngine;
@@ -648,7 +768,7 @@ std::string TLuaFile::GetOrigin() {
}
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg) {
RegisterThread(lua->GetFileName());
lua_State* luaState = lua->GetState();
lua_getglobal(luaState, FuncName.c_str());
if (lua_isfunction(luaState, -1)) {
@@ -682,31 +802,203 @@ void TLuaFile::SetFileName(const std::string& Name) {
mFileName = Name;
}
// GetOSName() -> Linux || Windows || Other
int lua_GetOSName(lua_State* L) {
#if defined(__linux) || defined(__linux__)
lua_pushstring(L, "Linux");
#elif defined(WIN32)
lua_pushstring(L, "Windows");
#else
lua_pushstring(L, "Unknown");
#endif
return 1;
}
int lua_GetServerVersion(lua_State* L) {
const auto& ver = Application::ServerVersion();
lua_pushinteger(L, ver.major);
lua_pushinteger(L, ver.minor);
lua_pushinteger(L, ver.patch);
return 3;
}
// status, body = HttpsGET(host, port, target)
// example usage:
// send a GET https://example.com:443/index.html:
// status, body = MP.HttpGET("example.com", 443, "/index.html")
int lua_HttpsGET(lua_State* L) {
if (!lua_isstring(L, 1)) {
SendError(Engine(), L, "`HttpsGET` expects host (type string) as first argument.");
ClearStack(L);
return 0;
}
if (!lua_isnumber(L, 2)) {
SendError(Engine(), L, "`HttpsGET` expects port (type number) as second argument.");
ClearStack(L);
return 0;
}
if (!lua_isstring(L, 3)) {
SendError(Engine(), L, "`HttpsGET` expects target (type string) as third argument.");
ClearStack(L);
return 0;
}
auto Host = lua_tostring(L, 1);
auto Port = int(lua_tointeger(L, 2));
auto Target = lua_tostring(L, 3);
ClearStack(L);
unsigned int Status;
auto Body = Http::GET(Host, Port, Target, &Status);
lua_pushinteger(L, Status);
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
if (Body == Http::ErrorString) {
SendError(Engine(), L, "HTTPS GET " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
return 1;
} else {
debug("GET " + PrettyRemote + " completed status " + std::to_string(Status));
}
lua_pushstring(L, Body.c_str());
return 2;
}
// status, body = HttpsPOST(host, port, target, body, content_type)
int lua_HttpsPOST(lua_State* L) {
if (!lua_isstring(L, 1)) {
SendError(Engine(), L, "`HttpsPOST` expects host (type string) as 1. argument.");
ClearStack(L);
return 0;
}
if (!lua_isnumber(L, 2)) {
SendError(Engine(), L, "`HttpsPOST` expects port (type number) as 2. argument.");
ClearStack(L);
return 0;
}
if (!lua_isstring(L, 3)) {
SendError(Engine(), L, "`HttpsPOST` expects target (type string) as 3. argument.");
ClearStack(L);
return 0;
}
if (!lua_isstring(L, 4)) {
SendError(Engine(), L, "`HttpsPOST` expects body (type string) as 4. argument.");
ClearStack(L);
return 0;
}
if (!lua_isstring(L, 5)) {
SendError(Engine(), L, "`HttpsPOST` expects content_type (type string) as 5. argument.");
ClearStack(L);
return 0;
}
auto Host = lua_tostring(L, 1);
auto Port = int(lua_tointeger(L, 2));
auto Target = lua_tostring(L, 3);
auto RequestBody = lua_tostring(L, 4);
auto ContentType = lua_tostring(L, 5);
ClearStack(L);
// build fields
std::unordered_map<std::string, std::string> Fields;
unsigned int Status;
auto ResponseBody = Http::POST(Host, Port, Target, {}, RequestBody, ContentType, &Status);
lua_pushinteger(L, Status);
auto PrettyRemote = "https://" + std::string(Host) + ":" + std::to_string(Port) + std::string(Target);
if (ResponseBody == Http::ErrorString) {
SendError(Engine(), L, "HTTPS POST " + PrettyRemote + " failed status " + std::to_string(Status) + ". Check the console or log for more info.");
return 1;
} else {
debug("POST " + PrettyRemote + " completed status " + std::to_string(Status));
}
lua_pushstring(L, ResponseBody.c_str());
return 2;
}
int lua_GetPluginName(lua_State* L) {
auto MaybeFile = Engine().GetScript(L);
if (MaybeFile) {
lua_pushstring(L, MaybeFile.value().get().GetPluginName().c_str());
return 1;
} else {
warn("no plugin associated with this state");
return 0;
}
}
int lua_GetPluginPath(lua_State* L) {
auto MaybeFile = Engine().GetScript(L);
if (MaybeFile) {
lua_pushstring(L, MaybeFile.value().get().GetPluginPath().c_str());
return 1;
} else {
warn("no plugin associated with this state");
return 0;
}
}
int lua_Dump(lua_State* L) {
}
void TLuaFile::Load() {
Assert(mLuaState);
luaL_openlibs(mLuaState);
lua_register(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
lua_register(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
lua_register(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
lua_register(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
lua_register(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
lua_register(mLuaState, "isPlayerConnected", lua_isConnected);
lua_register(mLuaState, "RegisterEvent", lua_RegisterEvent);
lua_register(mLuaState, "GetPlayerName", lua_GetPlayerName);
lua_register(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
lua_register(mLuaState, "GetPlayerDiscordID", lua_TempFix);
lua_register(mLuaState, "CreateThread", lua_CreateThread);
lua_register(mLuaState, "GetPlayerVehicles", lua_GetCars);
lua_register(mLuaState, "SendChatMessage", lua_sendChat);
lua_register(mLuaState, "GetPlayers", lua_GetAllPlayers);
lua_register(mLuaState, "GetPlayerGuest", lua_GetGuest);
lua_register(mLuaState, "StopThread", lua_StopThread);
lua_register(mLuaState, "DropPlayer", lua_dropPlayer);
lua_register(mLuaState, "GetPlayerHWID", lua_HWID);
lua_register(mLuaState, "exit", lua_ServerExit);
lua_register(mLuaState, "Sleep", lua_Sleep);
LuaTable::Begin(mLuaState);
LuaTable::BeginEntry(mLuaState, "Settings");
LuaTable::Begin(mLuaState);
// put Settings enums here
LuaTable::InsertInteger(mLuaState, "Debug", 0);
LuaTable::InsertInteger(mLuaState, "Private", 1);
LuaTable::InsertInteger(mLuaState, "MaxCars", 2);
LuaTable::InsertInteger(mLuaState, "MaxPlayers", 3);
LuaTable::InsertInteger(mLuaState, "Map", 4);
LuaTable::InsertInteger(mLuaState, "Name", 5);
LuaTable::InsertInteger(mLuaState, "Description", 6);
LuaTable::EndEntry(mLuaState);
LuaTable::InsertFunction(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
LuaTable::InsertFunction(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
LuaTable::InsertFunction(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
LuaTable::InsertFunction(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
LuaTable::InsertFunction(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
LuaTable::InsertFunction(mLuaState, "IsPlayerConnected", lua_isConnected);
LuaTable::InsertFunction(mLuaState, "RegisterEvent", lua_RegisterEvent);
LuaTable::InsertFunction(mLuaState, "GetPlayerName", lua_GetPlayerName);
LuaTable::InsertFunction(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
LuaTable::InsertFunction(mLuaState, "GetPlayerDiscordID", lua_TempFix);
LuaTable::InsertFunction(mLuaState, "CreateThread", lua_CreateThread);
LuaTable::InsertFunction(mLuaState, "GetPlayerVehicles", lua_GetCars);
LuaTable::InsertFunction(mLuaState, "SendChatMessage", lua_sendChat);
LuaTable::InsertFunction(mLuaState, "GetPlayers", lua_GetAllPlayers);
LuaTable::InsertFunction(mLuaState, "GetPlayerGuest", lua_GetGuest);
LuaTable::InsertFunction(mLuaState, "StopThread", lua_StopThread);
LuaTable::InsertFunction(mLuaState, "DropPlayer", lua_dropPlayer);
LuaTable::InsertFunction(mLuaState, "Register", lua_Register);
LuaTable::InsertFunction(mLuaState, "GetPlayerHWID", lua_HWID);
LuaTable::InsertFunction(mLuaState, "Sleep", lua_Sleep);
LuaTable::InsertFunction(mLuaState, "Set", lua_Set);
LuaTable::InsertFunction(mLuaState, "GetOSName", lua_GetOSName);
LuaTable::InsertFunction(mLuaState, "HttpsGET", lua_HttpsGET);
LuaTable::InsertFunction(mLuaState, "HttpsPOST", lua_HttpsPOST);
LuaTable::InsertFunction(mLuaState, "GetServerVersion", lua_GetServerVersion);
LuaTable::InsertFunction(mLuaState, "GetPluginName", lua_GetPluginName);
LuaTable::InsertFunction(mLuaState, "GetPluginPath", lua_GetPluginPath);
LuaTable::End(mLuaState, "MP");
lua_register(mLuaState, "print", lua_Print);
lua_register(mLuaState, "Set", lua_Set);
lua_register(mLuaState, "printRaw", lua_PrintRaw);
lua_register(mLuaState, "dump", lua_Dump);
lua_register(mLuaState, "exit", lua_ServerExit);
if (!mConsole)
Reload();
}
@@ -768,6 +1060,13 @@ TLuaFile::~TLuaFile() {
lua_close(mLuaState);
}
std::string TLuaFile::GetPluginPath() const {
auto path = fs::path(Application::Settings.Resource);
path /= "Server";
path /= mPluginName;
return path.string();
}
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
Assert(L);
auto MaybeS = Engine.GetScript(L);
@@ -778,25 +1077,7 @@ void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
TLuaFile& S = MaybeS.value();
a = fs::path(S.GetFileName()).filename().string();
}
warn(a + (" | Incorrect Call of ") + msg);
}
int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
auto MaybeClient = GetClient(Engine().Server(), ID);
if (!MaybeClient || MaybeClient.value().expired())
return 0;
std::string Ret;
auto c = MaybeClient.value().lock();
if (c->IsGuest()) {
Ret = "Guest-" + c->GetName();
} else
Ret = c->GetName();
lua_pushstring(L, Ret.c_str());
} else
SendError(Engine(), L, "GetDID not enough arguments");
return 1;
warn(a + (" | Error in MP Lua call: ") + msg);
}
void TLuaArg::PushArgs(lua_State* State) {

View File

@@ -9,6 +9,15 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
: mServer(Server)
, mPPSMonitor(PPSMonitor)
, mResourceManager(ResourceManager) {
Application::RegisterShutdownHandler([&] {
debug("Kicking all players due to shutdown");
Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool {
if (!client.expired()) {
ClientKick(*client.lock(), "Server shutdown");
}
return true;
});
});
Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) {
debug("shutting down TCPServer");
@@ -18,7 +27,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
}
});
Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) {
if (mTCPThread.joinable()) {
debug("shutting down TCPServer");
mShutdown = true;
mTCPThread.detach();
@@ -37,22 +46,7 @@ void TNetwork::UDPServerMain() {
error(("Can't start Winsock!"));
//return;
}
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
// Create a server hint structure for the server
sockaddr_in serverAddr {};
serverAddr.sin_addr.S_un.S_addr = ADDR_ANY; //Any Local
serverAddr.sin_family = AF_INET; // Address format is IPv4
serverAddr.sin_port = htons(Application::Settings.Port); // Convert from little to big endian
// Try and bind the socket to the IP and port
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
error(("Can't bind socket!") + std::to_string(WSAGetLastError()));
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1);
//return;
}
#else // unix
#endif // WIN32
mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
// Create a server hint structure for the server
sockaddr_in serverAddr {};
@@ -62,12 +56,11 @@ void TNetwork::UDPServerMain() {
// Try and bind the socket to the IP and port
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
error(("Can't bind socket!") + std::string(strerror(errno)));
error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1);
//return;
}
#endif
info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
@@ -114,13 +107,23 @@ void TNetwork::TCPServerMain() {
error("Can't start Winsock!");
return;
}
SOCKET client, Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif // WIN32
TConnection client {};
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int optval = 1;
#ifdef WIN32
const char* optval_ptr = reinterpret_cast<const char*>(&optval);
#else
void* optval_ptr = reinterpret_cast<void*>(&optval);
#endif
setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, optval_ptr, sizeof(optval));
// TODO: check optval or return value idk
sockaddr_in addr {};
addr.sin_addr.S_un.S_addr = ADDR_ANY;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_family = AF_INET;
addr.sin_port = htons(Application::Settings.Port);
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
error("Can't bind socket! " + std::to_string(WSAGetLastError()));
addr.sin_port = htons(uint16_t(Application::Settings.Port));
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1);
}
@@ -128,53 +131,9 @@ void TNetwork::TCPServerMain() {
error("Invalid listening socket");
return;
}
if (listen(Listener, SOMAXCONN)) {
error("listener failed " + std::to_string(GetLastError()));
//TODO Fix me leak for Listener socket
return;
}
info("Vehicle event network online");
do {
try {
client = accept(Listener, nullptr, nullptr);
if (client == -1) {
warn("Got an invalid client socket on connect! Skipping...");
continue;
}
std::thread ID(&TNetwork::Identify, this, client);
ID.detach();
} catch (const std::exception& e) {
error("fatal: " + std::string(e.what()));
}
} while (client);
CloseSocketProper(client);
WSACleanup();
#else // unix
// wondering why we need slightly different implementations of this?
// ask ms.
SOCKET client = -1;
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int optval = 1;
setsockopt(Listener, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
// TODO: check optval or return value idk
sockaddr_in addr {};
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_family = AF_INET;
addr.sin_port = htons(uint16_t(Application::Settings.Port));
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
error(("Can't bind socket! ") + std::string(strerror(errno)));
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1);
}
if (Listener == -1) {
error(("Invalid listening socket"));
return;
}
if (listen(Listener, SOMAXCONN)) {
error(("listener failed ") + std::string(strerror(errno)));
//TODO fix me leak Listener
error("listen() failed: " + GetPlatformAgnosticErrorString());
// FIXME leak Listener
return;
}
info(("Vehicle event network online"));
@@ -184,8 +143,9 @@ void TNetwork::TCPServerMain() {
debug("shutdown during TCP wait for accept loop");
break;
}
client = accept(Listener, nullptr, nullptr);
if (client == -1) {
client.SockAddrLen = sizeof(client.SockAddr);
client.Socket = accept(Listener, &client.SockAddr, &client.SockAddrLen);
if (client.Socket == -1) {
warn(("Got an invalid client socket on connect! Skipping..."));
continue;
}
@@ -194,12 +154,15 @@ void TNetwork::TCPServerMain() {
} catch (const std::exception& e) {
error(("fatal: ") + std::string(e.what()));
}
} while (client);
} while (client.Socket);
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
CloseSocketProper(client.Socket);
#ifdef WIN32
CloseSocketProper(client);
#endif
WSACleanup();
#endif // WIN32
}
#undef GetObject //Fixes Windows
@@ -207,19 +170,19 @@ void TNetwork::TCPServerMain() {
#include "Json.h"
namespace json = rapidjson;
void TNetwork::Identify(SOCKET TCPSock) {
void TNetwork::Identify(const TConnection& client) {
RegisterThreadAuto();
char Code;
if (recv(TCPSock, &Code, 1, 0) != 1) {
CloseSocketProper(TCPSock);
if (recv(client.Socket, &Code, 1, 0) != 1) {
CloseSocketProper(client.Socket);
return;
}
if (Code == 'C') {
Authentication(TCPSock);
Authentication(client);
} else if (Code == 'D') {
HandleDownload(TCPSock);
HandleDownload(client.Socket);
} else {
CloseSocketProper(TCPSock);
CloseSocketProper(client.Socket);
}
}
@@ -242,17 +205,21 @@ void TNetwork::HandleDownload(SOCKET TCPSock) {
});
}
void TNetwork::Authentication(SOCKET TCPSock) {
auto Client = CreateClient(TCPSock);
void TNetwork::Authentication(const TConnection& ClientConnection) {
auto Client = CreateClient(ClientConnection.Socket);
char AddrBuf[30];
inet_ntoa(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)->sin_addr);
auto str = inet_ntop(AF_INET, static_cast<const void*>(reinterpret_cast<const struct sockaddr_in*>(&ClientConnection.SockAddr)), AddrBuf, sizeof(struct sockaddr_in));
Client->SetIdentifier("ip", str);
std::string Rc;
info("Identifying new client...");
info("Identifying new ClientConnection...");
Rc = TCPRcv(*Client);
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
Rc = Rc.substr(2);
if (Rc.length() > 4 || Rc != Application::ClientVersion()) {
if (Rc.length() > 4 || Rc != Application::ClientVersionString()) {
ClientKick(*Client, "Outdated Version!");
return;
}
@@ -271,20 +238,39 @@ void TNetwork::Authentication(SOCKET TCPSock) {
return;
}
auto RequestString = R"({"key":")" + Rc + "\"}";
auto Target = "/pkToUser";
unsigned int ResponseCode = 0;
if (!Rc.empty()) {
Rc = Http::POST(Application::GetBackendUrlForAuth(), "/pkToUser", {}, R"({"key":")" + Rc + "\"}", true);
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode);
}
json::Document AuthResponse;
AuthResponse.Parse(Rc.c_str());
if (Rc == "-1" || AuthResponse.HasParseError()) {
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
ClientKick(*Client, "Invalid key! Please restart your game.");
return;
}
if (!AuthResponse.IsObject()) {
ClientKick(*Client, "Backend returned invalid auth response format.");
error("Backend returned invalid auth response format. This should never happen.");
if (Rc == "0") {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("auth",
{ { "response-body", Rc },
{ "key", RequestString } });
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
Sentry.Log(SentryLevel::Info, "default", "backend returned 0 instead of json (" + std::to_string(ResponseCode) + ")");
} else { // Rc != "0"
ClientKick(*Client, "Backend returned invalid auth response format.");
error("Backend returned invalid auth response format. This should never happen.");
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("auth",
{ { "response-body", Rc },
{ "key", RequestString } });
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")");
}
return;
}
@@ -295,7 +281,9 @@ void TNetwork::Authentication(SOCKET TCPSock) {
Client->SetRoles(AuthResponse["roles"].GetString());
Client->SetIsGuest(AuthResponse["guest"].GetBool());
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
Client->AddIdentifier(ID.GetString());
auto Raw = std::string(ID.GetString());
auto SepIndex = Raw.find(':');
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
}
} else {
ClientKick(*Client, "Invalid authentication data!");
@@ -303,7 +291,6 @@ void TNetwork::Authentication(SOCKET TCPSock) {
}
debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
debug("There are " + std::to_string(mServer.ClientCount()) + " known clients");
mServer.ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
std::shared_ptr<TClient> Cl;
{
@@ -313,10 +300,7 @@ void TNetwork::Authentication(SOCKET TCPSock) {
} else
return true;
}
info("Client Iteration: Name -> " + Cl->GetName() + ", Guest -> " + std::to_string(Cl->IsGuest()) + ", Roles -> " + Cl->GetRoles());
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
info("New client matched with current iteration");
info("Old client (" + Cl->GetName() + ") kicked: Reconnecting");
CloseSocketProper(Cl->GetTCPSock());
Cl->SetStatus(-2);
return false;
@@ -327,11 +311,10 @@ void TNetwork::Authentication(SOCKET TCPSock) {
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
std::string Type = Res.type().name();
if (Type.find("int") != std::string::npos && std::any_cast<int>(Res)) {
if (Res.type() == typeid(int) && std::any_cast<int>(Res)) {
ClientKick(*Client, "you are not allowed on the server!");
return;
} else if (Type.find("string") != std::string::npos) {
} else if (Res.type() == typeid(std::string)) {
ClientKick(*Client, std::any_cast<std::string>(Res));
return;
}
@@ -376,12 +359,12 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
#endif //WIN32
if (Temp == 0) {
debug("send() == 0: " + std::string(std::strerror(errno)));
debug("send() == 0: " + GetPlatformAgnosticErrorString());
if (c.GetStatus() > -1)
c.SetStatus(-1);
return false;
} else if (Temp < 0) {
debug("send() < 0: " + std::string(std::strerror(errno))); //TODO fix it was spamming yet everyone stayed on the server
debug("send() < 0: " + GetPlatformAgnosticErrorString()); //TODO fix it was spamming yet everyone stayed on the server
if (c.GetStatus() > -1)
c.SetStatus(-1);
CloseSocketProper(c.GetTCPSock());
@@ -395,19 +378,14 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
if (BytesRcv == 0) {
debug("(TCP) Connection closing...");
trace("(TCP) Connection closing...");
if (c.GetStatus() > -1)
c.SetStatus(-1);
return false;
} else if (BytesRcv < 0) {
#ifdef WIN32
debug(("(TCP) recv failed with error: ") + std::to_string(WSAGetLastError()));
#else // unix
debug(("(TCP) recv failed with error: ") + std::string(strerror(errno)));
#endif // WIN32
debug("(TCP) recv() failed: " + GetPlatformAgnosticErrorString());
if (c.GetStatus() > -1)
c.SetStatus(-1);
info(("Closing socket in CheckBytes, BytesRcv < 0"));
CloseSocketProper(c.GetTCPSock());
return false;
}
@@ -423,22 +401,13 @@ std::string TNetwork::TCPRcv(TClient& c) {
do {
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
if (!CheckBytes(c, Temp)) {
#ifdef DEBUG
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < 4)"));
#endif // DEBUG
return "";
}
BytesRcv += Temp;
} while (size_t(BytesRcv) < sizeof(Header));
memcpy(&Header, &Data[0], sizeof(Header));
#ifdef DEBUG
//debug(std::string(__func__) + (": expecting ") + std::to_string(Header) + (" bytes."));
#endif // DEBUG
if (!CheckBytes(c, BytesRcv)) {
#ifdef DEBUG
error(std::string(__func__) + (": failed on CheckBytes"));
#endif // DEBUG
return "";
}
if (Header < 100 * MB) {
@@ -452,29 +421,15 @@ std::string TNetwork::TCPRcv(TClient& c) {
do {
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
if (!CheckBytes(c, Temp)) {
#ifdef DEBUG
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < Header)"));
#endif // DEBUG
return "";
}
#ifdef DEBUG
//debug(std::string(__func__) + (": Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
#endif // DEBUG
BytesRcv += Temp;
} while (BytesRcv < Header);
#ifdef DEBUG
//debug(std::string(__func__) + (": finished recv with Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
#endif // DEBUG
std::string Ret(Data.data(), Header);
if (Ret.substr(0, 4) == "ABG:") {
Ret = DeComp(Ret.substr(4));
}
#ifdef DEBUG
//debug("Parsing from " + c->GetName() + " -> " +std::to_string(Ret.size()));
#endif
return Ret;
}
@@ -694,19 +649,29 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
}
}
void TNetwork::SendFile(TClient& c, const std::string& Name) {
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/')));
void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
if (!std::filesystem::exists(Name)) {
if (!fs::path(UnsafeName).has_filename()) {
if (!TCPSend(c, "CO")) {
// TODO: handle
}
warn("File " + Name + " could not be accessed!");
warn("File " + UnsafeName + " is not a file!");
return;
} else {
if (!TCPSend(c, "AG")) {
}
auto FileName = fs::path(UnsafeName).filename().string();
FileName = Application::Settings.Resource + "/Client/" + FileName;
if (!std::filesystem::exists(FileName)) {
if (!TCPSend(c, "CO")) {
// TODO: handle
}
warn("File " + UnsafeName + " could not be accessed!");
return;
}
if (!TCPSend(c, "AG")) {
// TODO: handle
}
///Wait for connections
@@ -723,14 +688,14 @@ void TNetwork::SendFile(TClient& c, const std::string& Name) {
return;
}
size_t Size = size_t(std::filesystem::file_size(Name)), MSize = Size / 2;
size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2;
std::thread SplitThreads[2] {
std::thread([&] {
SplitLoad(c, 0, MSize, false, Name);
SplitLoad(c, 0, MSize, false, FileName);
}),
std::thread([&] {
SplitLoad(c, MSize, Size, true, Name);
SplitLoad(c, MSize, Size, true, FileName);
})
};
@@ -937,31 +902,17 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) const {
#endif // WIN32
sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, int(AddrSize));
#ifdef WIN32
if (sendOk == -1) {
debug(("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
debug("(UDP) sendto() failed: " + GetPlatformAgnosticErrorString());
if (Client.GetStatus() > -1)
Client.SetStatus(-1);
return false;
} else if (sendOk == 0) {
debug(("(UDP) sendto returned 0"));
debug(("(UDP) sendto() returned 0"));
if (Client.GetStatus() > -1)
Client.SetStatus(-1);
return false;
}
#else // unix
if (sendOk == -1) {
debug(("(UDP) Send Failed Code : ") + std::string(strerror(errno)));
if (Client.GetStatus() > -1)
Client.SetStatus(-1);
return false;
} else if (sendOk == 0) {
debug(("(UDP) sendto returned 0"));
if (Client.GetStatus() > -1)
Client.SetStatus(-1);
return false;
}
#endif // WIN32
return true;
}
@@ -975,11 +926,7 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const {
#endif // WIN32
if (Rcv == -1) {
#ifdef WIN32
error(("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
#else // unix
error(("(UDP) Error receiving from Client! Code : ") + std::string(strerror(errno)));
#endif // WIN32
error("(UDP) Error receiving from client! recvfrom() failed: " + GetPlatformAgnosticErrorString());
return "";
}
return std::string(Ret.begin(), Ret.begin() + Rcv);

View File

@@ -44,7 +44,7 @@ void TPPSMonitor::operator()() {
V += c->GetCarCount();
}
// kick on "no ping"
if (c->SecondsSinceLastPing() > (5 * 60)) {
if (c->SecondsSinceLastPing() > (20 * 60)) {
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
TimedOutClients.push_back(c);
}
@@ -52,7 +52,7 @@ void TPPSMonitor::operator()() {
return true;
});
for (auto& ClientToKick : TimedOutClients) {
Network().ClientKick(*ClientToKick, "Timeout (no ping for >5 min)");
Network().ClientKick(*ClientToKick, "Timeout (no ping for way too long)");
}
TimedOutClients.clear();
if (C == 0 || mInternalPPS == 0) {

View File

@@ -19,7 +19,7 @@ TResourceManager::TResourceManager() {
++i;
File = File.substr(i,pos-i);
}
mTrimmedList += File + ';';
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
mMaxModSize += size_t(fs::file_size(entry.path()));
mModsLoaded++;

125
src/TSentry.cpp Normal file
View File

@@ -0,0 +1,125 @@
#include "TSentry.h"
#include "Common.h"
#include <sentry.h>
#include <sstream>
// compile-time length of a string/array
template <size_t N>
constexpr size_t ConstexprLength(char const (&)[N]) {
return N - 1;
}
TSentry::TSentry() {
if constexpr (ConstexprLength(SECRET_SENTRY_URL) == 0) {
mValid = false;
} else {
mValid = true;
sentry_options_t* options = sentry_options_new();
sentry_options_set_dsn(options, SECRET_SENTRY_URL);
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersionString();
sentry_options_set_symbolize_stacktraces(options, true);
sentry_options_set_release(options, ReleaseString.c_str());
sentry_options_set_max_breadcrumbs(options, 10);
sentry_init(options);
}
}
TSentry::~TSentry() {
if (mValid) {
sentry_close();
}
}
void TSentry::PrintWelcome() {
if (mValid) {
info("Sentry started");
} else {
info("Sentry disabled in unofficial build");
}
}
void TSentry::SetupUser() {
if (!mValid) {
return;
}
sentry_value_t user = sentry_value_new_object();
if (Application::Settings.Key.size() == 36) {
sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str()));
} else {
sentry_value_set_by_key(user, "id", sentry_value_new_string("unauthenticated"));
}
sentry_set_user(user);
}
void TSentry::Log(SentryLevel level, const std::string& logger, const std::string& text) {
if (!mValid) {
return;
}
SetContext("threads", { { "thread-name", ThreadName(true) } });
auto Msg = sentry_value_new_message_event(sentry_level_t(level), logger.c_str(), text.c_str());
sentry_capture_event(Msg);
sentry_remove_transaction();
}
void TSentry::LogError(const std::string& text, const std::string& file, const std::string& line) {
if (!mValid) {
return;
}
SetTransaction(file + ":" + line);
Log(SentryLevel::Error, "default", file + ": " + text);
}
void TSentry::SetContext(const std::string& context_name, const std::unordered_map<std::string, std::string>& map) {
if (!mValid) {
return;
}
auto ctx = sentry_value_new_object();
for (const auto& pair : map) {
std::string key = pair.first;
if (key == "type") {
// `type` is reserved
key = "_type";
}
sentry_value_set_by_key(ctx, key.c_str(), sentry_value_new_string(pair.second.c_str()));
}
sentry_set_context(context_name.c_str(), ctx);
}
void TSentry::LogException(const std::exception& e, const std::string& file, const std::string& line) {
if (!mValid) {
return;
}
SetTransaction(file + ":" + line);
Log(SentryLevel::Fatal, "exceptions", std::string(e.what()) + " @ " + file + ":" + line);
}
void TSentry::LogAssert(const std::string& condition_string, const std::string& file, const std::string& line, const std::string& function) {
if (!mValid) {
return;
}
SetTransaction(file + ":" + line + ":" + function);
std::stringstream ss;
ss << "\"" << condition_string << "\" failed @ " << file << ":" << line;
Log(SentryLevel::Fatal, "asserts", ss.str());
}
void TSentry::AddErrorBreadcrumb(const std::string& msg, const std::string& file, const std::string& line) {
if (!mValid) {
return;
}
auto crumb = sentry_value_new_breadcrumb("default", (msg + " @ " + file + ":" + line).c_str());
sentry_value_set_by_key(crumb, "level", sentry_value_new_string("error"));
sentry_add_breadcrumb(crumb);
}
void TSentry::SetTransaction(const std::string& id) {
if (!mValid) {
return;
}
sentry_set_transaction(id.c_str());
}
std::unique_lock<std::mutex> TSentry::CreateExclusiveContext() {
return std::unique_lock<std::mutex>(mMutex);
}

View File

@@ -7,14 +7,14 @@
#include <any>
#include <sstream>
#undef GetObject //Fixes Windows
#undef GetObject // Fixes Windows
#include "Json.h"
namespace json = rapidjson;
TServer::TServer(int argc, char** argv) {
info("BeamMP Server v" + Application::ServerVersion());
info("BeamMP Server v" + Application::ServerVersionString());
if (argc > 1) {
Application::Settings.CustomIP = argv[1];
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
@@ -65,7 +65,7 @@ size_t TServer::ClientCount() const {
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
abort();
//abort();
}
if (Packet.substr(0, 4) == "ABG:") {
Packet = DeComp(Packet.substr(4));
@@ -90,9 +90,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
}
switch (Code) {
case 'H': // initial connection
#ifdef DEBUG
debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
#endif
trace(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
if (!Network.SyncClient(Client)) {
// TODO handle
}
@@ -114,31 +112,27 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
ParseVehicle(*LockedClient, Packet, Network);
return;
case 'J':
#ifdef DEBUG
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
Network.SendToAll(LockedClient.get(), Packet, false, true);
return;
case 'C':
#ifdef DEBUG
debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
break;
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1)); // FIXME: this needs to be adjusted once lua is merged
if (std::any_cast<int>(Res))
break;
Network.SendToAll(nullptr, Packet, true, true);
return;
case 'E':
#ifdef DEBUG
debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
HandleEvent(*LockedClient, Packet);
return;
case 'N':
debug("got 'N' packet (" + std::to_string(Packet.size()) + ")");
trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
Network.SendToAll(LockedClient.get(), Packet, false, true);
return;
default:
return;
}
@@ -164,10 +158,10 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
a++;
}
}
bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) {
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
rapidjson::Document Car;
Car.Parse(CarJson.c_str(), CarJson.size());
if(Car.HasParseError()){
if (Car.HasParseError()) {
error("Failed to parse vehicle data -> " + CarJson);
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
return true;
@@ -176,11 +170,11 @@ bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) {
}
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
if(c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars){
if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) {
return true;
}
if(IsUnicycle(c,CarJson)){
if (IsUnicycle(c, CarJson)) {
c.SetUnicycleID(ID);
return true;
}
@@ -198,9 +192,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
std::string Data = Packet.substr(3), pid, vid;
switch (Code) { //Spawned Destroyed Switched/Moved NotFound Reset
case 's':
#ifdef DEBUG
debug(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
if (Data.at(0) == '0') {
int CarID = c.GetOpenCarID();
debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID));
@@ -225,9 +217,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
return;
case 'c':
#ifdef DEBUG
debug(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
pid = Data.substr(0, Data.find('-'));
vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1);
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
@@ -239,12 +229,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
true);
if ((c.GetUnicycleID() != VID || IsUnicycle(c,Packet.substr(Packet.find('{'))))
auto FoundPos = Packet.find('{');
FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this
if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos)))
&& std::any_cast<int>(Res) == 0) {
Network.SendToAll(&c, Packet, false, true);
Apply(c, VID, Packet);
} else {
if(c.GetUnicycleID() == VID){
if (c.GetUnicycleID() == VID) {
c.SetUnicycleID(-1);
}
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
@@ -256,9 +248,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
return;
case 'd':
#ifdef DEBUG
debug(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
pid = Data.substr(0, Data.find('-'));
vid = Data.substr(Data.find('-') + 1);
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
@@ -266,7 +256,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
VID = stoi(vid);
}
if (PID != -1 && VID != -1 && PID == c.GetID()) {
if(c.GetUnicycleID() == VID){
if (c.GetUnicycleID() == VID) {
c.SetUnicycleID(-1);
}
Network.SendToAll(nullptr, Packet, true, true);
@@ -277,9 +267,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
return;
case 'r':
#ifdef DEBUG
debug(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
Pos = int(Data.find('-'));
pid = Data.substr(0, Pos++);
vid = Data.substr(Pos, Data.find(':') - Pos);
@@ -298,24 +286,45 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
return;
case 't':
#ifdef DEBUG
debug(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
Network.SendToAll(&c, Packet, false, true);
return;
default:
#ifdef DEBUG
warn(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
#endif // DEBUG
trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
return;
}
}
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
std::string Packet = pckt.substr(pckt.find('{')), VD = c.GetCarData(VID);
auto FoundPos = pckt.find('{');
if (FoundPos == std::string::npos) {
error("Malformed packet received, no '{' found");
return;
}
std::string Packet = pckt.substr(FoundPos);
std::string VD = c.GetCarData(VID);
if (VD.empty()) {
error("Tried to apply change to vehicle that does not exist");
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("vehicle-change",
{ { "packet", Packet },
{ "vehicle-id", std::to_string(VID) },
{ "client-car-count", std::to_string(c.GetCarCount()) } });
Sentry.LogError("attempt to apply change to nonexistent vehicle", _file_basename, _line);
return;
}
std::string Header = VD.substr(0, VD.find('{'));
VD = VD.substr(VD.find('{'));
FoundPos = VD.find('{');
if (FoundPos == std::string::npos) {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("vehicle-change-packet",
{ { "packet", VD } });
Sentry.LogError("malformed packet", _file_basename, _line);
error("Malformed packet received, no '{' found");
return;
}
VD = VD.substr(FoundPos);
rapidjson::Document Veh, Pack;
Veh.Parse(VD.c_str());
if (Veh.HasParseError()) {

View File

@@ -1,18 +1,14 @@
#include "VehicleData.h"
#include <utility>
#include "Common.h"
#include <utility>
TVehicleData::TVehicleData(int ID, std::string Data)
: mID(ID)
, mData(std::move(Data)) {
#ifdef DEBUG
debug("vehicle " + std::to_string(mID) + " constructed");
#endif
trace("vehicle " + std::to_string(mID) + " constructed");
}
TVehicleData::~TVehicleData() {
#ifdef DEBUG
debug("vehicle " + std::to_string(mID) + " destroyed");
#endif
trace("vehicle " + std::to_string(mID) + " destroyed");
}

View File

@@ -1,4 +1,7 @@
#include "TSentry.h"
#include "Common.h"
#include "CustomAssert.h"
#include "Http.h"
#include "TConfig.h"
#include "THeartbeatThread.h"
@@ -7,6 +10,7 @@
#include "TPPSMonitor.h"
#include "TResourceManager.h"
#include "TServer.h"
#include <thread>
#ifdef __unix
@@ -32,22 +36,26 @@ void UnixSignalHandler(int sig) {
}
#endif // __unix
int main(int argc, char** argv) {
// this is provided by the build system, leave empty for source builds
// global, yes, this is ugly, no, it cant be done another way
TSentry Sentry {};
#include <iostream>
int main(int argc, char** argv) try {
#ifdef __unix
#if DEBUG
info("registering handlers for SIGINT, SIGTERM, SIGPIPE");
#endif // DEBUG
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
#ifndef DEBUG
signal(SIGINT, UnixSignalHandler);
#endif // DEBUG
#endif // __unix
setlocale(LC_ALL, "C");
bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
Application::RegisterShutdownHandler([] { TriggerLuaEvent("onShutdown", false, nullptr, {}, true); });
TServer Server(argc, argv);
TConfig Config;
@@ -59,6 +67,12 @@ int main(int argc, char** argv) {
}
RegisterThread("Main");
trace("Running in debug mode on a debug build");
Sentry.SetupUser();
Sentry.PrintWelcome();
Application::CheckForUpdates();
TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server);
@@ -71,4 +85,7 @@ int main(int argc, char** argv) {
while (!Shutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
} catch (const std::exception& e) {
error(e.what());
Sentry.LogException(e, _file_basename, _line);
}