Compare commits

..

64 Commits

Author SHA1 Message Date
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 fef069c9df.
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
27 changed files with 439 additions and 238 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
name: CMake Linux Build name: CMake Linux Build
on: [push, pull_request] on: [push]
env: env:
BUILD_TYPE: Release BUILD_TYPE: Release
+2 -2
View File
@@ -1,6 +1,6 @@
name: CMake Windows Build name: CMake Windows Build
on: [push, pull_request] on: [push]
env: env:
BUILD_TYPE: Release BUILD_TYPE: Release
@@ -20,7 +20,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp' vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f' vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
+1 -1
View File
@@ -86,7 +86,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp' vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f' vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
+8
View File
@@ -1,3 +1,5 @@
.idea/
*.toml
boost_* boost_*
Resources Resources
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
@@ -468,3 +470,9 @@ cmake-build-debug/include/commandline/Makefile
*.manifest *.manifest
*.rc *.rc
*.res *.res
BeamMP-Server
*.patch
callgrind.*
notes/*
compile_commands.json
nohup.out
+3
View File
@@ -10,3 +10,6 @@
[submodule "rapidjson"] [submodule "rapidjson"]
path = rapidjson path = rapidjson
url = https://github.com/Tencent/rapidjson url = https://github.com/Tencent/rapidjson
[submodule "include/tomlplusplus"]
path = include/tomlplusplus
url = https://github.com/marzer/tomlplusplus
Generated
-1
View File
@@ -1 +0,0 @@
Server
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>
Generated
-16
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>
+3 -3
View File
@@ -53,16 +53,16 @@ add_executable(BeamMP-Server
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline") target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
find_package(Lua REQUIRED) find_package(Lua REQUIRED)
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src") target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} "socket.io-client-cpp/src" "include/tomlplusplus")
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
if (UNIX) if (UNIX)
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline sioclient_tls) target_link_libraries(BeamMP-Server z pthread stdc++fs ${LUA_LIBRARIES} crypto ${OPENSSL_LIBRARIES} commandline sioclient_tls)
elseif (WIN32) elseif (WIN32)
include(FindLua) include(FindLua)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
find_package(RapidJSON CONFIG REQUIRED) find_package(RapidJSON CONFIG REQUIRED)
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls) target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls)
endif () endif ()
+1
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. 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.
+32 -12
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/). This is the server for the multiplayer mod **[BeamMP](https://beammp.com/)** for the game [BeamNG.drive](https://www.beamng.com/).
The server is the point throug which all clients communicate. You can write lua mods for the server, detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com). The server is the point throug which all clients communicate. You can write lua mods for the server, detailed instructions on the [BeamMP Wiki](https://wiki.beammp.com).
## Minimum Requirements
These values are guesstimated and are subject to change with each release.
* RAM: 50+ MiB usable (not counting OS overhead)
* CPU: Any Hz, preferably multicore
* OS: Windows, Linux (theoretically any POSIX)
* GPU: None
* HDD: 10 MiB + Mods/Plugins
* Bandwidth: 5-10 Mb/s upload
## Contributing
TLDR; [Issues](https://github.com/BeamMP/BeamMP-Server/issues) with the "help wanted" label or with nobody assigned, any [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer) cards in the "To-Do" column.
To contribute, look at the active [issues](https://github.com/BeamMP/BeamMP-Server/issues) and at the [trello](https://trello.com/b/Kw75j3zZ/beamngdrive-multiplayer). Any issues that have the "help wanted" label or don't have anyone assigned and any trello cards that aren't assigned or in the "In-Progress" section are good tasks to take on. You can either contribute by programming or by testing and adding more info and ideas.
Fork this repository, make a new branch for your feature, implement your feature or fix, and then create a pull-request here. Even incomplete features and fixes can be pull-requested.
If you need support with understanding the codebase, please write us in the discord. You'll need to be proficient in modern C++.
## About Building from Source ## About Building from Source
We only allow building unmodified (original) source code. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`. We only allow building unmodified (original) source code for public use. `master` is considered **unstable** and we will not provide technical support if such a build doesn't work, so always build from a tag. You can checkout a tag with `git checkout tags/TAGNAME`, where `TAGNAME` is the tag, for example `v1.20`.
## Supported Operating Systems ## Supported Operating Systems
@@ -42,28 +63,26 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
- `g++` - `g++`
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name). Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
- `liblua5.3` - `liblua5.3-dev`
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future. Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
You can also use any version of `libluajit`, but the same applies regarding the version. You can also use any version of `libluajit`, but the same applies regarding the version.
- `libz-dev` - `libz-dev`
- `rapidjson-dev` - `rapidjson-dev`
- `libboost1.70-dev` - `libopenssl-dev` or `libssl-dev`
If your distro doesn't have 1.7x version of libboost, you'll have to compile it from source or find another way to get it for your distro. **If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
- `libopenssl-dev`
### How to build ### How to build
On windows. use git-bash for these commands. On windows. use git-bash for these commands. On linux, these should work in your shell.
1. Make sure you have all [prerequisites](#prerequisites) installed 1. Make sure you have all [prerequisites](#prerequisites) installed
2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server` 2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server`. Now change into the cloned directory by running `cd BeamMP-Server`.
3. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. 3. 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).
4. `cd` into it with `cd BeamMP-Server` 4. Run `cmake .` (with `.`)
5. Run `cmake .` (with `.`) 5. Run `make`
6. Run `make` 6. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
*tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.* *tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*
@@ -71,3 +90,4 @@ On windows. use git-bash for these commands.
Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor). Copyright (c) 2019-present Anonymous275 (@Anonymous-275), Lion Kortlepel (@lionkor).
BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request. BeamMP-Server code is not in the public domain and is not free software. One must be granted explicit permission by the copyright holder(s) in order to modify or distribute any part of the source or binaries. Special permission to modify the source-code is implicitly granted only for the purpose of upstreaming those changes directly to github.com/BeamMP/BeamMP-Server via a GitHub pull-request.
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.
+1 -1
View File
@@ -83,7 +83,7 @@ private:
std::mutex mVehicleDataMutex; std::mutex mVehicleDataMutex;
TSetOfVehicleData mVehicleData; TSetOfVehicleData mVehicleData;
std::string mName = "Unknown Client"; std::string mName = "Unknown Client";
SOCKET mSocket[2] { SOCKET(-1) }; SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is sockaddr_in mUDPAddress {}; // is this initialization OK? yes it is
int mUnicycleID = -1; int mUnicycleID = -1;
std::string mRole; std::string mRole;
+14 -6
View File
@@ -18,17 +18,25 @@ public:
// types // types
struct TSettings { struct TSettings {
TSettings() noexcept TSettings() noexcept
: DebugModeEnabled(true) { } : ServerName("BeamMP Server")
, ServerDesc("BeamMP Default Description")
, Resource("Resources")
, MapName("/levels/gridmap_v2/info.json")
, MaxPlayers(10)
, Private(false)
, MaxCars(1)
, DebugModeEnabled(false)
, Port(30814) { }
std::string ServerName; std::string ServerName;
std::string ServerDesc; std::string ServerDesc;
std::string Resource; std::string Resource;
std::string MapName; std::string MapName;
std::string Key; std::string Key;
int MaxPlayers {}; int MaxPlayers;
bool Private {}; bool Private;
int MaxCars {}; int MaxCars;
bool DebugModeEnabled; bool DebugModeEnabled;
int Port {}; int Port;
std::string CustomIP; std::string CustomIP;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); } [[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
}; };
@@ -42,7 +50,7 @@ public:
// Causes all threads to finish up and exit gracefull gracefully // Causes all threads to finish up and exit gracefull gracefully
static void GracefullyShutdown(); static void GracefullyShutdown();
static TConsole& Console() { return *mConsole; } static TConsole& Console() { return *mConsole; }
static std::string ServerVersion() { return "2.0.2"; } static std::string ServerVersion() { return "2.2.0"; }
static std::string ClientVersion() { return "2.0"; } static std::string ClientVersion() { return "2.0"; }
static std::string PPS() { return mPPS; } static std::string PPS() { return mPPS; }
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; } static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
+9
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"
+1
View File
@@ -7,6 +7,7 @@
*/ */
#include <shared_mutex> #include <shared_mutex>
#include <mutex>
// Use ReadLock(m) and WriteLock(m) to lock it. // Use ReadLock(m) and WriteLock(m) to lock it.
using RWMutex = std::shared_mutex; using RWMutex = std::shared_mutex;
+12 -3
View File
@@ -2,11 +2,20 @@
#include "Common.h" #include "Common.h"
#include <atomic>
class TConfig { class TConfig {
public: public:
explicit TConfig(const std::string& ConfigFile); explicit TConfig();
[[nodiscard]] bool Failed() const { return mFailed; }
private: private:
static std::string RemoveComments(const std::string& Line); void CreateConfigFile(std::string_view name);
static void SetValues(const std::string& Line, int Index); void ParseFromFile(std::string_view name);
void PrintDebug();
void ParseOldFormat();
bool mFailed { false };
}; };
+1 -1
View File
@@ -44,6 +44,6 @@ private:
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked); void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked);
void Parse(TClient& c, const std::string& Packet); void Parse(TClient& c, const std::string& Packet);
void SendFile(TClient& c, const std::string& Name); void SendFile(TClient& c, const std::string& Name);
static bool TCPSendRaw(SOCKET C, char* Data, int32_t Size); static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size);
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name); static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
}; };
+200 -113
View File
@@ -1,126 +1,213 @@
#include "../include/TConfig.h" #include <toml.hpp> // header-only version of TOML++
#include "TConfig.h"
#include <fstream> #include <fstream>
#include <iostream>
#include <istream>
#include <sstream>
TConfig::TConfig(const std::string& ConfigFile) { static const char* ConfigFileName = static_cast<const char*>("ServerConfig.toml");
std::ifstream File(ConfigFile);
if (File.good()) { static constexpr std::string_view StrDebug = "Debug";
std::string line; static constexpr std::string_view StrPrivate = "Private";
int index = 1; static constexpr std::string_view StrPort = "Port";
while (getline(File, line)) { static constexpr std::string_view StrMaxCars = "MaxCars";
index++; static constexpr std::string_view StrMaxPlayers = "MaxPlayers";
static constexpr std::string_view StrMap = "Map";
static constexpr std::string_view StrName = "Name";
static constexpr std::string_view StrDescription = "Description";
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
static constexpr std::string_view StrAuthKey = "AuthKey";
TConfig::TConfig() {
if (!fs::exists(ConfigFileName) || !fs::is_regular_file(ConfigFileName)) {
info("No config file found! Generating one...");
CreateConfigFile(ConfigFileName);
}
if (!mFailed) {
if (fs::exists("Server.cfg")) {
warn("An old \"Server.cfg\" file still exists. Please note that this is no longer used. Instead, \"" + std::string(ConfigFileName) + "\" is used. You can safely delete the \"Server.cfg\".");
} }
if (index - 1 < 11) { ParseFromFile(ConfigFileName);
error(("Outdated/Incorrect config please remove it server will close in 5 secs")); }
std::this_thread::sleep_for(std::chrono::seconds(3)); }
_Exit(0);
} void TConfig::CreateConfigFile(std::string_view name) {
File.close(); // build from old config Server.cfg
File.open(("Server.cfg"));
info(("Config found updating values")); try {
index = 1; if (fs::exists("Server.cfg")) {
while (std::getline(File, line)) { // parse it (this is weird and bad and should be removed in some future version)
if (line.rfind('#', 0) != 0 && line.rfind(' ', 0) != 0) { //Checks if it starts as Comment ParseOldFormat();
std::string CleanLine = RemoveComments(line); //Cleans it from the Comments
SetValues(CleanLine, index); //sets the values
index++;
}
} }
} 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 },
} } },
} };
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;
} else { } else {
info(("Config not found generating default")); error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
std::ofstream FileStream; mFailed = true;
FileStream.open(ConfigFile);
// TODO REPLACE THIS SHIT OMG
FileStream << ("# This is the BeamMP Server Configuration File v0.60\n"
"Debug = false # true or false to enable debug console output\n"
"Private = true # Private?\n"
"Port = 30814 # Port to run the server on UDP and TCP\n"
"Cars = 1 # Max cars for every player\n"
"MaxPlayers = 10 # Maximum Amount of Clients\n"
"Map = \"/levels/gridmap/info.json\" # Default Map\n"
"Name = \"BeamMP New Server\" # Server Name\n"
"Desc = \"BeamMP Default Description\" # Server Description\n"
"use = \"Resources\" # Resource file name\n"
"AuthKey = \"\" # Auth Key");
FileStream.close();
error(("You are required to input the AuthKey"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
} }
debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
debug("Private : " + std::string(Application::Settings.Private ? "true" : "false"));
debug("Port : " + std::to_string(Application::Settings.Port));
debug("Max Cars : " + std::to_string(Application::Settings.MaxCars));
debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers));
debug("MapName : \"" + Application::Settings.MapName + "\"");
debug("ServerName : \"" + Application::Settings.ServerName + "\"");
debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\"");
debug("File : \"" + Application::Settings.Resource + "\"");
debug("Key length : " + std::to_string(Application::Settings.Key.length()) + "");
} }
std::string TConfig::RemoveComments(const std::string& Line) { void TConfig::ParseFromFile(std::string_view name) {
std::string Return; try {
for (char c : Line) { toml::table FullTable = toml::parse_file(name);
if (c == '#') toml::table GeneralTable = *FullTable["General"].as_table();
break; if (auto val = GeneralTable[StrDebug].value<bool>(); val.has_value()) {
Return += c; Application::Settings.DebugModeEnabled = val.value();
}
return Return;
}
void TConfig::SetValues(const std::string& Line, int Index) {
int state = 0;
std::string Data;
bool Switch = false;
if (Index > 5)
Switch = true;
for (char c : Line) {
if (Switch) {
if (c == '\"')
state++;
if (state > 0 && state < 2)
Data += c;
} else { } else {
if (c == ' ') throw std::runtime_error(std::string(StrDebug));
state++; }
if (state > 1) if (auto val = GeneralTable[StrPrivate].value<bool>(); val.has_value()) {
Data += c; 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));
}
} 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::stringstream Str(Content);
std::string::size_type sz; std::string Key, Ignore, Value;
bool FoundTrue = std::string(Data).find("true") != std::string::npos; //searches for "true" for (;;) {
switch (Index) { Str >> Key >> std::ws >> Ignore >> std::ws;
case 1: std::getline(Str, Value);
Application::Settings.DebugModeEnabled = FoundTrue; //checks and sets the Debug Value if (Str.eof()) {
break; break;
case 2: }
Application::Settings.Private = FoundTrue; //checks and sets the Private Value std::stringstream ValueStream(Value);
break; ValueStream >> std::ws; // strip leading whitespace if any
case 3: Value = ValueStream.str();
Application::Settings.Port = std::stoi(Data, &sz); //sets the Port if (Key == "Debug") {
break; Application::Settings.DebugModeEnabled = Value.find("true") != std::string::npos;
case 4: } else if (Key == "Private") {
Application::Settings.MaxCars = std::stoi(Data, &sz); //sets the Max Car amount Application::Settings.Private = Value.find("true") != std::string::npos;
break; } else if (Key == "Port") {
case 5: ValueStream >> Application::Settings.Port;
Application::Settings.MaxPlayers = std::stoi(Data, &sz); //sets the Max Amount of player } else if (Key == "Cars") {
break; ValueStream >> Application::Settings.MaxCars;
case 6: } else if (Key == "MaxPlayers") {
Application::Settings.MapName = Data; //Map ValueStream >> Application::Settings.MaxPlayers;
break; } else if (Key == "Map") {
case 7: Application::Settings.MapName = Value.substr(1, Value.size() - 3);
Application::Settings.ServerName = Data; //Name } else if (Key == "Name") {
break; Application::Settings.ServerName = Value.substr(1, Value.size() - 3);
case 8: } else if (Key == "Desc") {
Application::Settings.ServerDesc = Data; //desc Application::Settings.ServerDesc = Value.substr(1, Value.size() - 3);
break; } else if (Key == "use") {
case 9: Application::Settings.Resource = Value.substr(1, Value.size() - 3);
Application::Settings.Resource = Data; //File name } else if (Key == "AuthKey") {
break; Application::Settings.Key = Value.substr(1, Value.size() - 3);
case 10: } else {
Application::Settings.Key = Data; //File name warn("unknown key in old auth file (ignored): " + Key);
default: }
break; Str >> std::ws;
} }
} }
+1 -3
View File
@@ -26,9 +26,7 @@ void THeartbeatThread::operator()() {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue; continue;
} }
#ifdef DEBUG debug("heartbeat (after " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()) + "s)");
debug("heartbeat @ " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()));
#endif // DEBUG
Last = Body; Last = Body;
LastNormalUpdateTime = Now; LastNormalUpdateTime = Now;
+57 -24
View File
@@ -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"); SendError(lua->Engine(), lua->GetState(), R + " took too long to respond");
return 0; return 0;
} }
std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) { std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) {
Assert(lua); Assert(lua);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return Trigger(lua, R, arg); }); std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return Trigger(lua, R, arg); });
@@ -70,17 +71,16 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
return f1.get(); return f1.get();
return 0; return 0;
} }
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) { std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
std::any R; std::any R;
std::string Type;
int Ret = 0; int Ret = 0;
for (auto& Script : Engine().LuaFiles()) { for (auto& Script : Engine().LuaFiles()) {
if (Script->IsRegistered(Event)) { if (Script->IsRegistered(Event)) {
if (local) { if (local) {
if (Script->GetPluginName() == Caller->GetPluginName()) { if (Script->GetPluginName() == Caller->GetPluginName()) {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait); R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name(); if (R.type() == typeid(int)) {
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R)) if (std::any_cast<int>(R))
Ret++; Ret++;
} else if (Event == "onPlayerAuth") } else if (Event == "onPlayerAuth")
@@ -88,8 +88,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
} }
} else { } else {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait); R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name(); if (R.type() == typeid(int)) {
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R)) if (std::any_cast<int>(R))
Ret++; Ret++;
} else if (Event == "onPlayerAuth") } else if (Event == "onPlayerAuth")
@@ -99,6 +98,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
} }
return Ret; return Ret;
} }
bool ConsoleCheck(lua_State* L, int r) { bool ConsoleCheck(lua_State* L, int r) {
if (r != LUA_OK) { if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1); std::string msg = lua_tostring(L, -1);
@@ -107,6 +107,7 @@ bool ConsoleCheck(lua_State* L, int r) {
} }
return true; return true;
} }
bool CheckLua(lua_State* L, int r) { bool CheckLua(lua_State* L, int r) {
if (r != LUA_OK) { if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1); std::string msg = lua_tostring(L, -1);
@@ -134,6 +135,7 @@ int lua_RegisterEvent(lua_State* L) {
SendError(Engine(), L, "RegisterEvent invalid argument count expected 2 got " + std::to_string(Args)); SendError(Engine(), L, "RegisterEvent invalid argument count expected 2 got " + std::to_string(Args));
return 0; return 0;
} }
int lua_TriggerEventL(lua_State* L) { int lua_TriggerEventL(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
auto MaybeScript = Engine().GetScript(L); auto MaybeScript = Engine().GetScript(L);
@@ -197,6 +199,7 @@ int lua_StopThread(lua_State* L) {
MaybeScript.value().get().SetStopThread(true); MaybeScript.value().get().SetStopThread(true);
return 0; return 0;
} }
int lua_CreateThread(lua_State* L) { int lua_CreateThread(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
if (Args > 1) { if (Args > 1) {
@@ -220,6 +223,7 @@ int lua_CreateThread(lua_State* L) {
SendError(Engine(), L, ("CreateThread not enough arguments")); SendError(Engine(), L, ("CreateThread not enough arguments"));
return 0; return 0;
} }
int lua_Sleep(lua_State* L) { int lua_Sleep(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
int t = int(lua_tonumber(L, 1)); int t = int(lua_tonumber(L, 1));
@@ -230,6 +234,7 @@ int lua_Sleep(lua_State* L) {
} }
return 1; return 1;
} }
std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) { std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
std::optional<std::weak_ptr<TClient>> MaybeClient { std::nullopt }; std::optional<std::weak_ptr<TClient>> MaybeClient { std::nullopt };
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool { Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool {
@@ -245,7 +250,7 @@ std::optional<std::weak_ptr<TClient>> GetClient(TServer& Server, int ID) {
}); });
return MaybeClient; return MaybeClient;
} }
// CONTINUE
int lua_isConnected(lua_State* L) { int lua_isConnected(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1)); int ID = int(lua_tonumber(L, 1));
@@ -260,6 +265,7 @@ int lua_isConnected(lua_State* L) {
} }
return 1; return 1;
} }
int lua_GetPlayerName(lua_State* L) { int lua_GetPlayerName(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1)); int ID = int(lua_tonumber(L, 1));
@@ -274,10 +280,12 @@ int lua_GetPlayerName(lua_State* L) {
} }
return 1; return 1;
} }
int lua_GetPlayerCount(lua_State* L) { int lua_GetPlayerCount(lua_State* L) {
lua_pushinteger(L, Engine().Server().ClientCount()); lua_pushinteger(L, Engine().Server().ClientCount());
return 1; return 1;
} }
int lua_GetGuest(lua_State* L) { int lua_GetGuest(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1)); int ID = int(lua_tonumber(L, 1));
@@ -292,6 +300,7 @@ int lua_GetGuest(lua_State* L) {
} }
return 1; return 1;
} }
int lua_GetAllPlayers(lua_State* L) { int lua_GetAllPlayers(lua_State* L) {
lua_newtable(L); lua_newtable(L);
Engine().Server().ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool { Engine().Server().ForEachClient([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
@@ -311,6 +320,7 @@ int lua_GetAllPlayers(lua_State* L) {
return 0; return 0;
return 1; return 1;
} }
int lua_GetIdentifiers(lua_State* L) { int lua_GetIdentifiers(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1))); auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1)));
@@ -360,6 +370,7 @@ int lua_GetCars(lua_State* L) {
} }
return 1; return 1;
} }
int lua_dropPlayer(lua_State* L) { int lua_dropPlayer(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
@@ -380,6 +391,7 @@ int lua_dropPlayer(lua_State* L) {
SendError(Engine(), L, ("DropPlayer not enough arguments")); SendError(Engine(), L, ("DropPlayer not enough arguments"));
return 0; return 0;
} }
int lua_sendChat(lua_State* L) { int lua_sendChat(lua_State* L) {
if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) { if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) {
if (lua_isstring(L, 2)) { if (lua_isstring(L, 2)) {
@@ -404,6 +416,7 @@ int lua_sendChat(lua_State* L) {
SendError(Engine(), L, ("SendChatMessage invalid argument [1] expected number")); SendError(Engine(), L, ("SendChatMessage invalid argument [1] expected number"));
return 0; return 0;
} }
int lua_RemoveVehicle(lua_State* L) { int lua_RemoveVehicle(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
if (Args != 2) { if (Args != 2) {
@@ -428,10 +441,12 @@ int lua_RemoveVehicle(lua_State* L) {
SendError(Engine(), L, ("RemoveVehicle invalid argument expected number")); SendError(Engine(), L, ("RemoveVehicle invalid argument expected number"));
return 0; return 0;
} }
int lua_HWID(lua_State* L) { int lua_HWID(lua_State* L) {
lua_pushinteger(L, -1); lua_pushinteger(L, -1);
return 1; return 1;
} }
int lua_RemoteEvent(lua_State* L) { int lua_RemoteEvent(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
if (Args != 3) { if (Args != 3) {
@@ -465,14 +480,12 @@ int lua_RemoteEvent(lua_State* L) {
} }
return 0; return 0;
} }
int lua_ServerExit(lua_State* L) {
if (lua_gettop(L) > 0) { int lua_ServerExit(lua_State*) {
if (lua_isnumber(L, 1)) { Application::GracefullyShutdown();
_Exit(int(lua_tointeger(L, 1))); return 0;
}
}
_Exit(0);
} }
int lua_Set(lua_State* L) { int lua_Set(lua_State* L) {
int Args = lua_gettop(L); int Args = lua_gettop(L);
if (Args != 2) { if (Args != 2) {
@@ -548,6 +561,7 @@ int lua_Set(lua_State* L) {
return 0; return 0;
} }
extern "C" { extern "C" {
int lua_Print(lua_State* L) { int lua_Print(lua_State* L) {
int Arg = lua_gettop(L); int Arg = lua_gettop(L);
@@ -619,11 +633,13 @@ void TLuaFile::Execute(const std::string& Command) {
lua_settop(mLuaState, 0); lua_settop(mLuaState, 0);
} }
} }
void TLuaFile::Reload() { void TLuaFile::Reload() {
if (CheckLua(mLuaState, luaL_dofile(mLuaState, mFileName.c_str()))) { if (CheckLua(mLuaState, luaL_dofile(mLuaState, mFileName.c_str()))) {
CallFunction(this, ("onInit"), nullptr); CallFunction(this, ("onInit"), nullptr);
} }
} }
std::string TLuaFile::GetOrigin() { std::string TLuaFile::GetOrigin() {
return fs::path(GetFileName()).filename().string(); return fs::path(GetFileName()).filename().string();
} }
@@ -641,9 +657,13 @@ std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_pt
int R = lua_pcall(luaState, Size, 1, 0); int R = lua_pcall(luaState, Size, 1, 0);
if (CheckLua(luaState, R)) { if (CheckLua(luaState, R)) {
if (lua_isnumber(luaState, -1)) { if (lua_isnumber(luaState, -1)) {
return int(lua_tointeger(luaState, -1)); auto ret = int(lua_tointeger(luaState, -1));
ClearStack(luaState);
return ret;
} else if (lua_isstring(luaState, -1)) { } else if (lua_isstring(luaState, -1)) {
return std::string(lua_tostring(luaState, -1)); auto ret = std::string(lua_tostring(luaState, -1));
ClearStack(luaState);
return ret;
} }
} }
} }
@@ -691,6 +711,7 @@ void TLuaFile::Load() {
void TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) { void TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
mRegisteredEvents.insert(std::make_pair(Event, FunctionName)); mRegisteredEvents.insert(std::make_pair(Event, FunctionName));
} }
void TLuaFile::UnRegisterEvent(const std::string& Event) { void TLuaFile::UnRegisterEvent(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) { for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event) { if (a.first == Event) {
@@ -699,6 +720,7 @@ void TLuaFile::UnRegisterEvent(const std::string& Event) {
} }
} }
} }
bool TLuaFile::IsRegistered(const std::string& Event) { bool TLuaFile::IsRegistered(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) { for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event) if (a.first == Event)
@@ -706,6 +728,7 @@ bool TLuaFile::IsRegistered(const std::string& Event) {
} }
return false; return false;
} }
std::string TLuaFile::GetRegistered(const std::string& Event) const { std::string TLuaFile::GetRegistered(const std::string& Event) const {
for (const std::pair<std::string, std::string>& a : mRegisteredEvents) { for (const std::pair<std::string, std::string>& a : mRegisteredEvents) {
if (a.first == Event) if (a.first == Event)
@@ -713,12 +736,15 @@ std::string TLuaFile::GetRegistered(const std::string& Event) const {
} }
return ""; return "";
} }
std::string TLuaFile::GetFileName() const { std::string TLuaFile::GetFileName() const {
return mFileName; return mFileName;
} }
std::string TLuaFile::GetPluginName() const { std::string TLuaFile::GetPluginName() const {
return mPluginName; return mPluginName;
} }
lua_State* TLuaFile::GetState() { lua_State* TLuaFile::GetState() {
return mLuaState; return mLuaState;
} }
@@ -751,6 +777,7 @@ void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
} }
warn(a + (" | Incorrect Call of ") + msg); warn(a + (" | Incorrect Call of ") + msg);
} }
int lua_TempFix(lua_State* L) { int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1)); int ID = int(lua_tonumber(L, 1));
@@ -771,20 +798,26 @@ int lua_TempFix(lua_State* L) {
void TLuaArg::PushArgs(lua_State* State) { void TLuaArg::PushArgs(lua_State* State) {
for (std::any arg : args) { for (std::any arg : args) {
if (!arg.has_value()) if (!arg.has_value()) {
error("arg didn't have a value, this is not expected, bad");
return; return;
std::string Type = arg.type().name(); }
if (Type.find("bool") != std::string::npos) { const auto& Type = arg.type();
if (Type == typeid(bool)) {
lua_pushboolean(State, std::any_cast<bool>(arg)); lua_pushboolean(State, std::any_cast<bool>(arg));
} } else if (Type == typeid(std::string)) {
if (Type.find("basic_string") != std::string::npos || Type.find("char") != std::string::npos) {
lua_pushstring(State, std::any_cast<std::string>(arg).c_str()); lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
} } else if (Type == typeid(const char*)) {
if (Type.find("int") != std::string::npos) { lua_pushstring(State, std::any_cast<const char*>(arg));
} else if (Type == typeid(int)) {
lua_pushinteger(State, std::any_cast<int>(arg)); lua_pushinteger(State, std::any_cast<int>(arg));
} } else if (Type == typeid(float)) {
if (Type.find("float") != std::string::npos) {
lua_pushnumber(State, std::any_cast<float>(arg)); lua_pushnumber(State, std::any_cast<float>(arg));
} else if (Type == typeid(double)) {
lua_pushnumber(State, std::any_cast<double>(arg));
} else {
// if this happens, implement a sane behavior for that value
error("what in the hell is " + std::string(arg.type().name()));
} }
} }
} }
+46 -25
View File
@@ -9,6 +9,15 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
: mServer(Server) : mServer(Server)
, mPPSMonitor(PPSMonitor) , mPPSMonitor(PPSMonitor)
, mResourceManager(ResourceManager) { , mResourceManager(ResourceManager) {
Application::RegisterShutdownHandler([&] {
debug("Kicking all players due to shutdown");
Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool {
if (!client.expired()) {
ClientKick(*client.lock(), "Server shutdown");
}
return true;
});
});
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) { if (mUDPThread.joinable()) {
debug("shutting down TCPServer"); debug("shutting down TCPServer");
@@ -18,7 +27,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
} }
}); });
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mUDPThread.joinable()) { if (mTCPThread.joinable()) {
debug("shutting down TCPServer"); debug("shutting down TCPServer");
mShutdown = true; mShutdown = true;
mTCPThread.detach(); mTCPThread.detach();
@@ -204,9 +213,7 @@ void TNetwork::TCPServerMain() {
#undef GetObject //Fixes Windows #undef GetObject //Fixes Windows
#include "rapidjson/document.h" #include "Json.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace json = rapidjson; namespace json = rapidjson;
void TNetwork::Identify(SOCKET TCPSock) { void TNetwork::Identify(SOCKET TCPSock) {
@@ -329,11 +336,10 @@ void TNetwork::Authentication(SOCKET TCPSock) {
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } }); auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true); std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
std::string Type = Res.type().name(); if (Res.type() == typeid(int) && std::any_cast<int>(Res)) {
if (Type.find("int") != std::string::npos && std::any_cast<int>(Res)) {
ClientKick(*Client, "you are not allowed on the server!"); ClientKick(*Client, "you are not allowed on the server!");
return; return;
} else if (Type.find("string") != std::string::npos) { } else if (Res.type() == typeid(std::string)) {
ClientKick(*Client, std::any_cast<std::string>(Res)); ClientKick(*Client, std::any_cast<std::string>(Res));
return; return;
} }
@@ -390,8 +396,8 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
return false; return false;
} }
Sent += Temp; Sent += Temp;
c.UpdatePingTime();
} while (Sent < Size); } while (Sent < Size);
c.UpdatePingTime();
return true; return true;
} }
@@ -486,7 +492,12 @@ void TNetwork::ClientKick(TClient& c, const std::string& R) {
// TODO handle // TODO handle
} }
c.SetStatus(-2); c.SetStatus(-2);
CloseSocketProper(c.GetTCPSock());
if (c.GetTCPSock())
CloseSocketProper(c.GetTCPSock());
if (c.GetDownSock())
CloseSocketProper(c.GetDownSock());
} }
void TNetwork::Looper(const std::weak_ptr<TClient>& c) { void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
while (!c.expired()) { while (!c.expired()) {
@@ -691,19 +702,29 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
} }
} }
void TNetwork::SendFile(TClient& c, const std::string& Name) { void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/'))); info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
if (!std::filesystem::exists(Name)) { if (!fs::path(UnsafeName).has_filename()) {
if (!TCPSend(c, "CO")) { if (!TCPSend(c, "CO")) {
// TODO: handle // TODO: handle
} }
warn("File " + Name + " could not be accessed!"); warn("File " + UnsafeName + " is not a file!");
return; return;
} else { }
if (!TCPSend(c, "AG")) { auto FileName = fs::path(UnsafeName).filename().string();
FileName = Application::Settings.Resource + "/Client/" + FileName;
if (!std::filesystem::exists(FileName)) {
if (!TCPSend(c, "CO")) {
// TODO: handle // TODO: handle
} }
warn("File " + UnsafeName + " could not be accessed!");
return;
}
if (!TCPSend(c, "AG")) {
// TODO: handle
} }
///Wait for connections ///Wait for connections
@@ -720,14 +741,14 @@ void TNetwork::SendFile(TClient& c, const std::string& Name) {
return; return;
} }
size_t Size = size_t(std::filesystem::file_size(Name)), MSize = Size / 2; size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2;
std::thread SplitThreads[2] { std::thread SplitThreads[2] {
std::thread([&] { std::thread([&] {
SplitLoad(c, 0, MSize, false, Name); SplitLoad(c, 0, MSize, false, FileName);
}), }),
std::thread([&] { std::thread([&] {
SplitLoad(c, MSize, Size, true, Name); SplitLoad(c, MSize, Size, true, FileName);
}) })
}; };
@@ -757,7 +778,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
if (Diff > Split) { if (Diff > Split) {
f.seekg(Sent, std::ios_base::beg); f.seekg(Sent, std::ios_base::beg);
f.read(Data, Split); f.read(Data, Split);
if (!TCPSendRaw(TCPSock, Data, Split)) { if (!TCPSendRaw(c, TCPSock, Data, Split)) {
if (c.GetStatus() > -1) if (c.GetStatus() > -1)
c.SetStatus(-1); c.SetStatus(-1);
break; break;
@@ -766,7 +787,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
} else { } else {
f.seekg(Sent, std::ios_base::beg); f.seekg(Sent, std::ios_base::beg);
f.read(Data, Diff); f.read(Data, Diff);
if (!TCPSendRaw(TCPSock, Data, int32_t(Diff))) { if (!TCPSendRaw(c, TCPSock, Data, int32_t(Diff))) {
if (c.GetStatus() > -1) if (c.GetStatus() > -1)
c.SetStatus(-1); c.SetStatus(-1);
break; break;
@@ -778,16 +799,17 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.close(); f.close();
} }
bool TNetwork::TCPSendRaw(SOCKET C, char* Data, int32_t Size) { bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
intmax_t Sent = 0; intmax_t Sent = 0;
do { do {
intmax_t Temp = send(C, &Data[Sent], int(Size - Sent), 0); intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
if (Temp < 1) { if (Temp < 1) {
info("Socket Closed! " + std::to_string(C)); info("Socket Closed! " + std::to_string(socket));
CloseSocketProper(C); CloseSocketProper(socket);
return false; return false;
} }
Sent += Temp; Sent += Temp;
C.UpdatePingTime();
} while (Sent < Size); } while (Sent < Size);
return true; return true;
} }
@@ -854,7 +876,6 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
return false; return false;
} }
res = Respond(*LockedClient, v.Data(), true, true); res = Respond(*LockedClient, v.Data(), true, true);
std::this_thread::sleep_for(std::chrono::seconds(2));
} }
} }
+4 -6
View File
@@ -36,17 +36,15 @@ void TPPSMonitor::operator()() {
ReadLock Lock(mServer.GetClientMutex()); ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) { if (!ClientPtr.expired()) {
c = ClientPtr.lock(); c = ClientPtr.lock();
} else return true; } else
return true;
} }
if (c->GetCarCount() > 0) { if (c->GetCarCount() > 0) {
C++; C++;
V += c->GetCarCount(); V += c->GetCarCount();
} }
if (!c->IsSynced() || c->IsSyncing()) {
c->UpdatePingTime();
}
// kick on "no ping" // kick on "no ping"
if (c->SecondsSinceLastPing() > 30 && c->IsSynced() && !c->IsSyncing()) { if (c->SecondsSinceLastPing() > (5 * 60)) {
debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS()); debug("client " + std::string("(") + std::to_string(c->GetID()) + ")" + c->GetName() + " timing out: " + std::to_string(c->SecondsSinceLastPing()) + ", pps: " + Application::PPS());
TimedOutClients.push_back(c); TimedOutClients.push_back(c);
} }
@@ -54,7 +52,7 @@ void TPPSMonitor::operator()() {
return true; return true;
}); });
for (auto& ClientToKick : TimedOutClients) { for (auto& ClientToKick : TimedOutClients) {
Network().ClientKick(*ClientToKick, "Timeout (no ping for >30 seconds)"); Network().ClientKick(*ClientToKick, "Timeout (no ping for >5 min)");
} }
TimedOutClients.clear(); TimedOutClients.clear();
if (C == 0 || mInternalPPS == 0) { if (C == 0 || mInternalPPS == 0) {
+1 -1
View File
@@ -19,7 +19,7 @@ TResourceManager::TResourceManager() {
++i; ++i;
File = File.substr(i,pos-i); File = File.substr(i,pos-i);
} }
mTrimmedList += File + ';'; mTrimmedList += "/" + fs::path(File).filename().string() + ';';
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';'; mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
mMaxModSize += size_t(fs::file_size(entry.path())); mMaxModSize += size_t(fs::file_size(entry.path()));
mModsLoaded++; mModsLoaded++;
+30 -12
View File
@@ -9,9 +9,7 @@
#undef GetObject //Fixes Windows #undef GetObject //Fixes Windows
#include "rapidjson/document.h" #include "Json.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace json = rapidjson; namespace json = rapidjson;
@@ -138,6 +136,9 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
#endif #endif
HandleEvent(*LockedClient, Packet); HandleEvent(*LockedClient, Packet);
return; return;
case 'N':
debug("got 'N' packet (" + std::to_string(Packet.size()) + ")");
Network.SendToAll(LockedClient.get(), Packet, false, true);
default: default:
return; return;
} }
@@ -163,10 +164,10 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
a++; a++;
} }
} }
bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) { bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
rapidjson::Document Car; rapidjson::Document Car;
Car.Parse(CarJson.c_str(), CarJson.size()); Car.Parse(CarJson.c_str(), CarJson.size());
if(Car.HasParseError()){ if (Car.HasParseError()) {
error("Failed to parse vehicle data -> " + CarJson); error("Failed to parse vehicle data -> " + CarJson);
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") { } else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
return true; return true;
@@ -175,11 +176,11 @@ bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) {
} }
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) { 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; return true;
} }
if(IsUnicycle(c,CarJson)){ if (IsUnicycle(c, CarJson)) {
c.SetUnicycleID(ID); c.SetUnicycleID(ID);
return true; return true;
} }
@@ -238,12 +239,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }), std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
true); true);
if ((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) { && std::any_cast<int>(Res) == 0) {
Network.SendToAll(&c, Packet, false, true); Network.SendToAll(&c, Packet, false, true);
Apply(c, VID, Packet); Apply(c, VID, Packet);
} else { } else {
if(c.GetUnicycleID() == VID){ if (c.GetUnicycleID() == VID) {
c.SetUnicycleID(-1); c.SetUnicycleID(-1);
} }
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID); std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
@@ -265,7 +268,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
VID = stoi(vid); VID = stoi(vid);
} }
if (PID != -1 && VID != -1 && PID == c.GetID()) { if (PID != -1 && VID != -1 && PID == c.GetID()) {
if(c.GetUnicycleID() == VID){ if (c.GetUnicycleID() == VID) {
c.SetUnicycleID(-1); c.SetUnicycleID(-1);
} }
Network.SendToAll(nullptr, Packet, true, true); Network.SendToAll(nullptr, Packet, true, true);
@@ -311,10 +314,25 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
} }
void TServer::Apply(TClient& c, int VID, const std::string& pckt) { void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
std::string Packet = pckt.substr(pckt.find('{')), VD = c.GetCarData(VID); auto FoundPos = pckt.find('{');
if (FoundPos == std::string::npos) {
error("Malformed packet received, no '{' found");
return;
}
std::string Packet = pckt.substr(FoundPos);
std::string VD = c.GetCarData(VID);
if (VD.empty()) {
error("Tried to apply change to vehicle that does not exist");
return;
}
std::string Header = VD.substr(0, VD.find('{')); std::string Header = VD.substr(0, VD.find('{'));
VD = VD.substr(VD.find('{')); FoundPos = VD.find('{');
if (FoundPos == std::string::npos) {
error("Malformed packet received, no '{' found");
return;
}
VD = VD.substr(FoundPos);
rapidjson::Document Veh, Pack; rapidjson::Document Veh, Pack;
Veh.Parse(VD.c_str()); Veh.Parse(VD.c_str());
if (Veh.HasParseError()) { if (Veh.HasParseError()) {
+8 -1
View File
@@ -50,7 +50,14 @@ int main(int argc, char** argv) {
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; }); Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
TServer Server(argc, argv); TServer Server(argc, argv);
[[maybe_unused]] TConfig Config("Server.cfg"); TConfig Config;
if (Config.Failed()) {
info("Closing in 10 seconds");
std::this_thread::sleep_for(std::chrono::seconds(10));
return 1;
}
RegisterThread("Main"); RegisterThread("Main");
TResourceManager ResourceManager; TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server); TPPSMonitor PPSMonitor(Server);