mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-14 10:44:30 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd12ee672d |
@@ -20,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
|
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
@@ -44,15 +44,4 @@ jobs:
|
|||||||
name: BeamMP-Server.exe
|
name: BeamMP-Server.exe
|
||||||
path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe
|
path: ${{github.workspace}}/build-windows/Release/BeamMP-Server.exe
|
||||||
|
|
||||||
- name: Build debug
|
|
||||||
working-directory: ${{github.workspace}}/build-windows
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cmake --build . --config Debug
|
|
||||||
|
|
||||||
- name: Archive debug artifacts
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: BeamMP-Server-debug.exe
|
|
||||||
path: ${{github.workspace}}/build-windows/Debug/BeamMP-Server.exe
|
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
|
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: 'a106de33bbee694e3be6243718aa2a549a692832'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
|
|||||||
@@ -22,6 +22,3 @@
|
|||||||
[submodule "deps/cpp-httplib"]
|
[submodule "deps/cpp-httplib"]
|
||||||
path = deps/cpp-httplib
|
path = deps/cpp-httplib
|
||||||
url = https://github.com/yhirose/cpp-httplib
|
url = https://github.com/yhirose/cpp-httplib
|
||||||
[submodule "deps/json"]
|
|
||||||
path = deps/json
|
|
||||||
url = https://github.com/nlohmann/json
|
|
||||||
|
|||||||
+16
-25
@@ -8,15 +8,12 @@ project(BeamMP-Server
|
|||||||
HOMEPAGE_URL https://beammp.com
|
HOMEPAGE_URL https://beammp.com
|
||||||
LANGUAGES CXX C)
|
LANGUAGES CXX C)
|
||||||
|
|
||||||
set(HTTPLIB_REQUIRE_OPENSSL ON)
|
|
||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/asio/asio/include")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/commandline")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib")
|
include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps/json/single_include")
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/deps")
|
include_directories("${PROJECT_SOURCE_DIR}/deps")
|
||||||
|
|
||||||
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
|
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
@@ -37,33 +34,13 @@ if (WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories("include/sentry-native/include")
|
include_directories("include/sentry-native/include")
|
||||||
set(BUILD_SHARED_LIBS OFF)
|
set(SENTRY_BUILD_SHARED_LIBS OFF)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
set(SENTRY_BUILD_RUNTIMESTATIC ON)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Checking for Sentry URL")
|
set(SENTRY_BACKEND breakpad)
|
||||||
# this is set by the build system.
|
|
||||||
# IMPORTANT: if you're building from source, just leave this empty
|
|
||||||
if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL)
|
|
||||||
message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \
|
|
||||||
This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.")
|
|
||||||
set(BEAMMP_SECRET_SENTRY_URL "")
|
|
||||||
set(SENTRY_BACKEND none)
|
|
||||||
else()
|
|
||||||
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
|
|
||||||
message(STATUS "Sentry URL is length ${URL_LEN}")
|
|
||||||
set(SENTRY_BACKEND breakpad)
|
|
||||||
endif()
|
|
||||||
add_subdirectory("deps/sentry-native")
|
add_subdirectory("deps/sentry-native")
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
message(STATUS "Adding local source dependencies")
|
|
||||||
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
|
||||||
add_subdirectory(deps)
|
|
||||||
|
|
||||||
message(STATUS "Setting compiler flags")
|
message(STATUS "Setting compiler flags")
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
||||||
@@ -81,7 +58,21 @@ elseif (UNIX)
|
|||||||
endif (SANITIZE)
|
endif (SANITIZE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
message(STATUS "Checking for Sentry URL")
|
||||||
|
# this is set by the build system.
|
||||||
|
# IMPORTANT: if you're building from source, just leave this empty
|
||||||
|
if (NOT DEFINED BEAMMP_SECRET_SENTRY_URL)
|
||||||
|
message(WARNING "No sentry URL configured. Sentry logging is disabled for this build. \
|
||||||
|
This is not an error, and if you're building the BeamMP-Server yourself, this is expected and can be ignored.")
|
||||||
|
set(BEAMMP_SECRET_SENTRY_URL "")
|
||||||
|
else()
|
||||||
|
string(LENGTH ${BEAMMP_SECRET_SENTRY_URL} URL_LEN)
|
||||||
|
message(STATUS "Sentry URL is length ${URL_LEN}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Adding local source dependencies")
|
||||||
|
# this has to happen before -DDEBUG since it wont compile properly with -DDEBUG
|
||||||
|
add_subdirectory(deps)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,3 @@
|
|||||||
# v3.0.2
|
|
||||||
|
|
||||||
- ADDED Periodic update message if a new server is released
|
|
||||||
- ADDED Config setting for the IP the http server listens on
|
|
||||||
- CHANGED Default MaxPlayers to 8
|
|
||||||
- CHANGED Default http server listen IP to localhost
|
|
||||||
- FIXED `MP.CreateEventTimer` filling up the queue (see <https://wiki.beammp.com/en/Scripting/new-lua-scripting#mpcreateeventtimerevent_name-string-interval_ms-number-strategy-number-since-v302>)
|
|
||||||
- FIXED `MP.TriggerClientEvent` not kicking the client if it failed
|
|
||||||
- FIXED Lua result queue handling not checking all results
|
|
||||||
- FIXED bug which caused ServerConfig.toml to generate incorrectly
|
|
||||||
|
|
||||||
# v3.0.1
|
|
||||||
|
|
||||||
- ADDED Backup URLs to UpdateCheck (will fail less often now)
|
|
||||||
- ADDED console cursor left and right movement (with arrow keys) and working HOME and END key (via github.com/lionkor/commandline)
|
|
||||||
- FIXED infinite snowmen / infinite unicycle spawning bug
|
|
||||||
- FIXED a bug where, when run with --working-directory, the Server.log would still be in the original directory
|
|
||||||
- FIXED a bug which could cause the plugin reload thread to spin at 100% if the reloaded plugin's didn't terminate
|
|
||||||
- FIXED an issue which would cause servers to crash on mod download via SIGPIPE on POSIX
|
|
||||||
- FIXED an issue which would cause servers to crash when checking if a vehicle is a unicycle
|
|
||||||
|
|
||||||
# v3.0.0
|
# v3.0.0
|
||||||
|
|
||||||
- CHANGED entire plugin Lua implementation (rewrite)
|
- CHANGED entire plugin Lua implementation (rewrite)
|
||||||
|
|||||||
Vendored
+1
-1
Submodule deps/commandline updated: d6b1c32c8a...3d11606d02
Vendored
-1
Submodule deps/json deleted from eb21824147
Vendored
+1
-1
Submodule deps/toml11 updated: 1400dd223f...fda0a2b9ab
+1
-1
@@ -92,7 +92,7 @@ private:
|
|||||||
std::queue<std::string> mPacketsSync;
|
std::queue<std::string> mPacketsSync;
|
||||||
std::unordered_map<std::string, std::string> mIdentifiers;
|
std::unordered_map<std::string, std::string> mIdentifiers;
|
||||||
bool mIsGuest = false;
|
bool mIsGuest = false;
|
||||||
mutable std::mutex mVehicleDataMutex;
|
std::mutex mVehicleDataMutex;
|
||||||
TSetOfVehicleData mVehicleData;
|
TSetOfVehicleData mVehicleData;
|
||||||
std::string mName = "Unknown Client";
|
std::string mName = "Unknown Client";
|
||||||
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
|
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
|
||||||
|
|||||||
+6
-14
@@ -7,7 +7,6 @@ extern TSentry Sentry;
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <filesystem>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -44,7 +43,7 @@ public:
|
|||||||
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
std::string SSLKeyPath { "./.ssl/HttpServer/key.pem" };
|
||||||
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
std::string SSLCertPath { "./.ssl/HttpServer/cert.pem" };
|
||||||
bool HTTPServerEnabled { false };
|
bool HTTPServerEnabled { false };
|
||||||
int MaxPlayers { 8 };
|
int MaxPlayers { 10 };
|
||||||
bool Private { true };
|
bool Private { true };
|
||||||
int MaxCars { 1 };
|
int MaxCars { 1 };
|
||||||
bool DebugModeEnabled { false };
|
bool DebugModeEnabled { false };
|
||||||
@@ -53,9 +52,7 @@ public:
|
|||||||
bool SendErrors { true };
|
bool SendErrors { true };
|
||||||
bool SendErrorsMessageEnabled { true };
|
bool SendErrorsMessageEnabled { true };
|
||||||
int HTTPServerPort { 8080 };
|
int HTTPServerPort { 8080 };
|
||||||
std::string HTTPServerIP { "127.0.0.1" };
|
bool HTTPServerUseSSL { true };
|
||||||
bool HTTPServerUseSSL { false };
|
|
||||||
bool HideUpdateMessages { false };
|
|
||||||
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
[[nodiscard]] bool HasCustomIP() const { return !CustomIP.empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,15 +74,10 @@ public:
|
|||||||
|
|
||||||
static TSettings Settings;
|
static TSettings Settings;
|
||||||
|
|
||||||
static std::vector<std::string> GetBackendUrlsInOrder() {
|
|
||||||
return {
|
|
||||||
"backend.beammp.com",
|
|
||||||
"backup1.beammp.com",
|
|
||||||
"backup2.beammp.com"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
static std::string GetBackendUrlForAuth() { return "auth.beammp.com"; }
|
||||||
|
static std::string GetBackendHostname() { return "backend.beammp.com"; }
|
||||||
|
static std::string GetBackup1Hostname() { return "backup1.beammp.com"; }
|
||||||
|
static std::string GetBackup2Hostname() { return "backup2.beammp.com"; }
|
||||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||||
static void CheckForUpdates();
|
static void CheckForUpdates();
|
||||||
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
static std::array<uint8_t, 3> VersionStrToInts(const std::string& str);
|
||||||
@@ -122,7 +114,7 @@ private:
|
|||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
static inline Version mVersion { 3, 0, 2 };
|
static inline Version mVersion { 3, 0, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ThreadName(bool DebugModeOverride = false);
|
std::string ThreadName(bool DebugModeOverride = false);
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ constexpr size_t RSA_DEFAULT_KEYLENGTH { 2048 };
|
|||||||
|
|
||||||
namespace Http {
|
namespace Http {
|
||||||
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr);
|
||||||
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr, const httplib::Headers& headers = {});
|
std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr);
|
||||||
namespace Status {
|
namespace Status {
|
||||||
std::string ToString(int code);
|
std::string ToString(int code);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -15,13 +15,13 @@ public:
|
|||||||
void WriteRaw(const std::string& str);
|
void WriteRaw(const std::string& str);
|
||||||
void InitializeLuaConsole(TLuaEngine& Engine);
|
void InitializeLuaConsole(TLuaEngine& Engine);
|
||||||
void BackupOldLog();
|
void BackupOldLog();
|
||||||
void StartLoggingToFile();
|
|
||||||
Commandline& Internal() { return mCommandline; }
|
Commandline& Internal() { return mCommandline; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RunAsCommand(const std::string& cmd, bool IgnoreNotACommand = false);
|
void RunAsCommand(const std::string& cmd, bool IgnoreNotACommand = false);
|
||||||
void ChangeToLuaConsole(const std::string& LuaStateId);
|
void ChangeToLuaConsole(const std::string& LuaStateId);
|
||||||
void ChangeToRegularConsole();
|
void ChangeToRegularConsole();
|
||||||
|
void HandleLuaInternalCommand(const std::string& cmd);
|
||||||
|
|
||||||
void Command_Lua(const std::string& cmd);
|
void Command_Lua(const std::string& cmd);
|
||||||
void Command_Help(const std::string& cmd);
|
void Command_Help(const std::string& cmd);
|
||||||
@@ -38,6 +38,4 @@ private:
|
|||||||
bool mFirstTime { true };
|
bool mFirstTime { true };
|
||||||
std::string mStateId;
|
std::string mStateId;
|
||||||
const std::string mDefaultStateId = "BEAMMP_SERVER_CONSOLE";
|
const std::string mDefaultStateId = "BEAMMP_SERVER_CONSOLE";
|
||||||
std::ofstream mLogFileStream;
|
|
||||||
std::mutex mLogFileStreamMtx;
|
|
||||||
};
|
};
|
||||||
|
|||||||
+17
-23
@@ -6,7 +6,6 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <list>
|
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -33,8 +32,8 @@ static constexpr size_t TLuaArgTypes_Bool = 3;
|
|||||||
class TLuaPlugin;
|
class TLuaPlugin;
|
||||||
|
|
||||||
struct TLuaResult {
|
struct TLuaResult {
|
||||||
std::atomic_bool Ready;
|
bool Ready;
|
||||||
std::atomic_bool Error;
|
bool Error;
|
||||||
std::string ErrorMessage;
|
std::string ErrorMessage;
|
||||||
sol::object Result { sol::lua_nil };
|
sol::object Result { sol::lua_nil };
|
||||||
TLuaStateId StateId;
|
TLuaStateId StateId;
|
||||||
@@ -73,18 +72,6 @@ private:
|
|||||||
|
|
||||||
class TLuaEngine : IThreaded {
|
class TLuaEngine : IThreaded {
|
||||||
public:
|
public:
|
||||||
enum CallStrategy : int {
|
|
||||||
BestEffort,
|
|
||||||
Precise,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct QueuedFunction {
|
|
||||||
std::string FunctionName;
|
|
||||||
std::shared_ptr<TLuaResult> Result;
|
|
||||||
std::vector<TLuaArgTypes> Args;
|
|
||||||
std::string EventName; // optional, may be empty
|
|
||||||
};
|
|
||||||
|
|
||||||
TLuaEngine();
|
TLuaEngine();
|
||||||
~TLuaEngine() noexcept {
|
~TLuaEngine() noexcept {
|
||||||
beammp_debug("Lua Engine terminated");
|
beammp_debug("Lua Engine terminated");
|
||||||
@@ -102,6 +89,7 @@ public:
|
|||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
return mResultsToCheck.size();
|
return mResultsToCheck.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetLuaStateCount() {
|
size_t GetLuaStateCount() {
|
||||||
std::unique_lock Lock(mLuaStatesMutex);
|
std::unique_lock Lock(mLuaStatesMutex);
|
||||||
return mLuaStates.size();
|
return mLuaStates.size();
|
||||||
@@ -158,14 +146,19 @@ public:
|
|||||||
return Results; //
|
return Results; //
|
||||||
}
|
}
|
||||||
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
std::set<std::string> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId);
|
||||||
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy);
|
void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS);
|
||||||
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
void CancelEventTimers(const std::string& EventName, TLuaStateId StateId);
|
||||||
sol::state_view GetStateForPlugin(const fs::path& PluginPath);
|
sol::state_view GetStateForPlugin(const fs::path& PluginPath);
|
||||||
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
|
TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath);
|
||||||
void AddResultToCheck(const std::shared_ptr<TLuaResult>& Result);
|
|
||||||
|
|
||||||
static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND";
|
static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND";
|
||||||
|
|
||||||
|
// Debugging functions (slow)
|
||||||
|
std::unordered_map<std::string /*event name */, std::vector<std::string> /* handlers */> Debug_GetEventsForState(TLuaStateId StateId);
|
||||||
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId);
|
||||||
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> Debug_GetStateFunctionQueueForState(TLuaStateId StateId);
|
||||||
|
std::vector<TLuaResult> Debug_GetResultsToCheckForState(TLuaStateId StateId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CollectAndInitPlugins();
|
void CollectAndInitPlugins();
|
||||||
void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config);
|
void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config);
|
||||||
@@ -179,12 +172,15 @@ private:
|
|||||||
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
|
||||||
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
|
|
||||||
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
|
||||||
void AddPath(const fs::path& Path); // to be added to path and cpath
|
void AddPath(const fs::path& Path); // to be added to path and cpath
|
||||||
void operator()() override;
|
void operator()() override;
|
||||||
sol::state_view State() { return sol::state_view(mState); }
|
sol::state_view State() { return sol::state_view(mState); }
|
||||||
|
|
||||||
|
// Debug functions, slow
|
||||||
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> Debug_GetStateExecuteQueue();
|
||||||
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> Debug_GetStateFunctionQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
||||||
sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs);
|
||||||
@@ -202,7 +198,7 @@ private:
|
|||||||
std::thread mThread;
|
std::thread mThread;
|
||||||
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue;
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> mStateExecuteQueue;
|
||||||
std::recursive_mutex mStateExecuteQueueMutex;
|
std::recursive_mutex mStateExecuteQueueMutex;
|
||||||
std::vector<QueuedFunction> mStateFunctionQueue;
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> mStateFunctionQueue;
|
||||||
std::mutex mStateFunctionQueueMutex;
|
std::mutex mStateFunctionQueueMutex;
|
||||||
std::condition_variable mStateFunctionQueueCond;
|
std::condition_variable mStateFunctionQueueCond;
|
||||||
TLuaEngine* mEngine;
|
TLuaEngine* mEngine;
|
||||||
@@ -216,7 +212,6 @@ private:
|
|||||||
std::chrono::high_resolution_clock::time_point LastCompletion {};
|
std::chrono::high_resolution_clock::time_point LastCompletion {};
|
||||||
std::string EventName;
|
std::string EventName;
|
||||||
TLuaStateId StateId;
|
TLuaStateId StateId;
|
||||||
CallStrategy Strategy;
|
|
||||||
bool Expired();
|
bool Expired();
|
||||||
void Reset();
|
void Reset();
|
||||||
};
|
};
|
||||||
@@ -233,9 +228,8 @@ private:
|
|||||||
std::recursive_mutex mLuaEventsMutex;
|
std::recursive_mutex mLuaEventsMutex;
|
||||||
std::vector<TimedEvent> mTimedEvents;
|
std::vector<TimedEvent> mTimedEvents;
|
||||||
std::recursive_mutex mTimedEventsMutex;
|
std::recursive_mutex mTimedEventsMutex;
|
||||||
std::list<std::shared_ptr<TLuaResult>> mResultsToCheck;
|
std::queue<std::shared_ptr<TLuaResult>> mResultsToCheck;
|
||||||
std::mutex mResultsToCheckMutex;
|
std::recursive_mutex mResultsToCheckMutex;
|
||||||
std::condition_variable mResultsToCheckCond;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
// std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ void TClient::ClearCars() {
|
|||||||
int TClient::GetOpenCarID() const {
|
int TClient::GetOpenCarID() const {
|
||||||
int OpenID = 0;
|
int OpenID = 0;
|
||||||
bool found;
|
bool found;
|
||||||
std::unique_lock lock(mVehicleDataMutex);
|
|
||||||
do {
|
do {
|
||||||
found = true;
|
found = true;
|
||||||
for (auto& v : mVehicleData) {
|
for (auto& v : mVehicleData) {
|
||||||
|
|||||||
+3
-17
@@ -97,42 +97,28 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status
|
|||||||
|
|
||||||
void Application::CheckForUpdates() {
|
void Application::CheckForUpdates() {
|
||||||
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
|
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
|
||||||
static bool FirstTime = true;
|
|
||||||
// checks current version against latest version
|
// checks current version against latest version
|
||||||
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
|
||||||
for (const auto& url : GetBackendUrlsInOrder()) {
|
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
|
||||||
auto Response = Http::GET(GetBackendUrlsInOrder().at(0), 443, "/v/s");
|
|
||||||
bool Matches = std::regex_match(Response, VersionRegex);
|
bool Matches = std::regex_match(Response, VersionRegex);
|
||||||
if (Matches) {
|
if (Matches) {
|
||||||
auto MyVersion = ServerVersion();
|
auto MyVersion = ServerVersion();
|
||||||
auto RemoteVersion = Version(VersionStrToInts(Response));
|
auto RemoteVersion = Version(VersionStrToInts(Response));
|
||||||
if (IsOutdated(MyVersion, RemoteVersion)) {
|
if (IsOutdated(MyVersion, RemoteVersion)) {
|
||||||
std::string RealVersionString = RemoteVersion.AsString();
|
std::string RealVersionString = RemoteVersion.AsString();
|
||||||
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION IS OUT! Please update to the new version (v" + RealVersionString + ") of the BeamMP-Server! Download it here: https://beammp.com/! For a guide on how to update, visit: https://wiki.beammp.com/en/home/server-maintenance#updating-the-server" + std::string(ANSI_RESET));
|
beammp_warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For more info visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET));
|
||||||
} else {
|
} else {
|
||||||
if (FirstTime) {
|
|
||||||
beammp_info("Server up-to-date!");
|
beammp_info("Server up-to-date!");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
|
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
if (FirstTime) {
|
beammp_warn("Unable to fetch version from backend.");
|
||||||
beammp_debug("Failed to fetch version from: " + url);
|
|
||||||
beammp_trace("got " + Response);
|
beammp_trace("got " + Response);
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
auto Lock = Sentry.CreateExclusiveContext();
|
||||||
Sentry.SetContext("get-response", { { "response", Response } });
|
Sentry.SetContext("get-response", { { "response", Response } });
|
||||||
Sentry.LogError("failed to get server version", _file_basename, _line);
|
Sentry.LogError("failed to get server version", _file_basename, _line);
|
||||||
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
|
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Application::GetSubsystemStatuses().at("UpdateCheck") == Application::Status::Bad) {
|
|
||||||
if (FirstTime) {
|
|
||||||
beammp_warn("Unable to fetch version info from backend.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FirstTime = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// thread name stuff
|
// thread name stuff
|
||||||
|
|||||||
+4
-16
@@ -4,7 +4,6 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
#include "httplib.h"
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -34,20 +33,17 @@ std::string Http::GET(const std::string& host, int port, const std::string& targ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status, const httplib::Headers& headers) {
|
std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status) {
|
||||||
httplib::SSLClient client(host, port);
|
httplib::SSLClient client(host, port);
|
||||||
client.set_read_timeout(std::chrono::seconds(10));
|
|
||||||
beammp_assert(client.is_valid());
|
|
||||||
client.enable_server_certificate_verification(false);
|
client.enable_server_certificate_verification(false);
|
||||||
client.set_address_family(AF_INET);
|
client.set_address_family(AF_INET);
|
||||||
auto res = client.Post(target.c_str(), headers, body.c_str(), body.size(), ContentType.c_str());
|
auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str());
|
||||||
if (res) {
|
if (res) {
|
||||||
if (status) {
|
if (status) {
|
||||||
*status = res->status;
|
*status = res->status;
|
||||||
}
|
}
|
||||||
return res->body;
|
return res->body;
|
||||||
} else {
|
} else {
|
||||||
beammp_debug("POST failed: " + httplib::to_string(res.error()));
|
|
||||||
return Http::ErrorString;
|
return Http::ErrorString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +289,7 @@ Http::Server::THttpServerInstance::THttpServerInstance() {
|
|||||||
mThread.detach();
|
mThread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::Server::THttpServerInstance::operator()() try {
|
void Http::Server::THttpServerInstance::operator()() {
|
||||||
beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort));
|
beammp_info("HTTP(S) Server started on port " + std::to_string(Application::Settings.HTTPServerPort));
|
||||||
std::unique_ptr<httplib::Server> HttpLibServerInstance;
|
std::unique_ptr<httplib::Server> HttpLibServerInstance;
|
||||||
if (Application::Settings.HTTPServerUseSSL) {
|
if (Application::Settings.HTTPServerUseSSL) {
|
||||||
@@ -370,14 +366,6 @@ void Http::Server::THttpServerInstance::operator()() try {
|
|||||||
HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) {
|
HttpLibServerInstance->Get({ 0x2f, 0x6b, 0x69, 0x74, 0x74, 0x79 }, [](const httplib::Request&, httplib::Response& res) {
|
||||||
res.set_content(std::string(Magic), "text/plain");
|
res.set_content(std::string(Magic), "text/plain");
|
||||||
});
|
});
|
||||||
HttpLibServerInstance->set_logger([](const httplib::Request& Req, const httplib::Response& Res) {
|
|
||||||
beammp_debug("Http Server: " + Req.method + " " + Req.target + " -> " + std::to_string(Res.status));
|
|
||||||
});
|
|
||||||
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
|
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
|
||||||
auto ret = HttpLibServerInstance->listen(Application::Settings.HTTPServerIP.c_str(), Application::Settings.HTTPServerPort);
|
HttpLibServerInstance->listen("0.0.0.0", Application::Settings.HTTPServerPort);
|
||||||
if (!ret) {
|
|
||||||
beammp_error("Failed to start http server (failed to listen). Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it.");
|
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
beammp_error("Failed to start http server. Please ensure the http server is configured properly in the ServerConfig.toml, or turn it off if you don't need it. Error: " + std::string(e.what()));
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -112,8 +112,7 @@ bool LuaAPI::MP::TriggerClientEvent(int PlayerID, const std::string& EventName,
|
|||||||
}
|
}
|
||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!Engine->Network().Respond(*c, Packet, true)) {
|
if (!Engine->Network().Respond(*c, Packet, true)) {
|
||||||
beammp_lua_error("Respond failed, dropping client " + std::to_string(PlayerID));
|
beammp_lua_error("Respond failed");
|
||||||
Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-41
@@ -17,19 +17,15 @@ static constexpr std::string_view StrName = "Name";
|
|||||||
static constexpr std::string_view StrDescription = "Description";
|
static constexpr std::string_view StrDescription = "Description";
|
||||||
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
static constexpr std::string_view StrResourceFolder = "ResourceFolder";
|
||||||
static constexpr std::string_view StrAuthKey = "AuthKey";
|
static constexpr std::string_view StrAuthKey = "AuthKey";
|
||||||
|
|
||||||
// Misc
|
|
||||||
static constexpr std::string_view StrSendErrors = "SendErrors";
|
static constexpr std::string_view StrSendErrors = "SendErrors";
|
||||||
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
static constexpr std::string_view StrSendErrorsMessageEnabled = "SendErrorsShowMessage";
|
||||||
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
|
||||||
|
|
||||||
// HTTP
|
|
||||||
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
static constexpr std::string_view StrHTTPServerEnabled = "HTTPServerEnabled";
|
||||||
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
static constexpr std::string_view StrHTTPServerUseSSL = "UseSSL";
|
||||||
|
|
||||||
|
// HTTP
|
||||||
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
|
static constexpr std::string_view StrSSLKeyPath = "SSLKeyPath";
|
||||||
static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
|
static constexpr std::string_view StrSSLCertPath = "SSLCertPath";
|
||||||
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
|
||||||
static constexpr std::string_view StrHTTPServerIP = "HTTPServerIP";
|
|
||||||
|
|
||||||
TConfig::TConfig(const std::string& ConfigFileName)
|
TConfig::TConfig(const std::string& ConfigFileName)
|
||||||
: mConfigFileName(ConfigFileName) {
|
: mConfigFileName(ConfigFileName) {
|
||||||
@@ -62,8 +58,8 @@ void SetComment(CommentsT& Comments, const std::string& Comment) {
|
|||||||
* whether it is in TConfig.cpp or the configuration file.
|
* whether it is in TConfig.cpp or the configuration file.
|
||||||
*/
|
*/
|
||||||
void TConfig::FlushToFile() {
|
void TConfig::FlushToFile() {
|
||||||
// auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
auto data = toml::parse<toml::preserve_comments>(mConfigFileName);
|
||||||
auto data = toml::value {};
|
data["General"] = toml::table();
|
||||||
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
data["General"][StrAuthKey.data()] = Application::Settings.Key;
|
||||||
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
SetComment(data["General"][StrAuthKey.data()].comments(), " AuthKey has to be filled out in order to run the server");
|
||||||
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
data["General"][StrDebug.data()] = Application::Settings.DebugModeEnabled;
|
||||||
@@ -75,39 +71,19 @@ void TConfig::FlushToFile() {
|
|||||||
data["General"][StrMap.data()] = Application::Settings.MapName;
|
data["General"][StrMap.data()] = Application::Settings.MapName;
|
||||||
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
data["General"][StrDescription.data()] = Application::Settings.ServerDesc;
|
||||||
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
data["General"][StrResourceFolder.data()] = Application::Settings.Resource;
|
||||||
// Misc
|
data["General"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
||||||
data["Misc"][StrHideUpdateMessages.data()] = Application::Settings.HideUpdateMessages;
|
SetComment(data["General"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
||||||
SetComment(data["Misc"][StrHideUpdateMessages.data()].comments(), " Hides the periodic update message which notifies you of a new server version. You should really keep this on and always update as soon as possible. For more information visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server. An update message will always appear at startup regardless.");
|
data["General"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
||||||
data["Misc"][StrSendErrors.data()] = Application::Settings.SendErrors;
|
SetComment(data["General"][StrSendErrorsMessageEnabled.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`");
|
||||||
SetComment(data["Misc"][StrSendErrors.data()].comments(), " You can turn on/off the SendErrors message you get on startup here");
|
|
||||||
data["Misc"][StrSendErrorsMessageEnabled.data()] = Application::Settings.SendErrorsMessageEnabled;
|
|
||||||
SetComment(data["Misc"][StrSendErrorsMessageEnabled.data()].comments(), " If SendErrors is `true`, the server will send helpful info about crashes and other issues back to the BeamMP developers. This info may include your config, who is on your server at the time of the error, and similar general information. This kind of data is vital in helping us diagnose and fix issues faster. This has no impact on server performance. You can opt-out of this system by setting this to `false`");
|
|
||||||
// HTTP
|
|
||||||
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
|
data["HTTP"][StrSSLKeyPath.data()] = Application::Settings.SSLKeyPath;
|
||||||
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
|
data["HTTP"][StrSSLCertPath.data()] = Application::Settings.SSLCertPath;
|
||||||
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
data["HTTP"][StrHTTPServerPort.data()] = Application::Settings.HTTPServerPort;
|
||||||
SetComment(data["HTTP"][StrHTTPServerIP.data()].comments(), " Which IP to listen on. Pick 0.0.0.0 for a public-facing server with no specific IP, and 127.0.0.1 or 'localhost' for a local server.");
|
|
||||||
data["HTTP"][StrHTTPServerIP.data()] = Application::Settings.HTTPServerIP;
|
|
||||||
data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL;
|
data["HTTP"][StrHTTPServerUseSSL.data()] = Application::Settings.HTTPServerUseSSL;
|
||||||
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to have enabled for servers which face the internet. With SSL the server will serve https and requires valid key and cert files");
|
SetComment(data["HTTP"][StrHTTPServerUseSSL.data()].comments(), " Recommended to keep enabled. With SSL the server will serve https and requires valid key and cert files");
|
||||||
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
data["HTTP"][StrHTTPServerEnabled.data()] = Application::Settings.HTTPServerEnabled;
|
||||||
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
SetComment(data["HTTP"][StrHTTPServerEnabled.data()].comments(), " Enables the internal HTTP server");
|
||||||
std::stringstream Ss;
|
std::ofstream Stream(mConfigFileName);
|
||||||
Ss << "# This is the BeamMP-Server config file.\n"
|
Stream << data << std::flush;
|
||||||
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
|
|
||||||
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
|
|
||||||
<< data;
|
|
||||||
auto File = std::fopen(mConfigFileName.c_str(), "w+");
|
|
||||||
if (!File) {
|
|
||||||
beammp_error("Failed to create/write to config file: " + GetPlatformAgnosticErrorString());
|
|
||||||
throw std::runtime_error("Failed to create/write to config file");
|
|
||||||
}
|
|
||||||
auto Str = Ss.str();
|
|
||||||
auto N = std::fwrite(Str.data(), sizeof(char), Str.size(), File);
|
|
||||||
if (N != Str.size()) {
|
|
||||||
beammp_error("Failed to write to config file properly, config file might be misshapen");
|
|
||||||
}
|
|
||||||
std::fclose(File);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConfig::CreateConfigFile(std::string_view name) {
|
void TConfig::CreateConfigFile(std::string_view name) {
|
||||||
@@ -122,7 +98,32 @@ void TConfig::CreateConfigFile(std::string_view name) {
|
|||||||
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
beammp_error("an error occurred and was ignored during config transfer: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // create file context
|
||||||
|
std::ofstream ofs(name.data());
|
||||||
|
}
|
||||||
|
|
||||||
FlushToFile();
|
FlushToFile();
|
||||||
|
|
||||||
|
size_t FileSize = fs::file_size(name);
|
||||||
|
std::fstream ofs { std::string(name), std::ios::in | std::ios::out };
|
||||||
|
if (ofs.good()) {
|
||||||
|
std::string Contents {};
|
||||||
|
Contents.resize(FileSize);
|
||||||
|
ofs.readsome(Contents.data(), FileSize);
|
||||||
|
ofs.seekp(0);
|
||||||
|
ofs << "# This is the BeamMP-Server config file.\n"
|
||||||
|
"# Help & Documentation: `https://wiki.beammp.com/en/home/server-maintenance`\n"
|
||||||
|
"# IMPORTANT: Fill in the AuthKey with the key you got from `https://beammp.com/k/dashboard` on the left under \"Keys\"\n"
|
||||||
|
<< '\n'
|
||||||
|
<< Contents;
|
||||||
|
beammp_error("There was no \"" + std::string(mConfigFileName) + "\" file (this is normal for the first time running the server), so one was generated for you. It was automatically filled with the settings from your Server.cfg, if you have one. Please open ServerConfig.toml and ensure your AuthKey and other settings are filled in and correct, then restart the server. The old Server.cfg file will no longer be used and causes a warning if it exists from now on.");
|
||||||
|
mFailed = true;
|
||||||
|
ofs.close();
|
||||||
|
} else {
|
||||||
|
beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
|
||||||
|
Application::SetSubsystemStatus("Config", Application::Status::Bad);
|
||||||
|
mFailed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) {
|
void TConfig::TryReadValue(toml::value& Table, const std::string& Category, const std::string_view& Key, std::string& OutValue) {
|
||||||
@@ -157,15 +158,12 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "General", StrDescription, Application::Settings.ServerDesc);
|
TryReadValue(data, "General", StrDescription, Application::Settings.ServerDesc);
|
||||||
TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource);
|
TryReadValue(data, "General", StrResourceFolder, Application::Settings.Resource);
|
||||||
TryReadValue(data, "General", StrAuthKey, Application::Settings.Key);
|
TryReadValue(data, "General", StrAuthKey, Application::Settings.Key);
|
||||||
// Misc
|
TryReadValue(data, "General", StrSendErrors, Application::Settings.SendErrors);
|
||||||
TryReadValue(data, "Misc", StrSendErrors, Application::Settings.SendErrors);
|
TryReadValue(data, "General", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
||||||
TryReadValue(data, "Misc", StrHideUpdateMessages, Application::Settings.HideUpdateMessages);
|
|
||||||
TryReadValue(data, "Misc", StrSendErrorsMessageEnabled, Application::Settings.SendErrorsMessageEnabled);
|
|
||||||
// HTTP
|
// HTTP
|
||||||
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
|
TryReadValue(data, "HTTP", StrSSLKeyPath, Application::Settings.SSLKeyPath);
|
||||||
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
|
TryReadValue(data, "HTTP", StrSSLCertPath, Application::Settings.SSLCertPath);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort);
|
TryReadValue(data, "HTTP", StrHTTPServerPort, Application::Settings.HTTPServerPort);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerIP, Application::Settings.HTTPServerIP);
|
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled);
|
TryReadValue(data, "HTTP", StrHTTPServerEnabled, Application::Settings.HTTPServerEnabled);
|
||||||
TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL);
|
TryReadValue(data, "HTTP", StrHTTPServerUseSSL, Application::Settings.HTTPServerUseSSL);
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
@@ -204,7 +202,6 @@ void TConfig::PrintDebug() {
|
|||||||
beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\"");
|
beammp_debug(std::string(StrSSLKeyPath) + ": \"" + Application::Settings.SSLKeyPath + "\"");
|
||||||
beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\"");
|
beammp_debug(std::string(StrSSLCertPath) + ": \"" + Application::Settings.SSLCertPath + "\"");
|
||||||
beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\"");
|
beammp_debug(std::string(StrHTTPServerPort) + ": \"" + std::to_string(Application::Settings.HTTPServerPort) + "\"");
|
||||||
beammp_debug(std::string(StrHTTPServerIP) + ": \"" + Application::Settings.HTTPServerIP + "\"");
|
|
||||||
// special!
|
// special!
|
||||||
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
beammp_debug("Key Length: " + std::to_string(Application::Settings.Key.length()) + "");
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-18
@@ -97,17 +97,6 @@ void TConsole::BackupOldLog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TConsole::StartLoggingToFile() {
|
|
||||||
mLogFileStream.open("Server.log");
|
|
||||||
Application::Console().Internal().on_write = [this](const std::string& ToWrite) {
|
|
||||||
// TODO: Sanitize by removing all ansi escape codes (vt100)
|
|
||||||
std::unique_lock Lock(mLogFileStreamMtx);
|
|
||||||
mLogFileStream.write(ToWrite.c_str(), ToWrite.size());
|
|
||||||
mLogFileStream.write("\n", 1);
|
|
||||||
mLogFileStream.flush();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) {
|
void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) {
|
||||||
if (!mIsLuaConsole) {
|
if (!mIsLuaConsole) {
|
||||||
if (!mLuaEngine) {
|
if (!mLuaEngine) {
|
||||||
@@ -118,10 +107,10 @@ void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) {
|
|||||||
mStateId = LuaStateId;
|
mStateId = LuaStateId;
|
||||||
mIsLuaConsole = true;
|
mIsLuaConsole = true;
|
||||||
if (mStateId != mDefaultStateId) {
|
if (mStateId != mDefaultStateId) {
|
||||||
Application::Console().WriteRaw("Entered Lua console for state '" + mStateId + "'. To exit, type `exit()`");
|
Application::Console().WriteRaw("Attached to Lua state '" + mStateId + "'. For help, type `:help`. To detach, type `:detach`");
|
||||||
mCommandline.set_prompt("lua @" + LuaStateId + "> ");
|
mCommandline.set_prompt("lua @" + LuaStateId + "> ");
|
||||||
} else {
|
} else {
|
||||||
Application::Console().WriteRaw("Entered Lua console. To exit, type `exit()`");
|
Application::Console().WriteRaw("Attached to Lua. For help, type `:help`. To detach, type `:detach`");
|
||||||
mCommandline.set_prompt("lua> ");
|
mCommandline.set_prompt("lua> ");
|
||||||
}
|
}
|
||||||
mCachedRegularHistory = mCommandline.history();
|
mCachedRegularHistory = mCommandline.history();
|
||||||
@@ -133,9 +122,9 @@ void TConsole::ChangeToRegularConsole() {
|
|||||||
if (mIsLuaConsole) {
|
if (mIsLuaConsole) {
|
||||||
mIsLuaConsole = false;
|
mIsLuaConsole = false;
|
||||||
if (mStateId != mDefaultStateId) {
|
if (mStateId != mDefaultStateId) {
|
||||||
Application::Console().WriteRaw("Left Lua console for state '" + mStateId + "'.");
|
Application::Console().WriteRaw("Detached from Lua state '" + mStateId + "'.");
|
||||||
} else {
|
} else {
|
||||||
Application::Console().WriteRaw("Left Lua console.");
|
Application::Console().WriteRaw("Detached from Lua.");
|
||||||
}
|
}
|
||||||
mCachedLuaHistory = mCommandline.history();
|
mCachedLuaHistory = mCommandline.history();
|
||||||
mCommandline.set_history(mCachedRegularHistory);
|
mCommandline.set_history(mCachedRegularHistory);
|
||||||
@@ -375,11 +364,67 @@ void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TConsole::HandleLuaInternalCommand(const std::string& cmd) {
|
||||||
|
if (cmd == "detach") {
|
||||||
|
ChangeToRegularConsole();
|
||||||
|
} else if (cmd == "queued") {
|
||||||
|
auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId);
|
||||||
|
Application::Console().WriteRaw("Pending functions in State '" + mStateId + "'");
|
||||||
|
std::unordered_map<std::string, size_t> FunctionsCount;
|
||||||
|
std::vector<std::string> FunctionsInOrder;
|
||||||
|
while (!QueuedFunctions.empty()) {
|
||||||
|
auto Tuple = QueuedFunctions.front();
|
||||||
|
QueuedFunctions.pop();
|
||||||
|
FunctionsInOrder.push_back(std::get<0>(Tuple));
|
||||||
|
FunctionsCount[std::get<0>(Tuple)] += 1;
|
||||||
|
}
|
||||||
|
std::set<std::string> Uniques;
|
||||||
|
for (const auto& Function : FunctionsInOrder) {
|
||||||
|
if (Uniques.count(Function) == 0) {
|
||||||
|
Uniques.insert(Function);
|
||||||
|
if (FunctionsCount.at(Function) > 1) {
|
||||||
|
Application::Console().WriteRaw(" " + Function + " (" + std::to_string(FunctionsCount.at(Function)) + "x)");
|
||||||
|
} else {
|
||||||
|
Application::Console().WriteRaw(" " + Function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Application::Console().WriteRaw("Executed functions waiting to be checked in State '" + mStateId + "'");
|
||||||
|
for (const auto& Function : LuaAPI::MP::Engine->Debug_GetResultsToCheckForState(mStateId)) {
|
||||||
|
Application::Console().WriteRaw(" '" + Function.Function + "' (Ready? " + (Function.Ready ? "Yes" : "No") + ", Error? " + (Function.Error ? "Yes: '" + Function.ErrorMessage + "'" : "No") + ")");
|
||||||
|
}
|
||||||
|
} else if (cmd == "events") {
|
||||||
|
auto Events = LuaAPI::MP::Engine->Debug_GetEventsForState(mStateId);
|
||||||
|
Application::Console().WriteRaw("Registered Events + Handlers for State '" + mStateId + "'");
|
||||||
|
for (const auto& EventHandlerPair : Events) {
|
||||||
|
Application::Console().WriteRaw(" Event '" + EventHandlerPair.first + "'");
|
||||||
|
for (const auto& Handler : EventHandlerPair.second) {
|
||||||
|
Application::Console().WriteRaw(" " + Handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cmd == "help") {
|
||||||
|
Application::Console().WriteRaw(R"(BeamMP Lua Debugger
|
||||||
|
All commands must be prefixed with a `:`. Non-prefixed commands are interpreted as Lua.
|
||||||
|
|
||||||
|
Commands
|
||||||
|
:detach detaches (exits) from this Lua console
|
||||||
|
:help displays this help
|
||||||
|
:events shows a list of currently registered events
|
||||||
|
:queued shows a list of all pending and queued functions)");
|
||||||
|
} else {
|
||||||
|
beammp_error("internal command '" + cmd + "' is not known");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TConsole::TConsole() {
|
TConsole::TConsole() {
|
||||||
mCommandline.enable_history();
|
mCommandline.enable_history();
|
||||||
mCommandline.set_history_limit(20);
|
mCommandline.set_history_limit(20);
|
||||||
mCommandline.set_prompt("> ");
|
mCommandline.set_prompt("> ");
|
||||||
BackupOldLog();
|
BackupOldLog();
|
||||||
|
bool success = mCommandline.enable_write_to_file("Server.log");
|
||||||
|
if (!success) {
|
||||||
|
beammp_error("unable to open file for writing: \"Server.log\"");
|
||||||
|
}
|
||||||
mCommandline.on_command = [this](Commandline& c) {
|
mCommandline.on_command = [this](Commandline& c) {
|
||||||
try {
|
try {
|
||||||
auto cmd = c.get_command();
|
auto cmd = c.get_command();
|
||||||
@@ -388,12 +433,12 @@ TConsole::TConsole() {
|
|||||||
if (mIsLuaConsole) {
|
if (mIsLuaConsole) {
|
||||||
if (!mLuaEngine) {
|
if (!mLuaEngine) {
|
||||||
beammp_info("Lua not started yet, please try again in a second");
|
beammp_info("Lua not started yet, please try again in a second");
|
||||||
} else if (cmd == "exit()") {
|
} else if (!cmd.empty() && cmd.at(0) == ':') {
|
||||||
ChangeToRegularConsole();
|
HandleLuaInternalCommand(cmd.substr(1));
|
||||||
} else {
|
} else {
|
||||||
auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" });
|
auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared<std::string>(cmd), "", "" });
|
||||||
while (!Future->Ready) {
|
while (!Future->Ready) {
|
||||||
std::this_thread::yield(); // TODO: Add a timeout
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // TODO: Add a timeout
|
||||||
}
|
}
|
||||||
if (Future->Error) {
|
if (Future->Error) {
|
||||||
beammp_lua_error(Future->ErrorMessage);
|
beammp_lua_error(Future->ErrorMessage);
|
||||||
|
|||||||
+31
-73
@@ -19,9 +19,7 @@ void THeartbeatThread::operator()() {
|
|||||||
|
|
||||||
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||||
bool isAuth = false;
|
bool isAuth = false;
|
||||||
size_t UpdateReminderCounter = 0;
|
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
++UpdateReminderCounter;
|
|
||||||
Body = GenerateCall();
|
Body = GenerateCall();
|
||||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||||
auto Now = std::chrono::high_resolution_clock::now();
|
auto Now = std::chrono::high_resolution_clock::now();
|
||||||
@@ -56,82 +54,42 @@ void THeartbeatThread::operator()() {
|
|||||||
|
|
||||||
auto Target = "/heartbeat";
|
auto Target = "/heartbeat";
|
||||||
unsigned int ResponseCode = 0;
|
unsigned int ResponseCode = 0;
|
||||||
|
T = Http::POST(Application::GetBackendHostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
|
|
||||||
json::Document Doc;
|
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
||||||
bool Ok = false;
|
beammp_trace("got " + T + " from backend");
|
||||||
for (const auto& Url : Application::GetBackendUrlsInOrder()) {
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
|
||||||
T = Http::POST(Url, 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode, { { "api-v", "2" } });
|
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
|
||||||
beammp_trace(T);
|
|
||||||
Doc.Parse(T.data(), T.size());
|
|
||||||
if (Doc.HasParseError() || !Doc.IsObject()) {
|
|
||||||
beammp_debug("Failed to contact backend at " + Url + " (this is not an error).");
|
|
||||||
beammp_trace("Response was: " + T);
|
|
||||||
Sentry.SetContext("JSON Response", { { "reponse", T } });
|
|
||||||
SentryReportError(Url + Target, ResponseCode);
|
|
||||||
} else if (ResponseCode != 200) {
|
|
||||||
SentryReportError(Url + Target, ResponseCode);
|
|
||||||
} else {
|
|
||||||
// all ok
|
|
||||||
Ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
}
|
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
std::string Status {};
|
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
||||||
std::string Code {};
|
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
|
||||||
std::string Message {};
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
|
||||||
const auto StatusKey = "status";
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
const auto CodeKey = "code";
|
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
|
||||||
const auto MessageKey = "msg";
|
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
|
||||||
|
beammp_warn("Backend system refused server! Server will not show in the public server list.");
|
||||||
if (Ok) {
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
|
||||||
if (Doc.HasMember(StatusKey) && Doc[StatusKey].IsString()) {
|
isAuth = false;
|
||||||
Status = Doc[StatusKey].GetString();
|
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
|
||||||
} else {
|
} else {
|
||||||
Sentry.SetContext("JSON Response", { { StatusKey, "invalid string / missing" } });
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
if (Doc.HasMember(CodeKey) && Doc[CodeKey].IsString()) {
|
|
||||||
Code = Doc[CodeKey].GetString();
|
|
||||||
} else {
|
|
||||||
Sentry.SetContext("JSON Response", { { CodeKey, "invalid string / missing" } });
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
if (Doc.HasMember(MessageKey) && Doc[MessageKey].IsString()) {
|
|
||||||
Message = Doc[MessageKey].GetString();
|
|
||||||
} else {
|
|
||||||
Sentry.SetContext("JSON Response", { { MessageKey, "invalid string / missing" } });
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
if (!Ok) {
|
|
||||||
beammp_error("Missing/invalid json members in backend response");
|
|
||||||
Sentry.LogError("Missing/invalid json members in backend response", __FILE__, std::to_string(__LINE__));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!Application::Settings.Private) {
|
|
||||||
beammp_warn("Backend failed to respond to a heartbeat. Your server may temporarily disappear from the server list. This is not an error, and will likely resolve itself soon. Direct connect will still work.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Ok && !isAuth) {
|
|
||||||
if (Status == "2000") {
|
|
||||||
beammp_info(("Authenticated!"));
|
|
||||||
isAuth = true;
|
|
||||||
} else if (Status == "200") {
|
|
||||||
beammp_info(("Resumed authenticated session!"));
|
|
||||||
isAuth = true;
|
|
||||||
} else {
|
|
||||||
if (Message.empty()) {
|
|
||||||
Message = "Backend didn't provide a reason";
|
|
||||||
}
|
|
||||||
beammp_error("Backend REFUSED the auth key. " + Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isAuth) {
|
|
||||||
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||||
}
|
}
|
||||||
if (!Application::Settings.HideUpdateMessages && UpdateReminderCounter % 5) {
|
} else {
|
||||||
Application::CheckForUpdates();
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuth) {
|
||||||
|
if (T == "2000") {
|
||||||
|
beammp_info(("Authenticated!"));
|
||||||
|
isAuth = true;
|
||||||
|
} else if (T == "200") {
|
||||||
|
beammp_info(("Resumed authenticated session!"));
|
||||||
|
isAuth = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-92
@@ -44,7 +44,7 @@ void TLuaEngine::operator()() {
|
|||||||
CollectAndInitPlugins();
|
CollectAndInitPlugins();
|
||||||
// now call all onInit's
|
// now call all onInit's
|
||||||
auto Futures = TriggerEvent("onInit", "");
|
auto Futures = TriggerEvent("onInit", "");
|
||||||
WaitForAll(Futures, std::chrono::seconds(5));
|
WaitForAll(Futures);
|
||||||
for (const auto& Future : Futures) {
|
for (const auto& Future : Futures) {
|
||||||
if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) {
|
if (Future->Error && Future->ErrorMessage != BeamMPFnNotFoundError) {
|
||||||
beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage);
|
beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage);
|
||||||
@@ -54,20 +54,31 @@ void TLuaEngine::operator()() {
|
|||||||
auto ResultCheckThread = std::thread([&] {
|
auto ResultCheckThread = std::thread([&] {
|
||||||
RegisterThread("ResultCheckThread");
|
RegisterThread("ResultCheckThread");
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20));
|
|
||||||
if (!mResultsToCheck.empty()) {
|
if (!mResultsToCheck.empty()) {
|
||||||
mResultsToCheck.remove_if([](const std::shared_ptr<TLuaResult>& Ptr) -> bool {
|
auto Res = mResultsToCheck.front();
|
||||||
if (Ptr->Ready) {
|
mResultsToCheck.pop();
|
||||||
return true;
|
Lock.unlock();
|
||||||
} else if (Ptr->Error) {
|
|
||||||
if (Ptr->ErrorMessage != BeamMPFnNotFoundError) {
|
size_t Waited = 0;
|
||||||
beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage);
|
while (!Res->Ready) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
Waited++;
|
||||||
|
if (Waited > 250) {
|
||||||
|
// FIXME: This should *eventually* timeout.
|
||||||
|
// beammp_lua_error(Res->Function + " in " + Res->StateId + " took >1s to respond, not printing possible errors");
|
||||||
|
Lock.lock();
|
||||||
|
mResultsToCheck.push(Res);
|
||||||
|
Lock.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Res->Error) {
|
||||||
|
if (Res->ErrorMessage != BeamMPFnNotFoundError) {
|
||||||
|
beammp_lua_error(Res->Function + ": " + Res->ErrorMessage);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -75,37 +86,27 @@ void TLuaEngine::operator()() {
|
|||||||
auto Before = std::chrono::high_resolution_clock::now();
|
auto Before = std::chrono::high_resolution_clock::now();
|
||||||
while (!mShutdown) {
|
while (!mShutdown) {
|
||||||
if (mLuaStates.size() == 0) {
|
if (mLuaStates.size() == 0) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(100));
|
std::this_thread::sleep_for(std::chrono::seconds(500));
|
||||||
}
|
}
|
||||||
{ // Timed Events Scope
|
{ // Timed Events Scope
|
||||||
std::unique_lock Lock(mTimedEventsMutex);
|
std::unique_lock Lock(mTimedEventsMutex);
|
||||||
for (auto& Timer : mTimedEvents) {
|
for (auto& Timer : mTimedEvents) {
|
||||||
if (Timer.Expired()) {
|
if (Timer.Expired()) {
|
||||||
auto LastCompletionBeforeReset = Timer.LastCompletion;
|
|
||||||
Timer.Reset();
|
Timer.Reset();
|
||||||
auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId);
|
auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId);
|
||||||
std::unique_lock StateLock(mLuaStatesMutex);
|
std::unique_lock StateLock(mLuaStatesMutex);
|
||||||
std::unique_lock Lock2(mResultsToCheckMutex);
|
std::unique_lock Lock2(mResultsToCheckMutex);
|
||||||
for (auto& Handler : Handlers) {
|
for (auto& Handler : Handlers) {
|
||||||
auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCallFromCustomEvent(Handler, {}, Timer.EventName, Timer.Strategy);
|
auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCall(Handler, {});
|
||||||
if (Res) {
|
mResultsToCheck.push(Res);
|
||||||
mResultsToCheck.push_back(Res);
|
|
||||||
mResultsToCheckCond.notify_one();
|
|
||||||
} else {
|
|
||||||
// "revert" reset
|
|
||||||
Timer.LastCompletion = LastCompletionBeforeReset;
|
|
||||||
// beammp_trace("Reverted reset of \"" + Timer.EventName + "\" timer");
|
|
||||||
// no need to try to enqueue more handlers for this event (they will all fail)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
std::chrono::high_resolution_clock::duration Diff;
|
||||||
const auto Expected = std::chrono::milliseconds(10);
|
if ((Diff = std::chrono::high_resolution_clock::now() - Before)
|
||||||
if (auto Diff = std::chrono::high_resolution_clock::now() - Before;
|
< std::chrono::milliseconds(10)) {
|
||||||
Diff < Expected) {
|
std::this_thread::sleep_for(Diff);
|
||||||
std::this_thread::sleep_for(Expected - Diff);
|
|
||||||
} else {
|
} else {
|
||||||
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
|
beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind");
|
||||||
}
|
}
|
||||||
@@ -148,10 +149,48 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::AddResultToCheck(const std::shared_ptr<TLuaResult>& Result) {
|
std::unordered_map<std::string /*event name */, std::vector<std::string> /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) {
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> Result;
|
||||||
|
std::unique_lock Lock(mLuaEventsMutex);
|
||||||
|
for (const auto& EventNameToEventMap : mLuaEvents) {
|
||||||
|
for (const auto& IdSetOfHandlersPair : EventNameToEventMap.second) {
|
||||||
|
if (IdSetOfHandlersPair.first == StateId) {
|
||||||
|
for (const auto& Handler : IdSetOfHandlersPair.second) {
|
||||||
|
Result[EventNameToEventMap.first].push_back(Handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> TLuaEngine::Debug_GetStateExecuteQueueForState(TLuaStateId StateId) {
|
||||||
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> Result;
|
||||||
|
std::unique_lock Lock(mLuaStatesMutex);
|
||||||
|
Result = mLuaStates.at(StateId)->Debug_GetStateExecuteQueue();
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) {
|
||||||
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> Result;
|
||||||
|
std::unique_lock Lock(mLuaStatesMutex);
|
||||||
|
Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue();
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TLuaResult> TLuaEngine::Debug_GetResultsToCheckForState(TLuaStateId StateId) {
|
||||||
std::unique_lock Lock(mResultsToCheckMutex);
|
std::unique_lock Lock(mResultsToCheckMutex);
|
||||||
mResultsToCheck.push_back(Result);
|
auto ResultsToCheckCopy = mResultsToCheck;
|
||||||
mResultsToCheckCond.notify_one();
|
Lock.unlock();
|
||||||
|
std::vector<TLuaResult> Result;
|
||||||
|
while (!ResultsToCheckCopy.empty()) {
|
||||||
|
auto ResultToCheck = std::move(ResultsToCheckCopy.front());
|
||||||
|
ResultsToCheckCopy.pop();
|
||||||
|
if (ResultToCheck->StateId == StateId) {
|
||||||
|
Result.push_back(*ResultToCheck);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) {
|
void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, const std::optional<std::chrono::high_resolution_clock::duration>& Max) {
|
||||||
@@ -162,10 +201,8 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
ms += 10;
|
ms += 10;
|
||||||
if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) {
|
if (Max.has_value() && std::chrono::milliseconds(ms) > Max.value()) {
|
||||||
beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms).");
|
beammp_trace("'" + Result->Function + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms)");
|
||||||
Cancelled = true;
|
Cancelled = true;
|
||||||
} else if (ms > 1000 * 60) {
|
|
||||||
beammp_lua_warn("'" + Result->Function + "' in '" + Result->StateId + "' is taking very long. The event it's handling is too important to discard the result of this handler, but may block this event and possibly the whole lua state.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Cancelled) {
|
if (Cancelled) {
|
||||||
@@ -183,8 +220,7 @@ void TLuaEngine::WaitForAll(std::vector<std::shared_ptr<TLuaResult>>& Results, c
|
|||||||
void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) {
|
void TLuaEngine::ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results) {
|
||||||
std::unique_lock Lock2(mResultsToCheckMutex);
|
std::unique_lock Lock2(mResultsToCheckMutex);
|
||||||
for (const auto& Result : Results) {
|
for (const auto& Result : Results) {
|
||||||
mResultsToCheck.push_back(Result);
|
mResultsToCheck.push(Result);
|
||||||
mResultsToCheckCond.notify_one();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,27 +548,11 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
|
|||||||
return Lua_GetPlayerIdentifiers(ID);
|
return Lua_GetPlayerIdentifiers(ID);
|
||||||
});
|
});
|
||||||
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
MPTable.set_function("Sleep", &LuaAPI::MP::Sleep);
|
||||||
// const std::string& EventName, size_t IntervalMS, int strategy
|
MPTable.set_function("CreateEventTimer", [&](const std::string& EventName, size_t IntervalMS) {
|
||||||
MPTable.set_function("CreateEventTimer", [&](sol::variadic_args Args) {
|
|
||||||
if (Args.size() < 2 || Args.size() > 3) {
|
|
||||||
beammp_lua_error("CreateEventTimer expects 2 or 3 arguments.");
|
|
||||||
}
|
|
||||||
if (Args.get_type(0) != sol::type::string) {
|
|
||||||
beammp_lua_error("CreateEventTimer expects 1st argument to be a string");
|
|
||||||
}
|
|
||||||
if (Args.get_type(1) != sol::type::number) {
|
|
||||||
beammp_lua_error("CreateEventTimer expects 2nd argument to be a number");
|
|
||||||
}
|
|
||||||
if (Args.size() == 3 && Args.get_type(2) != sol::type::number) {
|
|
||||||
beammp_lua_error("CreateEventTimer expects 3rd argument to be a number (MP.CallStrategy)");
|
|
||||||
}
|
|
||||||
auto EventName = Args.get<std::string>(0);
|
|
||||||
auto IntervalMS = Args.get<size_t>(1);
|
|
||||||
CallStrategy Strategy = Args.size() > 2 ? Args.get<CallStrategy>(2) : CallStrategy::BestEffort;
|
|
||||||
if (IntervalMS < 25) {
|
if (IntervalMS < 25) {
|
||||||
beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly.");
|
beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly.");
|
||||||
}
|
}
|
||||||
mEngine->CreateEventTimer(EventName, mStateId, IntervalMS, Strategy);
|
mEngine->CreateEventTimer(EventName, mStateId, IntervalMS);
|
||||||
});
|
});
|
||||||
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
|
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
|
||||||
mEngine->CancelEventTimers(EventName, mStateId);
|
mEngine->CancelEventTimers(EventName, mStateId);
|
||||||
@@ -552,10 +572,6 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi
|
|||||||
"Name", 5,
|
"Name", 5,
|
||||||
"Description", 6);
|
"Description", 6);
|
||||||
|
|
||||||
MPTable.create_named("CallStrategy",
|
|
||||||
"BestEffort", CallStrategy::BestEffort,
|
|
||||||
"Precise", CallStrategy::Precise);
|
|
||||||
|
|
||||||
auto FSTable = StateView.create_named_table("FS");
|
auto FSTable = StateView.create_named_table("FS");
|
||||||
FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory);
|
FSTable.set_function("CreateDirectory", &LuaAPI::FS::CreateDirectory);
|
||||||
FSTable.set_function("Exists", &LuaAPI::FS::Exists);
|
FSTable.set_function("Exists", &LuaAPI::FS::Exists);
|
||||||
@@ -578,34 +594,12 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) {
|
|
||||||
// TODO: Document all this
|
|
||||||
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
|
|
||||||
if (Strategy == CallStrategy::BestEffort) {
|
|
||||||
Iter = std::find_if(mStateFunctionQueue.begin(), mStateFunctionQueue.end(),
|
|
||||||
[&EventName](const QueuedFunction& Element) {
|
|
||||||
return Element.EventName == EventName;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (Iter == mStateFunctionQueue.end()) {
|
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
|
||||||
Result->StateId = mStateId;
|
|
||||||
Result->Function = FunctionName;
|
|
||||||
std::unique_lock Lock(mStateFunctionQueueMutex);
|
|
||||||
mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName });
|
|
||||||
mStateFunctionQueueCond.notify_all();
|
|
||||||
return Result;
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) {
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
Result->Function = FunctionName;
|
Result->Function = FunctionName;
|
||||||
std::unique_lock Lock(mStateFunctionQueueMutex);
|
std::unique_lock Lock(mStateFunctionQueueMutex);
|
||||||
mStateFunctionQueue.push_back({ FunctionName, Result, Args, "" });
|
mStateFunctionQueue.push({ FunctionName, Result, Args });
|
||||||
mStateFunctionQueueCond.notify_all();
|
mStateFunctionQueueCond.notify_all();
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
@@ -668,13 +662,12 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
std::chrono::milliseconds(500),
|
std::chrono::milliseconds(500),
|
||||||
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
[&]() -> bool { return !mStateFunctionQueue.empty(); });
|
||||||
if (NotExpired) {
|
if (NotExpired) {
|
||||||
auto TheQueuedFunction = std::move(mStateFunctionQueue.front());
|
auto FnNameResultPair = std::move(mStateFunctionQueue.front());
|
||||||
mStateFunctionQueue.erase(mStateFunctionQueue.begin());
|
mStateFunctionQueue.pop();
|
||||||
Lock.unlock();
|
Lock.unlock();
|
||||||
auto& FnName = TheQueuedFunction.FunctionName;
|
auto& FnName = std::get<0>(FnNameResultPair);
|
||||||
auto& Result = TheQueuedFunction.Result;
|
auto& Result = std::get<1>(FnNameResultPair);
|
||||||
auto Args = TheQueuedFunction.Args;
|
auto Args = std::get<2>(FnNameResultPair);
|
||||||
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
|
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
sol::state_view StateView(mState);
|
sol::state_view StateView(mState);
|
||||||
auto Fn = StateView[FnName];
|
auto Fn = StateView[FnName];
|
||||||
@@ -722,14 +715,23 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy) {
|
std::queue<std::pair<TLuaChunk, std::shared_ptr<TLuaResult>>> TLuaEngine::StateThreadData::Debug_GetStateExecuteQueue() {
|
||||||
|
std::unique_lock Lock(mStateExecuteQueueMutex);
|
||||||
|
return mStateExecuteQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<std::tuple<std::string, std::shared_ptr<TLuaResult>, std::vector<TLuaArgTypes>>> TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() {
|
||||||
|
std::unique_lock Lock(mStateFunctionQueueMutex);
|
||||||
|
return mStateFunctionQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) {
|
||||||
std::unique_lock Lock(mTimedEventsMutex);
|
std::unique_lock Lock(mTimedEventsMutex);
|
||||||
TimedEvent Event {
|
TimedEvent Event {
|
||||||
std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) },
|
std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) },
|
||||||
std::chrono::high_resolution_clock::now(),
|
std::chrono::high_resolution_clock::now(),
|
||||||
EventName,
|
EventName,
|
||||||
StateId,
|
StateId
|
||||||
Strategy
|
|
||||||
};
|
};
|
||||||
mTimedEvents.push_back(std::move(Event));
|
mTimedEvents.push_back(std::move(Event));
|
||||||
beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval");
|
beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval");
|
||||||
@@ -757,7 +759,6 @@ void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) {
|
|||||||
|
|
||||||
void TLuaResult::WaitUntilReady() {
|
void TLuaResult::WaitUntilReady() {
|
||||||
while (!Ready) {
|
while (!Ready) {
|
||||||
std::this_thread::yield();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -815,7 +816,12 @@ void TPluginMonitor::operator()() {
|
|||||||
auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
auto StateID = mEngine.GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
||||||
auto Res = mEngine.EnqueueScript(StateID, Chunk);
|
auto Res = mEngine.EnqueueScript(StateID, Chunk);
|
||||||
// TODO: call onInit
|
// TODO: call onInit
|
||||||
mEngine.AddResultToCheck(Res);
|
while (!Res->Ready) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
}
|
||||||
|
if (Res->Error) {
|
||||||
|
beammp_lua_error(Res->ErrorMessage);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: trigger onFileChanged event
|
// TODO: trigger onFileChanged event
|
||||||
beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet");
|
beammp_trace("Change detected in file \"" + Pair.first + "\", event trigger not implemented yet");
|
||||||
|
|||||||
+9
-29
@@ -154,19 +154,6 @@ void TNetwork::TCPServerMain() {
|
|||||||
beammp_warn(("Got an invalid client socket on connect! Skipping..."));
|
beammp_warn(("Got an invalid client socket on connect! Skipping..."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// set timeout (DWORD, aka uint32_t)
|
|
||||||
uint32_t SendTimeoutMS = 30 * 1000;
|
|
||||||
#if defined(BEAMMP_WINDOWS)
|
|
||||||
int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&SendTimeoutMS), sizeof(SendTimeoutMS));
|
|
||||||
#else // POSIX
|
|
||||||
struct timeval optval;
|
|
||||||
optval.tv_sec = (int)(SendTimeoutMS / 1000);
|
|
||||||
optval.tv_usec = (SendTimeoutMS % 1000) * 1000;
|
|
||||||
int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<void*>(&optval), sizeof(optval));
|
|
||||||
#endif
|
|
||||||
if (ret < 0) {
|
|
||||||
throw std::runtime_error("setsockopt recv timeout: " + GetPlatformAgnosticErrorString());
|
|
||||||
}
|
|
||||||
std::thread ID(&TNetwork::Identify, this, client);
|
std::thread ID(&TNetwork::Identify, this, client);
|
||||||
ID.detach(); // TODO: Add to a queue and attempt to join periodically
|
ID.detach(); // TODO: Add to a queue and attempt to join periodically
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
@@ -200,11 +187,7 @@ void TNetwork::Identify(const TConnection& client) {
|
|||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(client.Socket);
|
HandleDownload(client.Socket);
|
||||||
} else if (Code == 'P') {
|
} else if (Code == 'P') {
|
||||||
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
|
||||||
send(client.Socket, "P", 1, MSG_NOSIGNAL);
|
|
||||||
#else
|
|
||||||
send(client.Socket, "P", 1, 0);
|
send(client.Socket, "P", 1, 0);
|
||||||
#endif
|
|
||||||
CloseSocketProper(client.Socket);
|
CloseSocketProper(client.Socket);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -527,7 +510,6 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
||||||
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
||||||
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
||||||
@@ -756,11 +738,11 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) {
|
void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name) {
|
||||||
std::ifstream f(Name.c_str(), std::ios::binary);
|
std::ifstream f(Name.c_str(), std::ios::binary);
|
||||||
uint32_t Split = 0x7735940; // 125MB
|
uint32_t Split = 0x7735940; // 125MB
|
||||||
std::vector<char> Data;
|
char* Data;
|
||||||
if (Size > Split)
|
if (Size > Split)
|
||||||
Data.resize(Split);
|
Data = new char[Split];
|
||||||
else
|
else
|
||||||
Data.resize(Size);
|
Data = new char[Size];
|
||||||
SOCKET TCPSock;
|
SOCKET TCPSock;
|
||||||
if (D)
|
if (D)
|
||||||
TCPSock = c.GetDownSock();
|
TCPSock = c.GetDownSock();
|
||||||
@@ -771,8 +753,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
size_t Diff = Size - Sent;
|
size_t Diff = Size - Sent;
|
||||||
if (Diff > Split) {
|
if (Diff > Split) {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(Data.data(), Split);
|
f.read(Data, Split);
|
||||||
if (!TCPSendRaw(c, TCPSock, Data.data(), Split)) {
|
if (!TCPSendRaw(c, TCPSock, Data, Split)) {
|
||||||
if (c.GetStatus() > -1)
|
if (c.GetStatus() > -1)
|
||||||
c.SetStatus(-1);
|
c.SetStatus(-1);
|
||||||
break;
|
break;
|
||||||
@@ -780,8 +762,8 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
Sent += Split;
|
Sent += Split;
|
||||||
} else {
|
} else {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(Data.data(), Diff);
|
f.read(Data, Diff);
|
||||||
if (!TCPSendRaw(c, TCPSock, Data.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;
|
||||||
@@ -789,16 +771,14 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
Sent += Diff;
|
Sent += Diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] Data;
|
||||||
|
f.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
||||||
intmax_t Sent = 0;
|
intmax_t Sent = 0;
|
||||||
do {
|
do {
|
||||||
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
|
||||||
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), MSG_NOSIGNAL);
|
|
||||||
#else
|
|
||||||
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
|
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
|
||||||
#endif
|
|
||||||
if (Temp < 1) {
|
if (Temp < 1) {
|
||||||
beammp_info("Socket Closed! " + std::to_string(socket));
|
beammp_info("Socket Closed! " + std::to_string(socket));
|
||||||
CloseSocketProper(socket);
|
CloseSocketProper(socket);
|
||||||
|
|||||||
+2
-2
@@ -21,8 +21,8 @@ TPPSMonitor::TPPSMonitor(TServer& Server)
|
|||||||
void TPPSMonitor::operator()() {
|
void TPPSMonitor::operator()() {
|
||||||
RegisterThread("PPSMonitor");
|
RegisterThread("PPSMonitor");
|
||||||
while (!mNetwork) {
|
while (!mNetwork) {
|
||||||
// hard(-ish) spin
|
// hard spi
|
||||||
std::this_thread::yield();
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
beammp_debug("PPSMonitor starting");
|
beammp_debug("PPSMonitor starting");
|
||||||
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
|
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
|
||||||
|
|||||||
+15
-13
@@ -7,14 +7,14 @@
|
|||||||
#include <any>
|
#include <any>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
|
|
||||||
#undef GetObject // Fixes Windows
|
#undef GetObject // Fixes Windows
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
|
namespace json = rapidjson;
|
||||||
|
|
||||||
TServer::TServer(const std::vector<std::string_view>& Arguments) {
|
TServer::TServer(const std::vector<std::string_view>& Arguments) {
|
||||||
beammp_info("BeamMP Server v" + Application::ServerVersionString());
|
beammp_info("BeamMP Server v" + Application::ServerVersionString());
|
||||||
Application::SetSubsystemStatus("Server", Application::Status::Starting);
|
Application::SetSubsystemStatus("Server", Application::Status::Starting);
|
||||||
@@ -171,25 +171,27 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
|
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
|
||||||
try {
|
rapidjson::Document Car;
|
||||||
auto Car = nlohmann::json::parse(CarJson);
|
Car.Parse(CarJson.c_str(), CarJson.size());
|
||||||
const std::string jbm = "jbm";
|
if (Car.HasParseError()) {
|
||||||
if (Car.contains(jbm) && Car[jbm].is_string() && Car[jbm] == "unicycle") {
|
beammp_error("Failed to parse vehicle data -> " + CarJson);
|
||||||
|
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
|
||||||
beammp_error("Failed to parse vehicle data as json for client " + std::to_string(c.GetID()) + ": '" + CarJson + "'");
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
||||||
if (IsUnicycle(c, CarJson) && c.GetUnicycleID() < 0) {
|
|
||||||
|
if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUnicycle(c, CarJson)) {
|
||||||
c.SetUnicycleID(ID);
|
c.SetUnicycleID(ID);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return c.GetCarCount() < Application::Settings.MaxCars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Application::Settings.MaxCars > c.GetCarCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) {
|
void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network) {
|
||||||
|
|||||||
+5
-15
@@ -78,6 +78,10 @@ int main(int argc, char** argv) {
|
|||||||
int BeamMPServerMain(MainArguments Arguments) {
|
int BeamMPServerMain(MainArguments Arguments) {
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
Application::InitializeConsole();
|
Application::InitializeConsole();
|
||||||
|
Application::SetSubsystemStatus("Main", Application::Status::Starting);
|
||||||
|
|
||||||
|
SetupSignalHandlers();
|
||||||
|
|
||||||
ArgsParser Parser;
|
ArgsParser Parser;
|
||||||
Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
|
Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
|
||||||
Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
|
Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
|
||||||
@@ -118,21 +122,14 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::SetSubsystemStatus("Main", Application::Status::Starting);
|
|
||||||
|
|
||||||
Application::Console().StartLoggingToFile();
|
|
||||||
|
|
||||||
SetupSignalHandlers();
|
|
||||||
|
|
||||||
bool Shutdown = false;
|
bool Shutdown = false;
|
||||||
Application::RegisterShutdownHandler([&Shutdown] {
|
Application::RegisterShutdownHandler([&Shutdown] {
|
||||||
beammp_info("If this takes too long, you can press Ctrl+C repeatedly to force a shutdown.");
|
|
||||||
Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown);
|
Application::SetSubsystemStatus("Main", Application::Status::ShuttingDown);
|
||||||
Shutdown = true;
|
Shutdown = true;
|
||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([] {
|
Application::RegisterShutdownHandler([] {
|
||||||
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", "");
|
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onShutdown", "");
|
||||||
TLuaEngine::WaitForAll(Futures, std::chrono::seconds(5));
|
TLuaEngine::WaitForAll(Futures);
|
||||||
});
|
});
|
||||||
|
|
||||||
TServer Server(Arguments.List);
|
TServer Server(Arguments.List);
|
||||||
@@ -171,10 +168,6 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
||||||
RegisterThread("Main(Waiting)");
|
RegisterThread("Main(Waiting)");
|
||||||
|
|
||||||
std::set<std::string> IgnoreSubsystems {
|
|
||||||
"UpdateCheck" // Ignore as not to confuse users (non-vital system)
|
|
||||||
};
|
|
||||||
|
|
||||||
bool FullyStarted = false;
|
bool FullyStarted = false;
|
||||||
while (!Shutdown) {
|
while (!Shutdown) {
|
||||||
if (!FullyStarted) {
|
if (!FullyStarted) {
|
||||||
@@ -183,9 +176,6 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
std::string SystemsBadList {};
|
std::string SystemsBadList {};
|
||||||
auto Statuses = Application::GetSubsystemStatuses();
|
auto Statuses = Application::GetSubsystemStatuses();
|
||||||
for (const auto& NameStatusPair : Statuses) {
|
for (const auto& NameStatusPair : Statuses) {
|
||||||
if (IgnoreSubsystems.count(NameStatusPair.first) > 0) {
|
|
||||||
continue; // ignore
|
|
||||||
}
|
|
||||||
if (NameStatusPair.second == Application::Status::Starting) {
|
if (NameStatusPair.second == Application::Status::Starting) {
|
||||||
FullyStarted = false;
|
FullyStarted = false;
|
||||||
} else if (NameStatusPair.second == Application::Status::Bad) {
|
} else if (NameStatusPair.second == Application::Status::Bad) {
|
||||||
|
|||||||
Reference in New Issue
Block a user