From ba0678dade63c24846bb0e3310b1ef142b6decaf Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Thu, 16 Sep 2021 03:21:00 +0200 Subject: [PATCH] Continue Lua Rewrite --- CMakeLists.txt | 9 ++-- include/CustomAssert.h | 6 +-- include/TConfig.h | 2 + include/TConsole.h | 9 ++-- include/TLuaEngine.h | 57 ++++++++++++++++++-- include/TLuaPlugin.h | 15 ++++++ src/TConsole.cpp | 19 +++---- src/TLuaEngine.cpp | 116 +++++++++++++++++++++++++++++++++++++---- src/TLuaPlugin.cpp | 10 ++++ src/TNetwork.cpp | 7 +-- src/TServer.cpp | 2 +- src/main.cpp | 2 +- 12 files changed, 215 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d3defc..8523a78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,16 +52,19 @@ target_include_directories(BeamMP-Server PRIVATE "${PROJECT_SOURCE_DIR}/deps/com target_include_directories(BeamMP-Server PRIVATE "${PROJECT_SOURCE_DIR}/deps/sol2/include") target_include_directories(BeamMP-Server PRIVATE "${PROJECT_SOURCE_DIR}/deps") -include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include) +find_package(Lua REQUIRED) find_package(OpenSSL REQUIRED) +target_link_libraries(BeamMP-Server sol2::sol2 ${LUA_LIBRARIES}) + if (UNIX) - target_link_libraries(BeamMP-Server z pthread stdc++fs ${SOL2_LIBRARIES} crypto ${OPENSSL_LIBRARIES} commandline sioclient_tls) + target_link_libraries(BeamMP-Server z pthread stdc++fs crypto ${OPENSSL_LIBRARIES} commandline sioclient_tls) elseif (WIN32) include(FindLua) find_package(ZLIB REQUIRED) find_package(RapidJSON CONFIG REQUIRED) target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) - target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${SOL2_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient_tls) + target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${OPENSSL_LIBRARIES} commandline sioclient_tls) endif () diff --git a/include/CustomAssert.h b/include/CustomAssert.h index cf1c496..78ff850 100644 --- a/include/CustomAssert.h +++ b/include/CustomAssert.h @@ -56,13 +56,13 @@ inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const ch } #define beammp_assert(cond) _assert(__FILE__, __func__, __LINE__, #cond, (cond)) -#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false) +#define beammp_assert_not_reachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false) #else // In release build, these macros turn into NOPs. The compiler will optimize these out. -#define Assert(x) \ +#define beammp_assert(x) \ do { \ } while (false) -#define AssertNotReachable() \ +#define beammp_assert_not_reachable() \ do { \ } while (false) #endif // DEBUG diff --git a/include/TConfig.h b/include/TConfig.h index 60001bd..1399aa6 100644 --- a/include/TConfig.h +++ b/include/TConfig.h @@ -4,6 +4,8 @@ #include +namespace fs = std::filesystem; + class TConfig { public: explicit TConfig(); diff --git a/include/TConsole.h b/include/TConsole.h index 7063d5d..edbfdc8 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -1,8 +1,10 @@ #pragma once +#include "commandline.h" #include #include -#include "commandline.h" + +class TLuaEngine; class TConsole { public: @@ -10,9 +12,10 @@ public: void Write(const std::string& str); void WriteRaw(const std::string& str); - // BROKEN void InitializeLuaConsole(TLuaEngine& Engine); + void InitializeLuaConsole(TLuaEngine& Engine); private: -// BROKEN std::unique_ptr mLuaConsole { nullptr }; Commandline mCommandline; + TLuaEngine& mLuaEngine; + const std::string mStateId = "BEAMMP_SERVER_CONSOLE"; }; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 770d949..82c33de 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -3,30 +3,77 @@ #include "TNetwork.h" #include "TServer.h" #include -#include +#include +#include +#include #include #include #include +#define SOL_ALL_SAFETIES_ON 1 +#include + +using TLuaStateId = std::string; namespace fs = std::filesystem; class TLuaPlugin; +struct TLuaResult { + std::atomic_bool Ready; + // TODO: Add condition_variable + sol::protected_function_result Result; +}; + +struct TLuaPluginConfig { + static inline const std::string FileName = "PluginConfig.toml"; + TLuaStateId StateId; + // TODO: Execute list +}; + class TLuaEngine : IThreaded { public: TLuaEngine(TServer& Server, TNetwork& Network); void operator()() override; + TLuaResult EnqueueScript(TLuaStateId StateID, const std::shared_ptr& Script); + void EnsureStateExists(TLuaStateId StateId, const std::string& Name); + private: void CollectPlugins(); - void InitializePlugin(const fs::path& folder); + void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config); + void FindAndParseConfig(const fs::path& Folder, TLuaPluginConfig& Config); + + class StateThreadData : IThreaded { + public: + StateThreadData(const std::string& Name, std::atomic_bool& Shutdown); + StateThreadData(const StateThreadData&) = delete; + void EnqueueScript(const std::shared_ptr& Script); + void operator()() override; + ~StateThreadData(); + + private: + std::string mName; + std::atomic_bool& mShutdown; + sol::state mState; + std::thread mThread; + std::queue> mStateExecuteQueue; + std::mutex mStateExecuteQueueMutex; + }; TNetwork& mNetwork; TServer& mServer; - sol::state mL; std::atomic_bool mShutdown { false }; fs::path mResourceServerPath; - std::vector mLuaPlugins; - std::unordered_map mLuaStates; + std::vector mLuaPlugins; + std::unordered_map> mLuaStates; + std::mutex mLuaStatesMutex; }; + +#include +// DEAD CODE +struct TLuaArg { + std::vector args; + void PushArgs(lua_State* State); +}; +std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr arg, bool Wait); diff --git a/include/TLuaPlugin.h b/include/TLuaPlugin.h index e69de29..c3ffc2f 100644 --- a/include/TLuaPlugin.h +++ b/include/TLuaPlugin.h @@ -0,0 +1,15 @@ +#include "TLuaEngine.h" + +class TLuaPlugin { +public: + TLuaPlugin(TLuaEngine& Engine, const TLuaPluginConfig& Config); + TLuaPlugin(const TLuaPlugin&) = delete; + TLuaPlugin& operator=(const TLuaPlugin&) = delete; + ~TLuaPlugin() noexcept = default; + + const TLuaPluginConfig& GetConfig() const { return mConfig; } + +private: + TLuaPluginConfig mConfig; + TLuaEngine& mEngine; +}; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 1f2a80c..1d7b0c6 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -2,6 +2,8 @@ #include "Common.h" #include "Compat.h" +#include "TLuaEngine.h" + #include #include @@ -55,13 +57,8 @@ TConsole::TConsole() { Application::GracefullyShutdown(); } else if (cmd == "clear" || cmd == "cls") { // TODO: clear screen + mLuaEngine.EnqueueScript(mStateId, std::make_shared(cmd)); } else { - /*if (mLuaConsole) { - mLuaConsole->Execute(cmd); - } else { - error("Lua subsystem not yet initialized, please wait a few seconds and try again"); - } BROKEN - */ } }; } @@ -71,11 +68,11 @@ void TConsole::Write(const std::string& str) { mCommandline.write(ToWrite); // TODO write to logfile, too } -/* BROKEN -void TConsole::InitializeLuaConsole(TLuaEngine& Engine) { - mLuaConsole = std::make_unique(Engine, true); -} -*/ + void TConsole::WriteRaw(const std::string& str) { mCommandline.write(str); } + +void TConsole::InitializeLuaConsole(TLuaEngine& Engine) { + Engine.EnsureStateExists(mStateId, "<>"); +} diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index f8438b4..a25cc41 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1,5 +1,16 @@ #include "TLuaEngine.h" #include "CustomAssert.h" +#include "TLuaPlugin.h" + +#include +#include + +static std::mt19937_64 MTGen64; + +static TLuaStateId GenerateUniqueStateId() { + auto Time = std::chrono::high_resolution_clock::now().time_since_epoch(); + return std::to_string(MTGen64()) + std::to_string(std::chrono::duration_cast(Time).count()); +} TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network) : mNetwork(Network) @@ -18,27 +29,114 @@ TLuaEngine::TLuaEngine(TServer& Server, TNetwork& Network) mThread.join(); } }); + Start(); } void TLuaEngine::operator()() { RegisterThread("LuaEngine"); // lua engine main thread CollectPlugins(); + while (!mShutdown) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} + +void TLuaEngine::EnqueueScript(TLuaStateId StateID, const std::shared_ptr& Script) { + std::unique_lock Lock(mLuaStatesMutex); + mLuaStates.at(StateID)->EnqueueScript(Script); } void TLuaEngine::CollectPlugins() { - for (const auto& dir : fs::directory_iterator(mResourceServerPath)) { - auto path = dir.path(); - path = fs::relative(path); - if (!dir.is_directory()) { - beammp_error("\"" + dir.path().string() + "\" is not a directory, skipping"); + for (const auto& Dir : fs::directory_iterator(mResourceServerPath)) { + auto Path = Dir.path(); + Path = fs::relative(Path); + if (!Dir.is_directory()) { + beammp_error("\"" + Dir.path().string() + "\" is not a directory, skipping"); } else { - beammp_debug("found plugin directory: " + path.string()); + beammp_debug("found plugin directory: " + Path.string()); + TLuaPluginConfig Config { GenerateUniqueStateId() }; + FindAndParseConfig(Path, Config); + InitializePlugin(Path, Config); } } } -void TLuaEngine::InitializePlugin(const fs::path& folder) { - beammp_assert(fs::exists(folder)); - beammp_assert(fs::is_directory(folder)); +void TLuaEngine::InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config) { + beammp_assert(fs::exists(Folder)); + beammp_assert(fs::is_directory(Folder)); + TLuaPlugin Plugin(*this, Config); + std::unique_lock Lock(mLuaStatesMutex); + EnsureStateExists(Config.StateId, Folder.stem().string()); +} + +void TLuaEngine::FindAndParseConfig(const fs::path& Folder, TLuaPluginConfig& Config) { + auto ConfigFile = Folder / TLuaPluginConfig::FileName; + if (fs::exists(ConfigFile) && fs::is_regular_file(ConfigFile)) { + beammp_debug("\"" + ConfigFile.string() + "\" found"); + // TODO use toml11 here to parse it + try { + auto Data = toml::parse(ConfigFile); + if (Data.contains("LuaStateID")) { + auto ID = toml::find(Data, "LuaStateID"); + if (!ID.empty()) { + beammp_debug("Plugin \"" + Folder.string() + "\" specified it wants LuaStateID \"" + ID + "\""); + Config.StateId = ID; + } else { + beammp_debug("LuaStateID empty, using randomized state ID"); + } + } + } catch (const std::exception& e) { + beammp_error(Folder.string() + ": " + e.what()); + } + } +} + +void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name) { + if (mLuaStates.find(StateId) == mLuaStates.end()) { + beammp_debug("Creating lua state for state id \"" + StateId + "\""); + auto DataPtr = std::make_unique(Name, mShutdown); + mLuaStates[StateId] = std::move(DataPtr); + } +} + +TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown) + : mName(Name) + , mShutdown(Shutdown) { + mState.open_libraries(sol::lib::base); + Start(); +} + +void TLuaEngine::StateThreadData::EnqueueScript(const std::shared_ptr& Script) { + beammp_debug("enqueuing script into \"" + mName + "\""); + std::unique_lock Lock(mStateExecuteQueueMutex); + mStateExecuteQueue.push(Script); +} + +void TLuaEngine::StateThreadData::operator()() { + RegisterThreadAuto(); + while (!mShutdown) { + std::unique_lock Lock(mStateExecuteQueueMutex); + if (mStateExecuteQueue.empty()) { + Lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } else { + auto S = mStateExecuteQueue.front(); + mStateExecuteQueue.pop(); + Lock.unlock(); + mState.do_string(*S); + } + } +} + +TLuaEngine::StateThreadData::~StateThreadData() { + if (mThread.joinable()) { + mThread.join(); + } +} + +// AHHH +std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr arg, bool Wait) { +} + +void TLuaArg::PushArgs(lua_State* State) { } diff --git a/src/TLuaPlugin.cpp b/src/TLuaPlugin.cpp index e69de29..5567335 100644 --- a/src/TLuaPlugin.cpp +++ b/src/TLuaPlugin.cpp @@ -0,0 +1,10 @@ +#include "TLuaPlugin.h" +#include +#include +#include +#include + +TLuaPlugin::TLuaPlugin(TLuaEngine& Engine, const TLuaPluginConfig& Config) + : mConfig(Config) + , mEngine(Engine) { +} diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 016deac..c76ae9c 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -2,6 +2,7 @@ #include "Client.h" #include #include +#include #include #include @@ -43,7 +44,7 @@ void TNetwork::UDPServerMain() { #ifdef WIN32 WSADATA data; if (WSAStartup(514, &data)) { - error(("Can't start Winsock!")); + beammp_error(("Can't start Winsock!")); //return; } #endif // WIN32 @@ -104,7 +105,7 @@ void TNetwork::TCPServerMain() { #ifdef WIN32 WSADATA wsaData; if (WSAStartup(514, &wsaData)) { - error("Can't start Winsock!"); + beammp_error("Can't start Winsock!"); return; } #endif // WIN32 @@ -621,7 +622,7 @@ void TNetwork::SyncResources(TClient& c) { } #ifndef DEBUG } catch (std::exception& e) { - error("Exception! : " + std::string(e.what())); + beammp_error("Exception! : " + std::string(e.what())); c.SetStatus(-1); } #endif diff --git a/src/TServer.cpp b/src/TServer.cpp index 7264207..38b5d9e 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -3,7 +3,7 @@ #include "Common.h" #include "TNetwork.h" #include "TPPSMonitor.h" -#include +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index 03bbbc0..c57ec08 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,7 +66,7 @@ int main(int argc, char** argv) { TNetwork Network(Server, PPSMonitor, ResourceManager); TLuaEngine LuaEngine(Server, Network); PPSMonitor.SetNetwork(Network); - Application::Console().InitializeLuaConsole(LuaEngine); + // BROKEN Application::Console().InitializeLuaConsole(LuaEngine); // TODO: replace while (!Shutdown) {