#pragma once #include "TNetwork.h" #include "TServer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOL_ALL_SAFETIES_ON 1 #define SOL_USER_C_ASSERT SOL_ON #define SOL_C_ASSERT(...) \ beammp_lua_errorf("SOL2 assertion failure: Assertion `{}` failed in {}:{}. This *should* be a fatal error, but BeamMP Server overrides it to not be fatal. This may cause the Lua Engine to crash, or cause other issues.", #__VA_ARGS__, __FILE__, __LINE__) #include using TLuaStateId = std::string; namespace fs = std::filesystem; /** * std::variant means, that TLuaArgTypes may be one of the Types listed as template args */ using TLuaArgTypes = std::variant>; static constexpr size_t TLuaArgTypes_String = 0; static constexpr size_t TLuaArgTypes_Int = 1; static constexpr size_t TLuaArgTypes_VariadicArgs = 2; static constexpr size_t TLuaArgTypes_Bool = 3; static constexpr size_t TLuaArgTypes_StringStringMap = 4; class TLuaPlugin; struct TLuaResult { bool Ready; bool Error; std::string ErrorMessage; sol::object Result { sol::lua_nil }; TLuaStateId StateId; std::string Function; std::shared_ptr ReadyMutex { std::make_shared() }; std::shared_ptr ReadyCondition { std::make_shared() }; void MarkAsReady(); void WaitUntilReady(); }; struct TLuaPluginConfig { static inline const std::string FileName = "PluginConfig.toml"; TLuaStateId StateId; // TODO: Add execute list // TODO: Build a better toml serializer, or some way to do this in an easier way }; struct TLuaChunk { TLuaChunk(std::shared_ptr Content, std::string FileName, std::string PluginPath); std::shared_ptr Content; std::string FileName; std::string PluginPath; }; class TLuaEngine : public std::enable_shared_from_this, IThreaded { public: enum CallStrategy : int { BestEffort, Precise, }; struct QueuedFunction { std::string FunctionName; std::shared_ptr Result; std::vector Args; std::string EventName; // optional, may be empty }; TLuaEngine(); virtual ~TLuaEngine() noexcept { beammp_debug("Lua Engine terminated"); } void operator()() override; TNetwork& Network() { return *mNetwork; } TServer& Server() { return *mServer; } void SetNetwork(TNetwork* Network) { mNetwork = Network; } void SetServer(TServer* Server) { mServer = Server; } size_t GetResultsToCheckSize() { std::unique_lock Lock(mResultsToCheckMutex); return mResultsToCheck.size(); } size_t GetLuaStateCount() { std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); } std::vector GetLuaStateNames() { std::vector names {}; for (auto const& [stateId, _] : mLuaStates) { names.push_back(stateId); } return names; } size_t GetTimedEventsCount() { std::unique_lock Lock(mTimedEventsMutex); return mTimedEvents.size(); } size_t GetRegisteredEventHandlerCount() { std::unique_lock Lock(mLuaEventsMutex); size_t LuaEventsCount = 0; for (const auto& State : mLuaEvents) { for (const auto& Events : State.second) { LuaEventsCount += Events.second.size(); } } return LuaEventsCount - GetLuaStateCount(); } static void WaitForAll(std::vector>& Results, const std::optional& Max = std::nullopt); void ReportErrors(const std::vector>& Results); bool HasState(TLuaStateId StateId); [[nodiscard]] std::shared_ptr EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script); [[nodiscard]] std::shared_ptr EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args); void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false); void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); /** * * @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means * @param EventName Name of the event * @param IgnoreId * @param Args * @return */ template [[nodiscard]] std::vector> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) { std::unique_lock Lock(mLuaEventsMutex); beammp_event(EventName); if (mLuaEvents.find(EventName) == mLuaEvents.end()) { // if no event handler is defined for 'EventName', return immediately return {}; } std::vector> Results; std::vector Arguments { TLuaArgTypes { std::forward(Args) }... }; for (const auto& Event : mLuaEvents.at(EventName)) { for (const auto& Function : Event.second) { if (Event.first != IgnoreId) { Results.push_back(EnqueueFunctionCall(Event.first, Function, Arguments)); } } } return Results; // } template [[nodiscard]] std::vector> TriggerLocalEvent(const TLuaStateId& StateId, const std::string& EventName, ArgsT&&... Args) { std::unique_lock Lock(mLuaEventsMutex); beammp_event(EventName + " in '" + StateId + "'"); if (mLuaEvents.find(EventName) == mLuaEvents.end()) { // if no event handler is defined for 'EventName', return immediately return {}; } std::vector> Results; std::vector Arguments { TLuaArgTypes { std::forward(Args) }... }; const auto Handlers = GetEventHandlersForState(EventName, StateId); for (const auto& Handler : Handlers) { Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments)); } return Results; } std::set GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy); void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); sol::state_view GetStateForPlugin(const fs::path& PluginPath); TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath); void AddResultToCheck(const std::shared_ptr& Result); static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; std::vector GetStateGlobalKeysForState(TLuaStateId StateId); std::vector GetStateTableKeysForState(TLuaStateId StateId, std::vector keys); // Debugging functions (slow) std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); std::vector Debug_GetStateFunctionQueueForState(TLuaStateId StateId); std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); private: void CollectAndInitPlugins(); void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config); void FindAndParseConfig(const fs::path& Folder, TLuaPluginConfig& Config); size_t CalculateMemoryUsage(); class StateThreadData : IThreaded { public: StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine); StateThreadData(const StateThreadData&) = delete; virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } [[nodiscard]] std::shared_ptr EnqueueScript(const TLuaChunk& Script); [[nodiscard]] std::shared_ptr EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args); [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); void RegisterEvent(const std::string& EventName, const std::string& FunctionName); void AddPath(const fs::path& Path); // to be added to path and cpath void operator()() override; sol::state_view State() { return sol::state_view(mState); } std::vector GetStateGlobalKeys(); std::vector GetStateTableKeys(const std::vector& keys); // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); std::vector Debug_GetStateFunctionQueue(); private: 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_GetPlayerIdentifiers(int ID); sol::table Lua_GetPlayers(); std::string Lua_GetPlayerName(int ID); sol::table Lua_GetPlayerVehicles(int ID); std::pair Lua_GetPositionRaw(int PID, int VID); sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port); sol::table Lua_JsonDecode(const std::string& str); int Lua_GetPlayerIDByName(const std::string& Name); sol::table Lua_FS_ListFiles(const std::string& Path); sol::table Lua_FS_ListDirectories(const std::string& Path); std::string mName; TLuaStateId mStateId; lua_State* mState; std::thread mThread; std::queue>> mStateExecuteQueue; std::recursive_mutex mStateExecuteQueueMutex; std::vector mStateFunctionQueue; std::mutex mStateFunctionQueueMutex; std::condition_variable mStateFunctionQueueCond; TLuaEngine* mEngine; sol::state_view mStateView { mState }; std::queue mPaths; std::recursive_mutex mPathsMutex; std::mt19937 mMersenneTwister; std::uniform_real_distribution mUniformRealDistribution01; }; struct TimedEvent { std::chrono::high_resolution_clock::duration Duration {}; std::chrono::high_resolution_clock::time_point LastCompletion {}; std::string EventName; TLuaStateId StateId; CallStrategy Strategy; bool Expired(); void Reset(); }; TNetwork* mNetwork; TServer* mServer; const fs::path mResourceServerPath; std::vector> mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; std::unordered_map>> mLuaEvents; std::recursive_mutex mLuaEventsMutex; std::vector mTimedEvents; std::recursive_mutex mTimedEventsMutex; std::list> mResultsToCheck; std::mutex mResultsToCheckMutex; std::condition_variable mResultsToCheckCond; }; // std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr arg, bool Wait);