Compare commits

..

46 Commits

Author SHA1 Message Date
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
29 changed files with 565 additions and 464 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
name: CMake Linux Build
on: [push, pull_request]
on: [push]
env:
BUILD_TYPE: Release
+1 -1
View File
@@ -1,6 +1,6 @@
name: CMake Windows Build
on: [push, pull_request]
on: [push]
env:
BUILD_TYPE: Release
+6 -10
View File
@@ -1,3 +1,5 @@
.idea/
*.toml
boost_*
Resources
## Ignore Visual Studio temporary files, build results, and
@@ -468,15 +470,9 @@ cmake-build-debug/include/commandline/Makefile
*.manifest
*.rc
*.res
changelog-2.0.txt
BeamMP-Server
*.patch
callgrind.*
notes/*
compile_commands.json
err.html
file.c
log.txt
nohup.out
notes/
patch.patch
new-backend.txt
sanitizer.txt
speedcc.fit
socket.txt
+3
View File
@@ -10,3 +10,6 @@
[submodule "rapidjson"]
path = 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")
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)
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)
include(FindLua)
find_package(ZLIB REQUIRED)
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)
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.
Commercial usage is prohibited, unless explicit permission has been granted prior to usage.
+25 -4
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: 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
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
@@ -48,11 +69,10 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
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`
**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
### How to build
On windows. use git-bash for these commands.
@@ -71,3 +91,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.
+1 -1
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;
+14 -6
View File
@@ -18,17 +18,25 @@ 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(false)
, MaxCars(1)
, DebugModeEnabled(false)
, Port(30814) { }
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;
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
};
@@ -42,7 +50,7 @@ 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.1.2"; }
static std::string ClientVersion() { return "2.0"; }
static std::string PPS() { return mPPS; }
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 <mutex>
// Use ReadLock(m) and WriteLock(m) to lock it.
using RWMutex = std::shared_mutex;
+12 -3
View File
@@ -2,11 +2,20 @@
#include "Common.h"
#include <atomic>
class TConfig {
public:
explicit TConfig(const std::string& ConfigFile);
explicit TConfig();
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 };
};
+5 -17
View File
@@ -9,42 +9,30 @@
#include <optional>
#include <set>
#include <filesystem>
namespace fs = std::filesystem;
struct TLuaArgs final {
std::vector<std::any> Args;
};
class TLuaEngine : public IThreaded {
public:
explicit TLuaEngine(TServer& Server, TNetwork& Network);
using TSetOfLuaFile = std::vector<std::shared_ptr<TLuaFile>>;
using TSetOfLuaFile = std::set<std::unique_ptr<TLuaFile>>;
void operator()() override;
std::any TriggerLuaEvent(const std::string& Event, bool local, std::weak_ptr<TLuaFile> Caller, std::shared_ptr<TLuaArgs> arg, bool Wait);
[[nodiscard]] const TSetOfLuaFile& LuaFiles() const { return mLuaFiles; }
[[nodiscard]] TServer& Server() { return mServer; }
[[nodiscard]] const TServer& Server() const { return mServer; }
[[nodiscard]] TNetwork& Network() { return mNetwork; }
[[nodiscard]] const TNetwork& Network() const { return mNetwork; }
void UnregisterScript(std::shared_ptr<TLuaFile> Script);
std::shared_ptr<TLuaFile> GetLuaFileOfState(lua_State* L);
std::shared_ptr<TLuaFile> InsertNewLuaFile(const fs::path& FileName, const std::string& PluginName);
void SendError(lua_State* L, const std::string& msg);
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
private:
void FolderList(const std::string& Path, bool HotSwap);
void RegisterFiles(const fs::path& Path, bool HotSwap);
bool IsNewFile(const std::string& Path);
void RegisterFiles(const std::string& Path, bool HotSwap);
bool NewFile(const std::string& Path);
TNetwork& mNetwork;
TServer& mServer;
std::string mPath;
bool mShutdown { false };
mutable std::mutex mLuaFilesMutex;
TSetOfLuaFile mLuaFiles;
};
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
class TLuaEvent final {
};
+11 -10
View File
@@ -11,14 +11,15 @@
namespace fs = std::filesystem;
struct TLuaArg {
std::vector<std::any> args;
void PushArgs(lua_State* State);
};
class TLuaEngine;
class TLuaArgs;
class TLuaFile {
public:
explicit TLuaFile(TLuaEngine& Engine, bool Console = false);
~TLuaFile();
void RegisterEvent(const std::string& Event, const std::string& FunctionName);
void UnRegisterEvent(const std::string& Event);
void SetLastWrite(fs::file_time_type time);
@@ -29,8 +30,11 @@ public:
fs::file_time_type GetLastWrite();
lua_State* GetState();
std::string GetOrigin();
std::mutex Lock;
void Reload();
void Init(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote);
explicit TLuaFile(TLuaEngine& Engine, bool Console = false);
~TLuaFile();
void SetStopThread(bool StopThread) { mStopThread = StopThread; }
TLuaEngine& Engine() { return mEngine; }
[[nodiscard]] std::string GetPluginName() const;
@@ -39,14 +43,8 @@ public:
[[nodiscard]] bool GetStopThread() const { return mStopThread; }
[[nodiscard]] const TLuaEngine& Engine() const { return mEngine; }
[[nodiscard]] std::string GetRegistered(const std::string& Event) const;
void PushArgs(const TLuaArgs& args);
bool operator==(const TLuaFile& Other) const;
void Load();
private:
std::mutex Lock;
TLuaEngine& mEngine;
std::set<std::pair<std::string, std::string>> mRegisteredEvents;
lua_State* mLuaState { nullptr };
@@ -55,6 +53,9 @@ private:
std::string mFileName {};
bool mStopThread = false;
bool mConsole = false;
void Load();
};
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
#endif // TLUAFILE_H
+1 -1
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);
};
+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 <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";
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 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 },
} } },
} };
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 {
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));
}
} 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;
}
}
+30 -203
View File
@@ -1,16 +1,18 @@
#include "TLuaEngine.h"
#include "Client.h"
#include "CustomAssert.h"
#include "TLuaFile.h"
#include <lua.hpp>
#include <filesystem>
#include <sys/stat.h>
extern TLuaEngine* TheLuaEngine;
namespace fs = std::filesystem;
// necessary as lua relies on global state
TLuaEngine* TheEngine;
TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
: mNetwork(Network)
, mServer(Server) {
TheLuaEngine = this;
TheEngine = this;
if (!fs::exists(Application::Settings.Resource)) {
fs::create_directory(Application::Settings.Resource);
}
@@ -29,27 +31,16 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network)
Start();
}
void TLuaEngine::UnregisterScript(std::shared_ptr<TLuaFile> Script) {
std::unique_lock Lock(mLuaFilesMutex);
auto Iter = std::find_if(mLuaFiles.begin(), mLuaFiles.end(), [&](const auto& ScriptPtr) {
return *Script == *ScriptPtr;
});
if (Iter != mLuaFiles.end()) {
// found!
mLuaFiles.erase(Iter);
}
}
void TLuaEngine::operator()() {
RegisterThread("LuaEngine");
info("Lua system online");
while (!mShutdown) {
if (!mLuaFiles.empty()) {
for (auto& Script : mLuaFiles) {
const auto& filename = Script->GetFileName();
if (!fs::is_regular_file(filename)) {
struct stat Info { };
if (stat(Script->GetFileName().c_str(), &Info) != 0) {
Script->SetStopThread(true);
UnregisterScript(Script);
mLuaFiles.erase(Script);
info(("[HOTSWAP] Removed removed script due to delete"));
break;
}
@@ -66,211 +57,47 @@ void TLuaEngine::operator()() {
}
}
std::any TLuaEngine::TriggerLuaEvent(const std::string& Event, bool local, std::weak_ptr<TLuaFile> Caller, std::shared_ptr<TLuaArgs> arg, bool Wait) {
std::any R;
std::string Type;
int Ret = 0;
// TODO: lock
for (auto& Script : mLuaFiles) {
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 (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
} else {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
}
}
return Ret;
}
std::shared_ptr<TLuaFile> TLuaEngine::GetLuaFileOfState(lua_State* L) {
std::unique_lock Lock(mLuaFilesMutex);
std::optional<std::reference_wrapper<TLuaFile>> TLuaEngine::GetScript(lua_State* L) {
for (auto& Script : mLuaFiles) {
if (Script->GetState() == L)
return Script;
return *Script;
}
return nullptr;
return std::nullopt;
}
void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) {
for (const auto& entry : fs::directory_iterator(Path)) {
if (fs::is_directory(entry)) {
RegisterFiles(entry.path(), HotSwap);
auto pos = entry.path().filename().string().find('.');
if (pos == std::string::npos) {
RegisterFiles(entry.path().string(), HotSwap);
}
}
}
void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) {
std::string Name = Path.filename();
if (!HotSwap) {
void TLuaEngine::RegisterFiles(const std::string& Path, bool HotSwap) {
std::string Name = Path.substr(Path.find_last_of('\\') + 1);
if (!HotSwap)
info(("Loading plugin : ") + Name);
}
for (const auto& entry : fs::directory_iterator(Path)) {
if (entry.is_regular_file() && entry.path().extension() == ".lua") {
if (!HotSwap || IsNewFile(entry.path().string())) {
InsertNewLuaFile(entry.path(), Path.filename());
if (HotSwap) {
info(("[HOTSWAP] Added : ") + entry.path().filename().string());
}
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('\\')));
}
}
}
}
bool TLuaEngine::IsNewFile(const std::string& Path) {
std::unique_lock Lock(mLuaFilesMutex);
bool TLuaEngine::NewFile(const std::string& Path) {
for (auto& Script : mLuaFiles) {
// TODO: use std::any_of
if (Path == Script->GetFileName())
return false;
}
return true;
}
void TLuaEngine::SendError(lua_State* L, const std::string& msg) {
Assert(L);
auto MaybeS = GetLuaFileOfState(L);
std::string a;
if (!MaybeS) {
a = ("_Console");
} else {
// FIXME: GetFileName already returns the filename. This is redundant.
a = fs::path(MaybeS->GetFileName()).filename().string();
}
warn(a + (" | Incorrect call of ") + msg);
}
// //////////////////////////////////////////////
// ////////// LUA *ONLY* BELOW //////////////////
// //////////////////////////////////////////////
std::shared_ptr<TClient> GetClient(TServer& Server, int ID) {
std::shared_ptr<TClient> MaybeClient = nullptr;
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) -> bool {
ReadLock Lock(Server.GetClientMutex());
if (!CPtr.expired()) {
auto C = CPtr.lock();
if (C->GetID() == ID) {
MaybeClient = C;
return false;
}
}
return true;
});
return MaybeClient;
}
int ServerLua_GetIdentifiers(lua_State* L) {
Assert(TheLuaEngine);
Assert(L);
if (lua_isnumber(L, 1)) {
// do not store this
// FIXME: should this not be lua_tointeger?!
auto MaybeClient = GetClient(TheLuaEngine->Server(), int(lua_tonumber(L, 1)));
if (MaybeClient) {
auto IDs = MaybeClient->GetIdentifiers();
if (IDs.empty())
return 0;
// FIXME: What does this entire thing do? Add comments.
lua_newtable(L);
for (const std::string& ID : IDs) {
lua_pushstring(L, ID.substr(0, ID.find(':')).c_str());
lua_pushstring(L, ID.c_str());
lua_settable(L, -3);
}
} else
return 0;
} else {
TheLuaEngine->SendError(L, "lua_GetIdentifiers wrong arguments");
return 0;
}
return 1;
}
static std::shared_ptr<TLuaArgs> CollectArgs(lua_State* L) {
int ArgCount = lua_gettop(L);
if (ArgCount == 0) {
return nullptr;
}
std::shared_ptr<TLuaArgs> Temp = std::make_shared<TLuaArgs>();
for (int C = 2; C <= ArgCount; C++) {
if (lua_isstring(L, C)) {
Temp->Args.emplace_back(std::string(lua_tostring(L, C)));
} else if (lua_isinteger(L, C)) {
Temp->Args.emplace_back(int(lua_tointeger(L, C)));
} else if (lua_isboolean(L, C)) {
Temp->Args.emplace_back(bool(lua_toboolean(L, C)));
} else if (lua_isnumber(L, C)) {
Temp->Args.emplace_back(float(lua_tonumber(L, C)));
}
}
return Temp;
}
int ServerLua_TriggerEventG(lua_State* L) {
auto MaybeScript = TheLuaEngine->GetLuaFileOfState(L);
Assert(!!MaybeScript);
if (lua_gettop(L) <= 0) {
TheLuaEngine->SendError(L, ("TriggerGlobalEvent not enough arguments"));
return 0;
} else if (!lua_isstring(L, 1)) {
TheLuaEngine->SendError(L, "TriggerGlobalEvent wrong argument [1] need string");
}
std::string Name = lua_tostring(L, 1);
auto Args = CollectArgs(L);
TheLuaEngine->TriggerLuaEvent(Name, false, MaybeScript, Args, false);
return 0;
}
// LEAVE THIS AT THE **VERY** BOTTOM OF THE FILE
std::shared_ptr<TLuaFile> TLuaEngine::InsertNewLuaFile(const fs::path& FileName, const std::string& PluginName) {
std::shared_ptr<TLuaFile> Script(new TLuaFile(*this));
mLuaFiles.push_back(Script);
Script->SetPluginName(PluginName);
Script->SetFileName(FileName);
Script->SetLastWrite(fs::last_write_time(FileName));
lua_State* State = Script->GetState();
luaL_openlibs(State);
lua_register(State, "GetPlayerIdentifiers", ServerLua_GetIdentifiers);
lua_register(State, "TriggerGlobalEvent", ServerLua_TriggerEventG);
lua_register(State, "TriggerLocalEvent", lua_TriggerEventL);
lua_register(State, "TriggerClientEvent", lua_RemoteEvent);
lua_register(State, "GetPlayerCount", lua_GetPlayerCount);
lua_register(State, "isPlayerConnected", lua_isConnected);
lua_register(State, "RegisterEvent", lua_RegisterEvent);
lua_register(State, "GetPlayerName", lua_GetPlayerName);
lua_register(State, "RemoveVehicle", lua_RemoveVehicle);
lua_register(State, "GetPlayerDiscordID", lua_TempFix);
lua_register(State, "CreateThread", lua_CreateThread);
lua_register(State, "GetPlayerVehicles", lua_GetCars);
lua_register(State, "SendChatMessage", lua_sendChat);
lua_register(State, "GetPlayers", lua_GetAllPlayers);
lua_register(State, "GetPlayerGuest", lua_GetGuest);
lua_register(State, "StopThread", lua_StopThread);
lua_register(State, "DropPlayer", lua_dropPlayer);
lua_register(State, "GetPlayerHWID", lua_HWID);
lua_register(State, "exit", lua_ServerExit);
lua_register(State, "Sleep", lua_Sleep);
lua_register(State, "print", lua_Print);
lua_register(State, "Set", lua_Set);
if (!Script->IsConsole()) {
Reload();
}
debug("inserted new lua file: " + PluginName + " - " + FileName.string());
return Script;
}
-2
View File
@@ -1,2 +0,0 @@
#include "TLuaEvent.h"
+208 -36
View File
@@ -12,13 +12,41 @@
// TODO: REWRITE
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArgs> Arg);
std::any TriggerLuaEvent(TLuaEngine& Engine, const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArgs> arg, bool Wait);
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
std::any TriggerLuaEvent(TLuaEngine& Engine, const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArgs> arg) {
extern TLuaEngine* TheEngine;
static TLuaEngine& Engine() {
Assert(TheEngine);
return *TheEngine;
}
std::shared_ptr<TLuaArg> CreateArg(lua_State* L, int T, int S) {
if (S > T)
return nullptr;
std::shared_ptr<TLuaArg> temp(new TLuaArg);
for (int C = S; C <= T; C++) {
if (lua_isstring(L, C)) {
temp->args.emplace_back(std::string(lua_tostring(L, C)));
} else if (lua_isinteger(L, C)) {
temp->args.emplace_back(int(lua_tointeger(L, C)));
} else if (lua_isboolean(L, C)) {
temp->args.emplace_back(bool(lua_toboolean(L, C)));
} else if (lua_isnumber(L, C)) {
temp->args.emplace_back(float(lua_tonumber(L, C)));
}
}
return temp;
}
void ClearStack(lua_State* L) {
lua_settop(L, 0);
}
std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
RegisterThread(lua->GetFileName());
std::lock_guard<std::mutex> lockGuard(lua->Lock);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArgs> arg) { return CallFunction(lua, R, arg); });
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> arg) { return CallFunction(lua, R, arg); });
std::future<std::any> f1 = task.get_future();
std::thread t(std::move(task), arg);
t.detach();
@@ -28,9 +56,10 @@ std::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArgs>
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<TLuaArgs> arg, bool Wait) {
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<TLuaArgs> 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); });
std::future<std::any> f1 = task.get_future();
std::thread t(std::move(task), arg);
t.detach();
@@ -42,6 +71,37 @@ 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 (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
} else {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
}
}
return Ret;
}
bool ConsoleCheck(lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
@@ -50,6 +110,7 @@ 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);
@@ -77,6 +138,7 @@ 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);
@@ -140,6 +202,7 @@ int lua_StopThread(lua_State* L) {
MaybeScript.value().get().SetStopThread(true);
return 0;
}
int lua_CreateThread(lua_State* L) {
int Args = lua_gettop(L);
if (Args > 1) {
@@ -163,6 +226,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));
@@ -174,7 +238,22 @@ int lua_Sleep(lua_State* L) {
return 1;
}
// CONTINUE
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 {
ReadLock Lock(Server.GetClientMutex());
if (!CPtr.expired()) {
auto C = CPtr.lock();
if (C->GetID() == ID) {
MaybeClient = CPtr;
return false;
}
}
return true;
});
return MaybeClient;
}
int lua_isConnected(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -189,6 +268,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));
@@ -203,10 +283,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));
@@ -221,6 +303,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 {
@@ -241,6 +324,28 @@ int lua_GetAllPlayers(lua_State* L) {
return 1;
}
int lua_GetIdentifiers(lua_State* L) {
if (lua_isnumber(L, 1)) {
auto MaybeClient = GetClient(Engine().Server(), int(lua_tonumber(L, 1)));
if (MaybeClient && !MaybeClient.value().expired()) {
auto IDs = MaybeClient.value().lock()->GetIdentifiers();
if (IDs.empty())
return 0;
lua_newtable(L);
for (const std::string& ID : IDs) {
lua_pushstring(L, ID.substr(0, ID.find(':')).c_str());
lua_pushstring(L, ID.c_str());
lua_settable(L, -3);
}
} else
return 0;
} else {
SendError(Engine(), L, "lua_GetIdentifiers wrong arguments");
return 0;
}
return 1;
}
int lua_GetCars(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -268,6 +373,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)) {
@@ -288,6 +394,7 @@ 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)) {
@@ -312,6 +419,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) {
@@ -336,10 +444,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) {
@@ -373,14 +483,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) {
@@ -456,6 +564,7 @@ int lua_Set(lua_State* L) {
return 0;
}
extern "C" {
int lua_Print(lua_State* L) {
int Arg = lua_gettop(L);
@@ -498,7 +607,19 @@ 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) {
// set global engine for lua_* functions
if (!TheEngine) {
TheEngine = &mEngine;
}
Assert(mLuaState);
if (!PluginName.empty()) {
SetPluginName(PluginName);
}
if (!FileName.empty()) {
SetFileName(FileName);
}
SetLastWrite(LastWrote);
Load();
}
TLuaFile::TLuaFile(TLuaEngine& Engine, bool Console)
@@ -515,16 +636,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<TLuaArgs> Arg) {
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());
@@ -537,9 +660,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;
}
}
}
@@ -556,11 +683,38 @@ void TLuaFile::SetFileName(const std::string& Name) {
}
void TLuaFile::Load() {
Assert(mLuaState);
luaL_openlibs(mLuaState);
lua_register(mLuaState, "GetPlayerIdentifiers", lua_GetIdentifiers);
lua_register(mLuaState, "TriggerGlobalEvent", lua_TriggerEventG);
lua_register(mLuaState, "TriggerLocalEvent", lua_TriggerEventL);
lua_register(mLuaState, "TriggerClientEvent", lua_RemoteEvent);
lua_register(mLuaState, "GetPlayerCount", lua_GetPlayerCount);
lua_register(mLuaState, "isPlayerConnected", lua_isConnected);
lua_register(mLuaState, "RegisterEvent", lua_RegisterEvent);
lua_register(mLuaState, "GetPlayerName", lua_GetPlayerName);
lua_register(mLuaState, "RemoveVehicle", lua_RemoveVehicle);
lua_register(mLuaState, "GetPlayerDiscordID", lua_TempFix);
lua_register(mLuaState, "CreateThread", lua_CreateThread);
lua_register(mLuaState, "GetPlayerVehicles", lua_GetCars);
lua_register(mLuaState, "SendChatMessage", lua_sendChat);
lua_register(mLuaState, "GetPlayers", lua_GetAllPlayers);
lua_register(mLuaState, "GetPlayerGuest", lua_GetGuest);
lua_register(mLuaState, "StopThread", lua_StopThread);
lua_register(mLuaState, "DropPlayer", lua_dropPlayer);
lua_register(mLuaState, "GetPlayerHWID", lua_HWID);
lua_register(mLuaState, "exit", lua_ServerExit);
lua_register(mLuaState, "Sleep", lua_Sleep);
lua_register(mLuaState, "print", lua_Print);
lua_register(mLuaState, "Set", lua_Set);
if (!mConsole)
Reload();
}
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) {
@@ -569,6 +723,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)
@@ -576,6 +731,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)
@@ -584,17 +740,14 @@ std::string TLuaFile::GetRegistered(const std::string& Event) const {
return "";
}
bool TLuaFile::operator==(const TLuaFile& Other) const {
return mPluginName == Other.mPluginName
&& mFileName == Other.mFileName;
}
std::string TLuaFile::GetFileName() const {
return mFileName;
}
std::string TLuaFile::GetPluginName() const {
return mPluginName;
}
lua_State* TLuaFile::GetState() {
return mLuaState;
}
@@ -615,6 +768,19 @@ TLuaFile::~TLuaFile() {
lua_close(mLuaState);
}
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
Assert(L);
auto MaybeS = Engine.GetScript(L);
std::string a;
if (!MaybeS.has_value()) {
a = ("_Console");
} else {
TLuaFile& S = MaybeS.value();
a = fs::path(S.GetFileName()).filename().string();
}
warn(a + (" | Incorrect Call of ") + msg);
}
int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
@@ -633,22 +799,28 @@ int lua_TempFix(lua_State* L) {
return 1;
}
void TLuaFile::PushArgs(const TLuaArgs& args) {
for (const std::any& arg : args.Args) {
if (!arg.has_value())
void TLuaArg::PushArgs(lua_State* State) {
for (std::any arg : args) {
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) {
lua_pushboolean(mLuaState, std::any_cast<bool>(arg));
}
if (Type.find("basic_string") != std::string::npos || Type.find("char") != std::string::npos) {
lua_pushstring(mLuaState, std::any_cast<std::string>(arg).c_str());
}
if (Type.find("int") != std::string::npos) {
lua_pushinteger(mLuaState, std::any_cast<int>(arg));
}
if (Type.find("float") != std::string::npos) {
lua_pushnumber(mLuaState, std::any_cast<float>(arg));
const auto& Type = arg.type();
if (Type == typeid(bool)) {
lua_pushboolean(State, std::any_cast<bool>(arg));
} else if (Type == typeid(std::string)) {
lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
} 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));
} 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()));
}
}
}
+15 -12
View File
@@ -204,9 +204,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) {
@@ -390,8 +388,8 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
return false;
}
Sent += Temp;
c.UpdatePingTime();
} while (Sent < Size);
c.UpdatePingTime();
return true;
}
@@ -486,7 +484,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()) {
@@ -757,7 +760,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 +769,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 +781,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 +858,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));
}
}
+4 -6
View File
@@ -36,17 +36,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() > (5 * 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 +52,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 >5 min)");
}
TimedOutClients.clear();
if (C == 0 || mInternalPPS == 0) {
+4 -3
View File
@@ -9,9 +9,7 @@
#undef GetObject //Fixes Windows
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "Json.h"
namespace json = rapidjson;
@@ -138,6 +136,9 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
#endif
HandleEvent(*LockedClient, Packet);
return;
case 'N':
debug("got 'N' packet (" + std::to_string(Packet.size()) + ")");
Network.SendToAll(LockedClient.get(), Packet, false, true);
default:
return;
}
+8 -3
View File
@@ -9,8 +9,6 @@
#include "TServer.h"
#include <thread>
TLuaEngine* TheLuaEngine = nullptr;
#ifdef __unix
#include <csignal>
@@ -52,7 +50,14 @@ int main(int argc, char** argv) {
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");
std::this_thread::sleep_for(std::chrono::seconds(10));
return 1;
}
RegisterThread("Main");
TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server);