Compare commits

...

190 Commits

Author SHA1 Message Date
Lion Kortlepel
aaefea1d01 update commandline 2021-11-09 22:05:04 +01:00
Lion Kortlepel
863e8eb8cf Bump version 2021-10-09 23:53:59 +02:00
Lion Kortlepel
d0431c0b9d Use v2 api 2021-09-27 15:46:37 +02:00
Lion Kortlepel
0961f86662 Add heartbeat-api-v API version header 2021-09-27 15:46:37 +02:00
Lion Kortlepel
7f2ca025f8 Start using new heartbeat response format 2021-09-27 15:46:37 +02:00
Lion Kortlepel
2355327c21 CMake: Fix typo in SANITIZE codepath 2021-09-15 17:55:34 +02:00
Lion Kortlepel
3837e101e2 CMake: Use gzipped debug info on linux 2021-09-15 17:55:34 +02:00
Lion Kortlepel
fa19ba08e3 Sentry: Properly store DSN 2021-09-15 17:55:34 +02:00
Lion Kortlepel
57d0eb735e Add cryptography header for the future 2021-09-15 17:55:34 +02:00
Lion Kortlepel
15704abf6c Http, Heartbeat: Process status < 0 differently, report as "Invalid
Response Code"
2021-09-15 17:55:34 +02:00
Lion Kortlepel
6883c96d33 Http: Add Sentry error breadcrumbs on internal https POST errors 2021-09-15 17:55:34 +02:00
Lion Kortlepel
f632606d76 Heartbeat: Dont report 200 + INVALID_KEY to Sentry 2021-09-15 17:55:34 +02:00
Lion Kortlepel
c70ada2926 Config: private by default 2021-09-15 17:55:34 +02:00
Lion Kortlepel
80aebcb9a7 Actions: prerelease by default 2021-09-13 11:58:01 +03:00
Lion Kortlepel
3fc397814c Move update check to after initialization (since its blocking) 2021-09-11 11:38:06 +03:00
Lion Kortlepel
6542be09ee Clarify what sentry sends, add a way to turn off the warning 2021-09-11 11:38:06 +03:00
Lion Kortlepel
38b934bc0f Move signal handling into its own translation unit to limit overlap 2021-09-11 11:38:06 +03:00
Lion Kortlepel
a2f92b5791 Update changelog, use std::exit instead of exit 2021-09-11 11:38:06 +03:00
Lion Kortlepel
30624c77a2 Update commandline; reset terminal before exit 2021-09-11 11:38:06 +03:00
Lion Kortlepel
b1664bb184 Application: Perform hard-shutdown after 3 Ctrl+C's 2021-09-11 11:38:06 +03:00
Lion Kortlepel
ffac000cd2 Config: Add basic opt-out for Sentry 2021-09-11 11:38:06 +03:00
Lion Kortlepel
3cd94380e2 Changelog: Add recent additions 2021-09-11 11:38:06 +03:00
Lion Kortlepel
b055fd8bda GracefullyShutdown: Add "subsystem x/y shutting down" message
Remove old "X shutting down", "X shut down" messages, they were bad and
confusing
2021-09-11 11:38:06 +03:00
Lion Kortlepel
d43ee4b7b6 Bump version to 2.3.2 2021-09-11 11:38:06 +03:00
Lion Kortlepel
a514591650 Main: Add Ctrl+C handler for windows 2021-09-11 11:38:06 +03:00
Lion Kortlepel
0f9f81e9fa Http: Add cloudflare 5XX status code strings 2021-09-11 11:38:06 +03:00
Lion Kortlepel
11e94e91a7 Update Changelog.md to reflect latest changes in recent merge from #44 2021-09-10 13:41:45 +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
Lion Kortlepel
7bc230974a fix typo 2021-06-23 23:50:18 +02:00
Anonymous-275
b1ab7e65da Possible windows compile fix 2021-06-23 20:03:42 +03:00
Lion Kortlepel
c82c53e3d1 remove debug prints 2021-06-23 16:40:08 +02:00
Lion Kortlepel
9e861ab993 parse old Server.cfg if it exists 2021-06-23 16:39:08 +02:00
Lion Kortlepel
ae11ba5f0d bump version to 2.0.4 2021-06-23 11:55:47 +02:00
Lion Kortlepel
1427966d1a private false by default 2021-06-23 02:08:21 +02:00
Lion Kortlepel
db92f2867d put a line between header and content of toml 2021-06-23 01:54:17 +02:00
Lion Kortlepel
208a41d45f remove unused 2021-06-23 01:54:17 +02:00
Lion Kortlepel
f2915f9c2a remove dead code 2021-06-23 01:54:17 +02:00
Lion Kortlepel
1abf6d5adf Fully implement TOML config file, delete .idea folder
The new config is called "ServerConfig.toml" and uses the TOML library.
It's nice.
2021-06-23 01:54:17 +02:00
Lion Kortlepel
f626474b4f add TOML++ 2021-06-23 01:54:17 +02:00
Lion Kortlepel
6f3960d2c2 final commit for the fix to linux lua - fix #27 2021-06-23 01:52:39 +02:00
Lion Kortlepel
6cb19a7991 (possibly) Fix linux lua issue 2021-06-23 01:47:37 +02:00
Lion Kortlepel
4fe3c50edd update commandline, fix #24 2021-06-18 11:24:17 +02:00
Lion Kortlepel
fe7335fb0e github actions: run only on push, not PR, since then it runs twice 2021-06-18 10:53:44 +02:00
Lion Kortlepel
40cd203047 update commandline library in a fix attempt for #24 2021-06-18 10:44:53 +02:00
Lion Kortlepel
1fc03500f0 update lionkor/commandline to latest 2021-06-17 12:53:21 +02:00
Lion Kortlepel
73729746ff update gitignore with some common unix stuff and notes folder 2021-06-17 12:51:44 +02:00
Anonymous275
ffdf4dce60 Update LICENSE 2021-06-05 21:42:17 +03:00
Anonymous275
bb5d7fdcf4 Update README.md 2021-06-05 21:41:51 +03:00
Anonymous275
2df2475b59 Update LICENSE 2021-06-05 21:40:54 +03:00
Anonymous275
bb32b9cfea Update README.md 2021-06-05 21:40:26 +03:00
Anonymous275
3f034264f9 Update LICENSE 2021-06-05 21:39:53 +03:00
Anonymous275
c67fffed58 Update README.md 2021-06-02 12:16:33 +03:00
Lion
f50b821733 Clarify ways to contribute 2021-05-28 12:26:22 +02:00
Lion
1e5d19bca4 Update README.md 2021-05-28 12:13:12 +02:00
Lion
1011de1971 Update README.md
Add a section about contributing
2021-05-27 16:58:50 +02:00
Anonymous-275
7336d63f58 Added N packet flag 2021-05-23 19:21:04 +03:00
Anonymous-275
c5d7369088 fixed std::unique_lock not a member of std 2021-05-22 14:17:55 +03:00
Lion
c3a463552f Update README.md 2021-05-01 17:46:59 +02:00
Lion
ae9462898e Update README.md
fixes #22 

@Anonymous-275 please look over
2021-05-01 17:46:02 +02:00
Anonymous-275
529b7e2ae4 Potential mod download timeout crash fix 2021-04-25 19:09:08 +03:00
Anonymous-275
1bee72a175 Server config now uses json 2021-04-22 02:28:02 +03:00
Lion Kortlepel
f1e1b6cc28 remove boost from runtime dependencies 2021-04-09 23:56:09 +02:00
Lion Kortlepel
573bd77bbd v2.0.3 2021-04-06 12:50:39 +02:00
Lion Kortlepel
770e03e3c6 update lionkor/commandline to include build speedup 2021-04-06 11:25:16 +02:00
Lion Kortlepel
11d4522dc7 update lionkor/commandline to fix windows build 2021-04-05 23:27:32 +02:00
Lion Kortlepel
830bc47987 update lionkor/commandline
Fixes the 100% CPU spin issue when stdin was /dev/null or similar.
Thanks to @Worty for implementing this fix
2021-04-05 14:47:26 +02:00
Lion Kortlepel
bec698fbdc temporary fix for timeout during sync 2021-04-04 02:57:25 +02:00
Lion Kortlepel
60cc835daf remove random 2 second sleep on every car sync (!) 2021-04-04 02:26:51 +02:00
Lion Kortlepel
a85ce18589 Implement possible ghost player fix
coauthor @Anonymous-275
2021-04-04 01:44:40 +02:00
48 changed files with 1632 additions and 476 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

@@ -1,6 +1,6 @@
name: CMake Linux Build
on: [push, pull_request]
on: [push]
env:
BUILD_TYPE: Release
@@ -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

@@ -1,6 +1,6 @@
name: CMake Windows Build
on: [push, pull_request]
on: [push]
env:
BUILD_TYPE: Release
@@ -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

@@ -24,7 +24,7 @@ jobs:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: false
prerelease: true
body: |
Files included in this release:
- `BeamMP-Server.exe` is the windows build
@@ -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

10
.gitignore vendored
View File

@@ -1,5 +1,9 @@
.idea/
*.orig
*.toml
boost_*
Resources
run-in-env.sh
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
@@ -468,3 +472,9 @@ cmake-build-debug/include/commandline/Makefile
*.manifest
*.rc
*.res
BeamMP-Server
*.patch
callgrind.*
notes/*
compile_commands.json
nohup.out

6
.gitmodules vendored
View File

@@ -10,3 +10,9 @@
[submodule "rapidjson"]
path = rapidjson
url = https://github.com/Tencent/rapidjson
[submodule "include/tomlplusplus"]
path = include/tomlplusplus
url = https://github.com/marzer/tomlplusplus
[submodule "include/sentry-native"]
path = include/sentry-native
url = https://github.com/getsentry/sentry-native

1
.idea/.name generated
View File

@@ -1 +0,0 @@
Server

4
.idea/misc.xml generated
View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

16
.idea/vcs.xml generated
View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/asio" vcs="Git" />
<mapping directory="$PROJECT_DIR$/include/commandline" vcs="Git" />
<mapping directory="$PROJECT_DIR$/rapidjson" vcs="Git" />
<mapping directory="$PROJECT_DIR$/rapidjson/thirdparty/gtest" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/asio" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/catch" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/rapidjson/thirdparty/gtest" vcs="Git" />
<mapping directory="$PROJECT_DIR$/socket.io-client-cpp/lib/websocketpp" vcs="Git" />
</component>
</project>

View File

@@ -1,10 +1,30 @@
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)
@@ -12,13 +32,26 @@ if (WIN32)
elseif (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -gz -fno-builtin")
if (SANITIZE)
message(STATUS "sanitize is ON")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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")
@@ -27,9 +60,9 @@ 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 +79,57 @@ 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)
include/TNetwork.h src/TNetwork.cpp
include/SignalHandling.h src/SignalHandling.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")
target_include_directories(BeamMP-Server PUBLIC
${Boost_INCLUDE_DIRS}
${LUA_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
"socket.io-client-cpp/src"
"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 ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline sioclient_tls)
target_link_libraries(BeamMP-Server
z
pthread
stdc++fs
${LUA_LIBRARIES}
crypto
${OPENSSL_LIBRARIES}
commandline
sioclient_tls
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} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls)
target_link_libraries(BeamMP-Server PRIVATE
ws2_32
ZLIB::ZLIB
${LUA_LIBRARIES}
${OPENSSL_LIBRARIES}
commandline
sioclient_tls
sentry)
endif ()

82
Changelog.md Normal file
View File

@@ -0,0 +1,82 @@
# v2.3.3
- CHANGED servers to be private by default
# v2.3.2
- ADDED Ctrl+C causes a graceful shutdown on windows (did already on linux)
- ADDED more meaningful shutdown messages
- ADDED even better backend connection error reporting
- ADDED `SendErrors` config in `ServerConfig.toml` to opt-out of error reporting
- ADDED hard-shutdown if Ctrl+C pressed 3 times
- FIXED issue with shells like bash being unusable after server exit
# v2.3.1
- CHANGED join/sync timeout to 20 minutes, players wont drop if loading takes >5 mins
# v2.3.0
- 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

@@ -1 +1,2 @@
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor). BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.

View File

@@ -6,9 +6,30 @@
This is the server for the multiplayer mod **[BeamMP](https://beammp.com/)** for the game [BeamNG.drive](https://www.beamng.com/).
The server is the point throug which all clients communicate. You can write lua mods for the server, detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com).
## Minimum Requirements
These values are guesstimated and are subject to change with each release.
* RAM: 50+ MiB usable (not counting OS overhead)
* CPU: >1GHz, preferably multicore
* OS: Windows, Linux (theoretically any POSIX)
* GPU: None
* HDD: 10 MiB + Mods/Plugins
* Bandwidth: 5-10 Mb/s upload
## Contributing
TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" label or with nobody assigned, any [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer) cards in the "To-Do" column.
To contribute, look at the active [issues](https://github.com/BeamMP/BeamMP-Server/issues) and at the [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer). Any issues that have the "help wanted" label or don't have anyone assigned and any trello cards that aren't assigned or in the "In-Progress" section are good tasks to take on. You can either contribute by programming or by testing and adding more info and ideas.
Fork this repository, make a new branch for your feature, implement your feature or fix, and then create a pull-request here. Even incomplete features and fixes can be pull-requested.
If you need support with understanding the codebase, please write us in the discord. You'll need to be proficient in modern C++.
## About Building from Source
We only allow building unmodified (original) source code. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`.
We only allow building unmodified (original) source code for public use. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`.
## Supported Operating Systems
@@ -40,27 +61,28 @@ 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`
- `libboost1.70-dev`
If your distro doesn't have 1.7x version of libboost, you'll have to compile it from source or find another way to get it for your distro.
- `libopenssl-dev`
- `libopenssl-dev` or `libssl-dev`
**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`).
### 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`**. Now change into the cloned directory by running `cd BeamMP-Server`.
3. Ensure that all submodules are initialized by running `git submodule update --init --recursive`. Then change into the cloned directory by running `cd BeamMP-Server`.
4. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
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.
@@ -71,3 +93,4 @@ On windows. use git-bash for these commands.
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor).
BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.

View File

@@ -83,7 +83,7 @@ private:
std::mutex mVehicleDataMutex;
TSetOfVehicleData mVehicleData;
std::string mName = "Unknown Client";
SOCKET mSocket[2] { SOCKET(-1) };
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is
int mUnicycleID = -1;
std::string mRole;

View File

@@ -1,5 +1,9 @@
#pragma once
#include "TSentry.h"
extern TSentry Sentry;
#include <array>
#include <atomic>
#include <deque>
#include <functional>
@@ -18,18 +22,30 @@ public:
// types
struct TSettings {
TSettings() noexcept
: DebugModeEnabled(true) { }
: ServerName("BeamMP Server")
, ServerDesc("BeamMP Default Description")
, Resource("Resources")
, MapName("/levels/gridmap_v2/info.json")
, MaxPlayers(10)
, Private(true)
, MaxCars(1)
, DebugModeEnabled(false)
, Port(30814)
, SendErrors(true)
, SendErrorsMessageEnabled(true) { }
std::string ServerName;
std::string ServerDesc;
std::string Resource;
std::string MapName;
std::string Key;
int MaxPlayers {};
bool Private {};
int MaxCars {};
int MaxPlayers;
bool Private;
int MaxCars;
bool DebugModeEnabled;
int Port {};
int Port;
std::string CustomIP;
bool SendErrors;
bool SendErrorsMessageEnabled;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
};
using TShutdownHandler = std::function<void()>;
@@ -42,16 +58,21 @@ 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.0.2"; }
static std::string ServerVersion() { return "2.3.3"; }
static std::string ClientVersion() { 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<int, 3> VersionStrToInts(const std::string& str);
static bool IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest);
private:
static inline std::string mPPS;
@@ -60,12 +81,13 @@ private:
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
};
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
#define MB (KB * 1024)
#define SSU_UNRAW SECRET_SENTRY_URL
#define _file_basename std::filesystem::path(__FILE__).filename().string()
#define _line std::to_string(__LINE__)
@@ -91,16 +113,22 @@ void RegisterThread(const std::string str);
#else
#define _this_location (ThreadName() + _file_basename + ":" + _line + " ")
#endif
#define SU_RAW SSU_UNRAW
#else // !defined(DEBUG)
#define SU_RAW RAWIFY(SSU_UNRAW)
#define _this_location (ThreadName())
#endif // defined(DEBUG)
#define 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 { \
@@ -108,8 +136,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);
#define S_DSN SU_RAW

View File

@@ -12,9 +12,9 @@ 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 +23,9 @@ 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

114
include/Cryptography.h Normal file
View File

@@ -0,0 +1,114 @@
// Copyright Anonymous275 8/11/2020
#pragma once
#include <array>
#include <cstdarg>
#include <string>
namespace Crypto {
constexpr auto time = __TIME__;
constexpr auto seed = static_cast<int>(time[7]) + static_cast<int>(time[6]) * 10 + static_cast<int>(time[4]) * 60 + static_cast<int>(time[3]) * 600 + static_cast<int>(time[1]) * 3600 + static_cast<int>(time[0]) * 36000;
// 1988, Stephen Park and Keith Miller
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
// with 32-bit math and without division
template <int N>
struct RandomGenerator {
private:
static constexpr unsigned a = 16807; // 7^5
static constexpr unsigned m = 2147483647; // 2^31 - 1
static constexpr unsigned s = RandomGenerator<N - 1>::value;
static constexpr unsigned lo = a * (s & 0xFFFFu); // Multiply lower 16 bits by 16807
static constexpr unsigned hi = a * (s >> 16u); // Multiply higher 16 bits by 16807
static constexpr unsigned lo2 = lo + ((hi & 0x7FFFu) << 16u); // Combine lower 15 bits of hi with lo's upper bits
static constexpr unsigned hi2 = hi >> 15u; // Discard lower 15 bits of hi
static constexpr unsigned lo3 = lo2 + hi;
public:
static constexpr unsigned max = m;
static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
};
template <>
struct RandomGenerator<0> {
static constexpr unsigned value = seed;
};
template <int N, int M>
struct RandomInt {
static constexpr auto value = RandomGenerator<N + 1>::value % M;
};
template <int N>
struct RandomChar {
static const char value = static_cast<char>(1 + RandomInt<N, 0x7F - 1>::value);
};
template <size_t N, int K, typename Char>
struct MangleString {
private:
const char _key;
std::array<Char, N + 1> _encrypted;
constexpr Char enc(Char c) const {
return c ^ _key;
}
Char dec(Char c) const {
return c ^ _key;
}
public:
template <size_t... Is>
constexpr MangleString(const Char* str, std::index_sequence<Is...>)
: _key(RandomChar<K>::value)
, _encrypted { enc(str[Is])... } { }
decltype(auto) decrypt() {
for (size_t i = 0; i < N; ++i) {
_encrypted[i] = dec(_encrypted[i]);
}
_encrypted[N] = '\0';
return _encrypted.data();
}
};
static auto w_printf = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
};
static auto w_printf_s = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
};
static auto w_sprintf_s = [](char* buf, size_t buf_size, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
};
static auto w_sprintf_s_ret = [](char* buf, size_t buf_size, const char* fmt, ...) {
int ret;
va_list args;
va_start(args, fmt);
ret = vsprintf(buf, fmt, args);
va_end(args);
return ret;
};
#define XOR_C(s) [] { constexpr Crypto::MangleString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char > expr( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ); return expr; }().decrypt()
#define XOR_W(s) [] { constexpr Crypto::MangleString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t > expr( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ); return expr; }().decrypt()
#define RAWIFY(s) XOR_C(s)
}

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,9 @@
#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, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json, int* status = nullptr);
namespace Status {
std::string ToString(int code);
}
}

9
include/Json.h Normal file
View File

@@ -0,0 +1,9 @@
//
// Created by anon on 4/21/21.
//
#pragma once
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"

View File

@@ -7,6 +7,7 @@
*/
#include <shared_mutex>
#include <mutex>
// Use ReadLock(m) and WriteLock(m) to lock it.
using RWMutex = std::shared_mutex;

3
include/SignalHandling.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
void SetupSignalHandlers();

View File

@@ -2,11 +2,20 @@
#include "Common.h"
#include <atomic>
class TConfig {
public:
explicit TConfig(const std::string& ConfigFile);
explicit TConfig();
[[nodiscard]] bool Failed() const { return mFailed; }
private:
static std::string RemoveComments(const std::string& Line);
static void SetValues(const std::string& Line, int Index);
void CreateConfigFile(std::string_view name);
void ParseFromFile(std::string_view name);
void PrintDebug();
void ParseOldFormat();
bool mFailed { false };
};

View File

@@ -2,6 +2,7 @@
#include "commandline/commandline.h"
#include "TLuaFile.h"
#include "Cryptography.h"
#include <atomic>
#include <fstream>

View File

@@ -27,12 +27,13 @@ public:
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

@@ -54,6 +54,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

@@ -44,6 +44,6 @@ private:
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked);
void Parse(TClient& c, const std::string& Packet);
void SendFile(TClient& c, const std::string& Name);
static bool TCPSendRaw(SOCKET C, char* Data, int32_t Size);
static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size);
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
};

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/tomlplusplus Submodule

Submodule include/tomlplusplus added at bc6891e1fb

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) {
@@ -17,10 +23,74 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
}
void Application::GracefullyShutdown() {
info("please wait while all subsystems are shutting down...");
static bool AlreadyShuttingDown = false;
static uint8_t ShutdownAttempts = 0;
if (AlreadyShuttingDown) {
++ShutdownAttempts;
// hard shutdown at 2 additional tries
if (ShutdownAttempts == 2) {
info("hard shutdown forced by multiple shutdown requests");
std::exit(0);
}
info("already shutting down!");
return;
} else {
AlreadyShuttingDown = true;
}
trace("waiting for lock release");
std::unique_lock Lock(mShutdownHandlersMutex);
for (auto& Handler : mShutdownHandlers) {
Handler();
info("please wait while all subsystems are shutting down...");
for (size_t i = 0; i < mShutdownHandlers.size(); ++i) {
info("Subsystem " + std::to_string(i + 1) + "/" + std::to_string(mShutdownHandlers.size()) + " shutting down");
mShutdownHandlers[i]();
}
}
std::array<int, 3> Application::VersionStrToInts(const std::string& str) {
std::array<int, 3> Version;
std::stringstream ss(str);
for (int& i : Version) {
std::string Part;
std::getline(ss, Part, '.');
std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i);
}
return Version;
}
bool Application::IsOutdated(const std::array<int, 3>& Current, const std::array<int, 3>& Newest) {
if (Newest[0] > Current[0]) {
return true;
} else if (Newest[0] == Current[0] && Newest[1] > Current[1]) {
return true;
} else if (Newest[0] == Current[0] && Newest[1] == Current[1] && Newest[2] > Current[2]) {
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 = VersionStrToInts(ServerVersion());
auto RemoteVersion = VersionStrToInts(Response);
if (IsOutdated(MyVersion, RemoteVersion)) {
std::string RealVersionString = std::to_string(RemoteVersion[0]) + ".";
RealVersionString += std::to_string(RemoteVersion[1]) + ".";
RealVersionString += std::to_string(RemoteVersion[2]);
warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
} else {
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);
}
}
@@ -70,10 +140,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 +155,19 @@ 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;
}
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;
Application::Console().Write(ss.str());
}

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,85 @@ 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
std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) {
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);
// Check command line arguments.
int version = 11;
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
// The io_context is required for all I/O
net::io_context ioc;
req.set(http::field::host, host);
// tell the server what we are (boost beast)
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// The SSL context is required, and holds certificates
ssl::context ctx(ssl::context::tlsv12_client);
http::write(stream, req);
// This holds the root certificate used for verification
// we don't do / have this
// load_root_certificates(ctx);
// used for reading
beast::flat_buffer buffer;
http::response<http::string_body> response;
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_none);
http::read(stream, buffer, response);
// These objects perform our I/O
tcp::resolver resolver(ioc);
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
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
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
beast::error_code ec { static_cast<int>(::ERR_get_error()), net::error::get_ssl_category() };
throw beast::system_error { ec };
}
return result;
// Look up the domain name
auto const results = resolver.resolve(host.c_str(), std::to_string(port));
} catch (const std::exception& e) {
Application::Console().Write(e.what());
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream).connect(results);
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client);
// Set up an HTTP GET request message
http::request<http::string_body> req { http::verb::get, target, version };
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::string_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Gracefully close the stream
beast::error_code ec;
stream.shutdown(ec);
if (ec == net::error::eof) {
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec = {};
}
if (status) {
*status = res.base().result_int();
}
// ignore ec
// If we get here then the connection is closed gracefully
return std::string(res.body());
} catch (std::exception const& e) {
Application::Console().Write(__func__ + std::string(": ") + e.what());
return "-1";
}
}
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
std::string Http::POST(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json, int* status) {
try {
net::io_context io;
@@ -87,7 +126,8 @@ 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);
Sentry.AddErrorBreadcrumb("failed to resolve or connect to " + host + target, __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
return "-1";
}
//}
@@ -97,7 +137,6 @@ std::string Http::POST(const std::string& host, const std::string& target, const
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");
@@ -112,6 +151,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 +174,20 @@ 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);
std::stringstream result;
result << response;
@@ -140,6 +203,92 @@ std::string Http::POST(const std::string& host, const std::string& target, const
} catch (const std::exception& e) {
Application::Console().Write(e.what());
Sentry.AddErrorBreadcrumb(e.what(), __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly.
return "-1";
}
}
// RFC 2616, RFC 7231
static std::map<size_t, const char*> Map = {
{ -1, "Invalid Response Code"},
{ 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" },
// cloudflare status codes
{ 520, "(CDN) Web Server Returns An Unknown Error" },
{ 521, "(CDN) Web Server Is Down" },
{ 522, "(CDN) Connection Timed Out" },
{ 523, "(CDN) Origin Is Unreachable" },
{ 524, "(CDN) A Timeout Occurred" },
{ 525, "(CDN) SSL Handshake Failed" },
{ 526, "(CDN) Invalid SSL Certificate" },
{ 527, "(CDN) Railgun Listener To Origin Error" },
{ 530, "(CDN) 1XXX Internal Error" },
};
std::string Http::Status::ToString(int code) {
if (Map.find(code) != Map.end()) {
return Map.at(code);
} else {
return std::to_string(code);
}
}

65
src/SignalHandling.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "SignalHandling.h"
#include "Common.h"
#ifdef __unix
#include <csignal>
static void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
warn("ignoring SIGPIPE");
break;
case SIGTERM:
info("gracefully shutting down via SIGTERM");
Application::GracefullyShutdown();
break;
case SIGINT:
info("gracefully shutting down via SIGINT");
Application::GracefullyShutdown();
break;
default:
debug("unhandled signal: " + std::to_string(sig));
break;
}
}
#endif // __unix
#ifdef WIN32
#include <windows.h>
// return TRUE if handled, FALSE if not
BOOL WINAPI Win32CtrlC_Handler(DWORD CtrlType) {
switch (CtrlType) {
case CTRL_C_EVENT:
info("gracefully shutting down via CTRL+C");
Application::GracefullyShutdown();
return TRUE;
case CTRL_BREAK_EVENT:
info("gracefully shutting down via CTRL+BREAK");
Application::GracefullyShutdown();
return TRUE;
case CTRL_CLOSE_EVENT:
info("gracefully shutting down via close");
Application::GracefullyShutdown();
return TRUE;
}
// we dont care for any others like CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT
return FALSE;
}
#endif // WIN32
void SetupSignalHandlers() {
// signal handlers for unix#include <windows.h>
#ifdef __unix
trace("registering handlers for SIGINT, SIGTERM, SIGPIPE");
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
#ifndef DEBUG
signal(SIGINT, UnixSignalHandler);
#endif // DEBUG
#endif // __unix
// signal handlers for win32
#ifdef WIN32
trace("registering handlers for CTRL_*_EVENTs");
SetConsoleCtrlHandler(Win32CtrlC_Handler, TRUE);
#endif // WIN32
}

View File

@@ -1,126 +1,240 @@
#include "../include/TConfig.h"
#include <toml.hpp> // header-only version of TOML++
#include "TConfig.h"
#include <fstream>
#include <iostream>
#include <istream>
#include <sstream>
TConfig::TConfig(const std::string& ConfigFile) {
std::ifstream File(ConfigFile);
if (File.good()) {
std::string line;
int index = 1;
while (getline(File, line)) {
index++;
static const char* ConfigFileName = static_cast<const char*>("ServerConfig.toml");
static constexpr std::string_view StrDebug = "Debug";
static constexpr std::string_view StrPrivate = "Private";
static constexpr std::string_view StrPort = "Port";
static constexpr std::string_view StrMaxCars = "MaxCars";
static constexpr std::string_view StrMaxPlayers = "MaxPlayers";
static constexpr std::string_view StrMap = "Map";
static constexpr std::string_view StrName = "Name";
static constexpr std::string_view StrDescription = "Description";
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
static constexpr std::string_view StrAuthKey = "AuthKey";
static constexpr std::string_view StrSendErrors = "SendErrors";
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
TConfig::TConfig() {
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
info("No config file found! Generating one...");
CreateConfigFile(ConfigFileName);
}
if (!mFailed) {
if (fs::exists("Server.cfg")) {
warn("An old \"Server.cfg\" file still exists. Please note that this is no longer used. Instead, \"" + std::string(ConfigFileName) + "\" is used. You can safely delete the \"Server.cfg\".");
}
if (index - 1 < 11) {
error(("Outdated/Incorrect config please remove it server will close in 5 secs"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}
File.close();
File.open(("Server.cfg"));
info(("Config found updating values"));
index = 1;
while (std::getline(File, line)) {
if (line.rfind('#', 0) != 0 && line.rfind(' ', 0) != 0) { //Checks if it starts as Comment
std::string CleanLine = RemoveComments(line); //Cleans it from the Comments
SetValues(CleanLine, index); //sets the values
index++;
}
ParseFromFile(ConfigFileName);
}
}
void WriteSendErrors(const std::string& name) {
std::ofstream CfgFile { name, std::ios::out | std::ios::app };
CfgFile << "# You can turn on/off the SendErrors message you get on startup here" << std::endl
<< StrSendErrorsMessageEnabled << " = true" << std::endl
<< "# If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`."
<< std::endl
<< StrSendErrors << " = true" << std::endl;
}
void TConfig::CreateConfigFile(std::string_view name) {
// build from old config Server.cfg
try {
if (fs::exists("Server.cfg")) {
// parse it (this is weird and bad and should be removed in some future version)
ParseOldFormat();
}
} catch (const std::exception& e) {
error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
}
toml::table tbl { {
{ "General",
toml::table { {
{ 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 },
//{ StrSendErrors, Application::Settings.SendErrors },
} } },
} };
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';
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;
ofs.close();
WriteSendErrors(std::string(name));
} else {
info(("Config not found generating default"));
std::ofstream FileStream;
FileStream.open(ConfigFile);
// TODO REPLACE THIS SHIT OMG
FileStream << ("# This is the BeamMP Server Configuration File v0.60\n"
"Debug = false # true or false to enable debug console output\n"
"Private = true # Private?\n"
"Port = 30814 # Port to run the server on UDP and TCP\n"
"Cars = 1 # Max cars for every player\n"
"MaxPlayers = 10 # Maximum Amount of Clients\n"
"Map = \"/levels/gridmap/info.json\" # Default Map\n"
"Name = \"BeamMP New Server\" # Server Name\n"
"Desc = \"BeamMP Default Description\" # Server Description\n"
"use = \"Resources\" # Resource file name\n"
"AuthKey = \"\" # Auth Key");
FileStream.close();
error(("You are required to input the AuthKey"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
mFailed = true;
}
debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
debug("Private : " + std::string(Application::Settings.Private ? "true" : "false"));
debug("Port : " + std::to_string(Application::Settings.Port));
debug("Max Cars : " + std::to_string(Application::Settings.MaxCars));
debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers));
debug("MapName : \"" + Application::Settings.MapName + "\"");
debug("ServerName : \"" + Application::Settings.ServerName + "\"");
debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\"");
debug("File : \"" + Application::Settings.Resource + "\"");
debug("Key length : " + std::to_string(Application::Settings.Key.length()) + "");
}
std::string TConfig::RemoveComments(const std::string& Line) {
std::string Return;
for (char c : Line) {
if (c == '#')
break;
Return += c;
}
return Return;
}
void TConfig::SetValues(const std::string& Line, int Index) {
int state = 0;
std::string Data;
bool Switch = false;
if (Index > 5)
Switch = true;
for (char c : Line) {
if (Switch) {
if (c == '\"')
state++;
if (state > 0 && state < 2)
Data += c;
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 {
if (c == ' ')
state++;
if (state > 1)
Data += c;
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));
}
// added later, so behaves differently
if (auto val = GeneralTable[StrSendErrors].value<bool>(); val.has_value()) {
Application::Settings.SendErrors = val.value();
} else {
// dont throw, instead write it into the file and use default
WriteSendErrors(std::string(name));
}
if (auto val = GeneralTable[StrSendErrorsMessageEnabled].value<bool>(); val.has_value()) {
Application::Settings.SendErrorsMessageEnabled = val.value();
} else {
// no idea what to do here, ignore...?
// this entire toml parser sucks and is replaced in the upcoming lua.
}
} catch (const std::exception& err) {
error("Error parsing config file value: " + std::string(err.what()));
mFailed = true;
return;
}
PrintDebug();
// all good so far, let's check if there's a key
if (Application::Settings.Key.empty()) {
error("No AuthKey specified in the \"" + std::string(ConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server.");
mFailed = true;
}
}
void TConfig::PrintDebug() {
debug(std::string(StrDebug) + ": " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.Private ? "true" : "false"));
debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.Port));
debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.MaxCars));
debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.MaxPlayers));
debug(std::string(StrMap) + ": \"" + Application::Settings.MapName + "\"");
debug(std::string(StrName) + ": \"" + Application::Settings.ServerName + "\"");
debug(std::string(StrDescription) + ": \"" + Application::Settings.ServerDesc + "\"");
debug(std::string(StrResourceFolder) + ": \"" + Application::Settings.Resource + "\"");
// special!
debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
}
void TConfig::ParseOldFormat() {
std::ifstream File("Server.cfg");
// read all, strip comments
std::string Content;
for (;;) {
std::string Line;
std::getline(File, Line);
if (!Line.empty() && Line.at(0) != '#') {
Line = Line.substr(0, Line.find_first_of('#'));
Content += Line + "\n";
}
if (!File.good()) {
break;
}
}
Data = Data.substr(1);
std::string::size_type sz;
bool FoundTrue = std::string(Data).find("true") != std::string::npos; //searches for "true"
switch (Index) {
case 1:
Application::Settings.DebugModeEnabled = FoundTrue; //checks and sets the Debug Value
break;
case 2:
Application::Settings.Private = FoundTrue; //checks and sets the Private Value
break;
case 3:
Application::Settings.Port = std::stoi(Data, &sz); //sets the Port
break;
case 4:
Application::Settings.MaxCars = std::stoi(Data, &sz); //sets the Max Car amount
break;
case 5:
Application::Settings.MaxPlayers = std::stoi(Data, &sz); //sets the Max Amount of player
break;
case 6:
Application::Settings.MapName = Data; //Map
break;
case 7:
Application::Settings.ServerName = Data; //Name
break;
case 8:
Application::Settings.ServerDesc = Data; //desc
break;
case 9:
Application::Settings.Resource = Data; //File name
break;
case 10:
Application::Settings.Key = Data; //File name
default:
break;
std::stringstream Str(Content);
std::string Key, Ignore, Value;
for (;;) {
Str >> Key >> std::ws >> Ignore >> std::ws;
std::getline(Str, Value);
if (Str.eof()) {
break;
}
std::stringstream ValueStream(Value);
ValueStream >> std::ws; // strip leading whitespace if any
Value = ValueStream.str();
if (Key == "Debug") {
Application::Settings.DebugModeEnabled = Value.find("true") != std::string::npos;
} else if (Key == "Private") {
Application::Settings.Private = Value.find("true") != std::string::npos;
} else if (Key == "Port") {
ValueStream >> Application::Settings.Port;
} else if (Key == "Cars") {
ValueStream >> Application::Settings.MaxCars;
} else if (Key == "MaxPlayers") {
ValueStream >> Application::Settings.MaxPlayers;
} else if (Key == "Map") {
Application::Settings.MapName = Value.substr(1, Value.size() - 3);
} else if (Key == "Name") {
Application::Settings.ServerName = Value.substr(1, Value.size() - 3);
} else if (Key == "Desc") {
Application::Settings.ServerDesc = Value.substr(1, Value.size() - 3);
} else if (Key == "use") {
Application::Settings.Resource = Value.substr(1, Value.size() - 3);
} else if (Key == "AuthKey") {
Application::Settings.Key = Value.substr(1, Value.size() - 3);
} else {
warn("unknown key in old auth file (ignored): " + Key);
}
Str >> std::ws;
}
}

View File

@@ -3,8 +3,12 @@
#include "Client.h"
#include "Http.h"
//#include "SocketIO.h"
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
#include <sstream>
namespace json = rapidjson;
void THeartbeatThread::operator()() {
RegisterThread("Heartbeat");
std::string Body;
@@ -26,9 +30,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,33 +39,92 @@ void THeartbeatThread::operator()() {
Body += "&pps=" + Application::PPS();
T = Http::POST(Application::GetBackendHostname(), "/heartbeat", {}, Body, false);
auto SentryReportError = [&](const std::string& transaction, int status) {
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("heartbeat",
{ { "response-body", T },
{ "request-body", Body } });
Sentry.SetTransaction(transaction);
Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")");
};
if (T.substr(0, 2) != "20") {
//Backend system refused server startup!
auto Target = "/heartbeat";
int ResponseCode = -1;
const std::vector<std::string> Urls = {
Application::GetBackendHostname(),
Application::GetBackup1Hostname(),
Application::GetBackup2Hostname(),
};
json::Document Doc;
bool Ok = false;
for (const auto& Url : Urls) {
T = Http::POST(Url, Target, { { "api-v", "2" } }, Body, false, &ResponseCode);
trace(T);
Doc.Parse(T.data(), T.size());
if (Doc.HasParseError() || !Doc.IsObject()) {
error("Backend response failed to parse as valid json");
debug("Response was: `" + T + "`");
Sentry.SetContext("JSON Response", { { "reponse", T } });
SentryReportError(Url + Target, ResponseCode);
} else if (ResponseCode != 200) {
SentryReportError(Url + Target, ResponseCode);
} else {
// all ok
Ok = true;
break;
}
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;
}
std::string Status {};
std::string Code {};
std::string Message {};
const auto StatusKey = "status";
const auto CodeKey = "code";
const auto MessageKey = "msg";
if (Ok) {
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
Status = Doc[StatusKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
Code = Doc[CodeKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } });
Ok = false;
}
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
Message = Doc[MessageKey].GetString();
} else {
Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } });
Ok = false;
}
if (!Ok) {
error("Missing/invalid json members in backend response");
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
}
}
if (!isAuth) {
if (T == "2000") {
if (Ok && !isAuth) {
if (Status == "2000") {
info(("Authenticated!"));
isAuth = true;
} else if (T == "200") {
} else if (Status == "200") {
info(("Resumed authenticated session!"));
isAuth = true;
} else {
if (Message.empty()) {
Message = "Backend didn't provide a reason";
}
error("Backend REFUSED the auth key. " + Message);
}
}
//SocketIO::Get().SetAuthenticated(isAuth);
}
}
std::string THeartbeatThread::GenerateCall() {
std::stringstream Ret;
@@ -88,10 +149,8 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S
, mServer(Server) {
Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) {
debug("shutting down Heartbeat");
mShutdown = true;
mThread.join();
debug("shut down Heartbeat");
}
});
Start();

View File

@@ -23,10 +23,8 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
FolderList(Path, false);
mPath = Path;
Application::RegisterShutdownHandler([&] {if (mThread.joinable()) {
debug("shutting down LuaEngine");
mShutdown = true;
mThread.join();
debug("shut down LuaEngine");
} });
Start();
}
@@ -66,37 +64,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,6 +2,7 @@
#include "Client.h"
#include "Common.h"
#include "CustomAssert.h"
#include "Defer.h"
#include "TLuaEngine.h"
#include "TNetwork.h"
#include "TServer.h"
@@ -44,7 +45,6 @@ void ClearStack(lua_State* L) {
}
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();
@@ -56,6 +56,7 @@ std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> a
SendError(lua->Engine(), lua->GetState(), R + " took too long to respond");
return 0;
}
std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) {
Assert(lua);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return Trigger(lua, R, arg); });
@@ -70,17 +71,16 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
return f1.get();
return 0;
}
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")
@@ -88,8 +88,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")
@@ -99,6 +98,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
}
return Ret;
}
bool ConsoleCheck(lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
@@ -107,9 +107,16 @@ bool ConsoleCheck(lua_State* L, int r) {
}
return true;
}
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();
@@ -117,7 +124,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;
@@ -126,7 +133,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));
@@ -134,10 +144,14 @@ int lua_RegisterEvent(lua_State* L) {
SendError(Engine(), L, "RegisterEvent invalid argument count expected 2 got " + std::to_string(Args));
return 0;
}
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)) {
@@ -153,7 +167,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)) {
@@ -181,7 +198,6 @@ 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()) {
@@ -192,11 +208,15 @@ 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;
}
int lua_CreateThread(lua_State* L) {
int Args = lua_gettop(L);
if (Args > 1) {
@@ -206,7 +226,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();
@@ -220,6 +243,7 @@ int lua_CreateThread(lua_State* L) {
SendError(Engine(), L, ("CreateThread not enough arguments"));
return 0;
}
int lua_Sleep(lua_State* L) {
if (lua_isnumber(L, 1)) {
int t = int(lua_tonumber(L, 1));
@@ -230,6 +254,7 @@ int lua_Sleep(lua_State* L) {
}
return 1;
}
std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
std::optional<std::weak_ptr<TClient>> MaybeClient { std::nullopt };
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool {
@@ -245,7 +270,7 @@ std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
});
return MaybeClient;
}
// CONTINUE
int lua_isConnected(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -260,6 +285,7 @@ int lua_isConnected(lua_State* L) {
}
return 1;
}
int lua_GetPlayerName(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -274,10 +300,12 @@ int lua_GetPlayerName(lua_State* L) {
}
return 1;
}
int lua_GetPlayerCount(lua_State* L) {
lua_pushinteger(L, Engine().Server().ClientCount());
return 1;
}
int lua_GetGuest(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -292,6 +320,7 @@ int lua_GetGuest(lua_State* L) {
}
return 1;
}
int lua_GetAllPlayers(lua_State* L) {
lua_newtable(L);
Engine().Server().ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
@@ -311,6 +340,7 @@ int lua_GetAllPlayers(lua_State* L) {
return 0;
return 1;
}
int lua_GetIdentifiers(lua_State* L) {
if (lua_isnumber(L, 1)) {
auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1)));
@@ -360,6 +390,7 @@ int lua_GetCars(lua_State* L) {
}
return 1;
}
int lua_dropPlayer(lua_State* L) {
int Args = lua_gettop(L);
if (lua_isnumber(L, 1)) {
@@ -380,12 +411,15 @@ int lua_dropPlayer(lua_State* L) {
SendError(Engine(), L, ("DropPlayer not enough arguments"));
return 0;
}
int lua_sendChat(lua_State* L) {
if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) {
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);
@@ -393,7 +427,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"));
@@ -404,6 +440,7 @@ int lua_sendChat(lua_State* L) {
SendError(Engine(), L, ("SendChatMessage invalid argument [1] expected number"));
return 0;
}
int lua_RemoveVehicle(lua_State* L) {
int Args = lua_gettop(L);
if (Args != 2) {
@@ -428,10 +465,12 @@ int lua_RemoveVehicle(lua_State* L) {
SendError(Engine(), L, ("RemoveVehicle invalid argument expected number"));
return 0;
}
int lua_HWID(lua_State* L) {
lua_pushinteger(L, -1);
return 1;
}
int lua_RemoteEvent(lua_State* L) {
int Args = lua_gettop(L);
if (Args != 3) {
@@ -465,14 +504,12 @@ int lua_RemoteEvent(lua_State* L) {
}
return 0;
}
int lua_ServerExit(lua_State* L) {
if (lua_gettop(L) > 0) {
if (lua_isnumber(L, 1)) {
_Exit(int(lua_tointeger(L, 1)));
}
}
_Exit(0);
int lua_ServerExit(lua_State*) {
Application::GracefullyShutdown();
return 0;
}
int lua_Set(lua_State* L) {
int Args = lua_gettop(L);
if (Args != 2) {
@@ -548,6 +585,7 @@ int lua_Set(lua_State* L) {
return 0;
}
extern "C" {
int lua_Print(lua_State* L) {
int Arg = lua_gettop(L);
@@ -590,6 +628,7 @@ int lua_Print(lua_State* L) {
int lua_TempFix(lua_State* L);
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;
@@ -619,17 +658,18 @@ void TLuaFile::Execute(const std::string& Command) {
lua_settop(mLuaState, 0);
}
}
void TLuaFile::Reload() {
if (CheckLua(mLuaState, luaL_dofile(mLuaState, mFileName.c_str()))) {
CallFunction(this, ("onInit"), nullptr);
}
}
std::string TLuaFile::GetOrigin() {
return fs::path(GetFileName()).filename().string();
}
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)) {
@@ -641,9 +681,13 @@ std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_pt
int R = lua_pcall(luaState, Size, 1, 0);
if (CheckLua(luaState, R)) {
if (lua_isnumber(luaState, -1)) {
return int(lua_tointeger(luaState, -1));
auto ret = int(lua_tointeger(luaState, -1));
ClearStack(luaState);
return ret;
} else if (lua_isstring(luaState, -1)) {
return std::string(lua_tostring(luaState, -1));
auto ret = std::string(lua_tostring(luaState, -1));
ClearStack(luaState);
return ret;
}
}
}
@@ -691,6 +735,7 @@ void TLuaFile::Load() {
void TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
mRegisteredEvents.insert(std::make_pair(Event, FunctionName));
}
void TLuaFile::UnRegisterEvent(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event) {
@@ -699,6 +744,7 @@ void TLuaFile::UnRegisterEvent(const std::string& Event) {
}
}
}
bool TLuaFile::IsRegistered(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event)
@@ -706,6 +752,7 @@ bool TLuaFile::IsRegistered(const std::string& Event) {
}
return false;
}
std::string TLuaFile::GetRegistered(const std::string& Event) const {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event)
@@ -713,12 +760,15 @@ std::string TLuaFile::GetRegistered(const std::string& Event) const {
}
return "";
}
std::string TLuaFile::GetFileName() const {
return mFileName;
}
std::string TLuaFile::GetPluginName() const {
return mPluginName;
}
lua_State* TLuaFile::GetState() {
return mLuaState;
}
@@ -751,6 +801,7 @@ void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
}
warn(a + (" | Incorrect Call of ") + msg);
}
int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -771,20 +822,26 @@ int lua_TempFix(lua_State* L) {
void TLuaArg::PushArgs(lua_State* State) {
for (std::any arg : args) {
if (!arg.has_value())
if (!arg.has_value()) {
error("arg didn't have a value, this is not expected, bad");
return;
std::string Type = arg.type().name();
if (Type.find("bool") != std::string::npos) {
}
const auto& Type = arg.type();
if (Type == typeid(bool)) {
lua_pushboolean(State, std::any_cast<bool>(arg));
}
if (Type.find("basic_string") != std::string::npos || Type.find("char") != std::string::npos) {
} else if (Type == typeid(std::string)) {
lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
}
if (Type.find("int") != std::string::npos) {
} else if (Type == typeid(const char*)) {
lua_pushstring(State, std::any_cast<const char*>(arg));
} else if (Type == typeid(int)) {
lua_pushinteger(State, std::any_cast<int>(arg));
}
if (Type.find("float") != std::string::npos) {
} else if (Type == typeid(float)) {
lua_pushnumber(State, std::any_cast<float>(arg));
} else if (Type == typeid(double)) {
lua_pushnumber(State, std::any_cast<double>(arg));
} else {
// if this happens, implement a sane behavior for that value
error("what in the hell is " + std::string(arg.type().name()));
}
}
}

View File

@@ -10,19 +10,24 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
, mPPSMonitor(PPSMonitor)
, mResourceManager(ResourceManager) {
Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) {
debug("shutting down TCPServer");
mShutdown = true;
mUDPThread.detach();
debug("shut down TCPServer");
}
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");
mShutdown = true;
mUDPThread.detach();
}
});
Application::RegisterShutdownHandler([&] {
if (mTCPThread.joinable()) {
mShutdown = true;
mTCPThread.detach();
debug("shut down TCPServer");
}
});
mTCPThread = std::thread(&TNetwork::TCPServerMain, this);
@@ -204,9 +209,7 @@ void TNetwork::TCPServerMain() {
#undef GetObject //Fixes Windows
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "Json.h"
namespace json = rapidjson;
void TNetwork::Identify(SOCKET TCPSock) {
@@ -273,8 +276,12 @@ void TNetwork::Authentication(SOCKET TCPSock) {
return;
}
auto RequestString = R"({"key":")" + Rc + "\"}";
auto Target = "/pkToUser";
int ResponseCode = -1;
if (!Rc.empty()) {
Rc = Http::POST(Application::GetBackendUrlForAuth(), "/pkToUser", {}, R"({"key":")" + Rc + "\"}", true);
Rc = Http::POST(Application::GetBackendUrlForAuth(), Target, {}, RequestString, true, &ResponseCode);
}
json::Document AuthResponse;
@@ -285,8 +292,23 @@ void TNetwork::Authentication(SOCKET TCPSock) {
}
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;
}
@@ -305,7 +327,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;
{
@@ -315,10 +336,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;
@@ -329,11 +347,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;
}
@@ -390,14 +407,14 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
return false;
}
Sent += Temp;
c.UpdatePingTime();
} while (Sent < Size);
c.UpdatePingTime();
return true;
}
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;
@@ -409,7 +426,6 @@ bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
#endif // WIN32
if (c.GetStatus() > -1)
c.SetStatus(-1);
info(("Closing socket in CheckBytes, BytesRcv < 0"));
CloseSocketProper(c.GetTCPSock());
return false;
}
@@ -425,22 +441,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) {
@@ -454,29 +461,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;
}
@@ -486,7 +479,12 @@ void TNetwork::ClientKick(TClient& c, const std::string& R) {
// TODO handle
}
c.SetStatus(-2);
CloseSocketProper(c.GetTCPSock());
if (c.GetTCPSock())
CloseSocketProper(c.GetTCPSock());
if (c.GetDownSock())
CloseSocketProper(c.GetDownSock());
}
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
while (!c.expired()) {
@@ -691,19 +689,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
@@ -720,14 +728,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);
})
};
@@ -757,7 +765,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
if (Diff > Split) {
f.seekg(Sent, std::ios_base::beg);
f.read(Data, Split);
if (!TCPSendRaw(TCPSock, Data, Split)) {
if (!TCPSendRaw(c, TCPSock, Data, Split)) {
if (c.GetStatus() > -1)
c.SetStatus(-1);
break;
@@ -766,7 +774,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
} else {
f.seekg(Sent, std::ios_base::beg);
f.read(Data, Diff);
if (!TCPSendRaw(TCPSock, Data, int32_t(Diff))) {
if (!TCPSendRaw(c, TCPSock, Data, int32_t(Diff))) {
if (c.GetStatus() > -1)
c.SetStatus(-1);
break;
@@ -778,16 +786,17 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.close();
}
bool TNetwork::TCPSendRaw(SOCKET C, char* Data, int32_t Size) {
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
intmax_t Sent = 0;
do {
intmax_t Temp = send(C, &Data[Sent], int(Size - Sent), 0);
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
if (Temp < 1) {
info("Socket Closed! " + std::to_string(C));
CloseSocketProper(C);
info("Socket Closed! " + std::to_string(socket));
CloseSocketProper(socket);
return false;
}
Sent += Temp;
C.UpdatePingTime();
} while (Sent < Size);
return true;
}
@@ -854,7 +863,6 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
return false;
}
res = Respond(*LockedClient, v.Data(), true, true);
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}

View File

@@ -7,10 +7,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
Application::SetPPS("-");
Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) {
debug("shutting down PPSMonitor");
mShutdown = true;
mThread.join();
debug("shut down PPSMonitor");
}
});
Start();
@@ -36,17 +34,15 @@ void TPPSMonitor::operator()() {
ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) {
c = ClientPtr.lock();
} else return true;
} else
return true;
}
if (c->GetCarCount() > 0) {
C++;
V += c->GetCarCount();
}
if (!c->IsSynced() || c->IsSyncing()) {
c->UpdatePingTime();
}
// kick on "no ping"
if (c->SecondsSinceLastPing() > 30 && c->IsSynced() && !c->IsSyncing()) {
if (c->SecondsSinceLastPing() > (20 * 60)) {
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
TimedOutClients.push_back(c);
}
@@ -54,7 +50,7 @@ void TPPSMonitor::operator()() {
return true;
});
for (auto& ClientToKick : TimedOutClients) {
Network().ClientKick(*ClientToKick, "Timeout (no ping for >30 seconds)");
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++;

138
src/TSentry.cpp Normal file
View File

@@ -0,0 +1,138 @@
#include "TSentry.h"
#include "Common.h"
#include <cstring>
#include <sentry.h>
#include <sstream>
TSentry::TSentry() {
if (std::strlen(S_DSN) == 0) {
mValid = false;
} else {
mValid = true;
sentry_options_t* options = sentry_options_new();
sentry_options_set_dsn(options, S_DSN);
sentry_options_set_debug(options, false); // needs to always be false
sentry_options_set_symbolize_stacktraces(options, true);
auto ReleaseString = "BeamMP-Server@" + Application::ServerVersion();
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) {
if (!Application::Settings.SendErrors) {
mValid = false;
if (Application::Settings.SendErrorsMessageEnabled) {
info("Opted out of error reporting (SendErrors), Sentry disabled.");
} else {
info("Sentry disabled");
}
} else {
if (Application::Settings.SendErrorsMessageEnabled) {
info("Sentry started! Reporting errors automatically. This sends data to the developers in case of errors and crashes. You can learn more, turn this message off or opt-out of this in the ServerConfig.toml.");
} else {
info("Sentry started");
}
}
} else {
if (Application::Settings.SendErrorsMessageEnabled) {
info("Sentry disabled in unofficial build. Automatic error reporting disabled.");
} 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,11 +7,9 @@
#include <any>
#include <sstream>
#undef GetObject //Fixes Windows
#undef GetObject // Fixes Windows
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "Json.h"
namespace json = rapidjson;
@@ -67,7 +65,7 @@ size_t TServer::ClientCount() const {
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
abort();
//abort();
}
if (Packet.substr(0, 4) == "ABG:") {
Packet = DeComp(Packet.substr(4));
@@ -92,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
}
@@ -116,28 +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':
trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
Network.SendToAll(LockedClient.get(), Packet, false, true);
return;
default:
return;
}
@@ -163,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;
@@ -175,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;
}
@@ -197,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));
@@ -224,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) {
@@ -238,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);
@@ -255,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) {
@@ -265,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);
@@ -276,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);
@@ -297,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,5 +1,9 @@
#include "TSentry.h"
#include "Common.h"
#include "CustomAssert.h"
#include "Http.h"
#include "SignalHandling.h"
#include "TConfig.h"
#include "THeartbeatThread.h"
#include "TLuaEngine.h"
@@ -7,51 +11,40 @@
#include "TPPSMonitor.h"
#include "TResourceManager.h"
#include "TServer.h"
#include <iostream>
#include <thread>
#ifdef __unix
#include <csignal>
void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
warn("ignoring SIGPIPE");
break;
case SIGTERM:
info("gracefully shutting down via SIGTERM");
Application::GracefullyShutdown();
break;
case SIGINT:
info("gracefully shutting down via SIGINT");
Application::GracefullyShutdown();
break;
default:
debug("unhandled signal: " + std::to_string(sig));
break;
}
}
#endif // __unix
int main(int argc, char** argv) {
#ifdef __unix
#if DEBUG
info("registering handlers for SIGINT, SIGTERM, SIGPIPE");
#endif // DEBUG
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
#ifndef DEBUG
signal(SIGINT, UnixSignalHandler);
#endif // DEBUG
#endif // __unix
// 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 {};
int main(int argc, char** argv) try {
setlocale(LC_ALL, "C");
SetupSignalHandlers();
bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
TServer Server(argc, argv);
[[maybe_unused]] TConfig Config("Server.cfg");
TConfig Config;
if (Config.Failed()) {
info("Closing in 10 seconds");
// loop to make it possible to ctrl+c instead
for (size_t i = 0; i < 20; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 1;
}
RegisterThread("Main");
trace("Running in debug mode on a debug build");
Sentry.SetupUser();
Sentry.PrintWelcome();
TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server);
@@ -59,9 +52,14 @@ int main(int argc, char** argv) {
TLuaEngine LuaEngine(Server, Network);
PPSMonitor.SetNetwork(Network);
Application::Console().InitializeLuaConsole(LuaEngine);
Application::CheckForUpdates();
// TODO: replace
while (!Shutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
info("Shutdown.");
} catch (const std::exception& e) {
error(e.what());
Sentry.LogException(e, _file_basename, _line);
}