diff --git a/include/Common.h b/include/Common.h index abb866df..44bb7dfb 100644 --- a/include/Common.h +++ b/include/Common.h @@ -36,11 +36,12 @@ #include #include +#include namespace fs = std::filesystem; #include "Settings.h" #include "TConsole.h" - +using LuaFunction = std::variant, std::string>; struct Version { uint8_t major; uint8_t minor; @@ -267,7 +268,7 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg); std::vector Comp(std::span input); std::vector DeComp(std::span input); - +std::string GetFunctionName(const LuaFunction& cb); std::string GetPlatformAgnosticErrorString(); #define S_DSN SU_RAW diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 47a5c760..56a17288 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #define SOL_ALL_SAFETIES_ON 1 @@ -76,7 +77,7 @@ struct TLuaResult { std::string ErrorMessage; sol::object Result { sol::lua_nil }; TLuaStateId StateId; - std::string Function; + LuaFunction Function; std::shared_ptr ReadyMutex { std::make_shared() }; @@ -112,7 +113,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { }; struct QueuedFunction { - std::string FunctionName; + LuaFunction FunctionObject; std::shared_ptr Result; std::vector Args; std::string EventName; // optional, may be empty @@ -167,9 +168,9 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { 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, const std::string& EventName); + [[nodiscard]] std::shared_ptr EnqueueFunctionCall(TLuaStateId StateID, const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName); void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false); - void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); + void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const LuaFunction& FunctionObject); /** * * @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means @@ -182,7 +183,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { [[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 + if (!mLuaEvents.contains(EventName)) { // if no event handler is defined for 'EventName', return immediately return {}; } @@ -202,7 +203,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { [[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 + if (!mLuaEvents.contains(EventName)) { // if no event handler is defined for 'EventName', return immediately return {}; } std::vector> Results; @@ -213,11 +214,12 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { } return Results; } - std::set 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 CancelEventTimers(const std::string& EventName, TLuaStateId StateId); sol::state_view GetStateForPlugin(const fs::path& PluginPath); TLuaStateId GetStateIDForPlugin(const fs::path& PluginPath); + void ClearEventsForState(const std::string& StateID); void AddResultToCheck(const std::shared_ptr& Result); static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; @@ -226,9 +228,9 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { std::vector GetStateTableKeysForState(TLuaStateId StateId, std::vector keys); // Debugging functions (slow) - std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); + std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); - std::vector Debug_GetStateFunctionQueueForState(TLuaStateId StateId); + std::deque Debug_GetStateFunctionQueueForState(TLuaStateId StateId); std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); private: @@ -243,9 +245,9 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { 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, const std::string& EventName); - [[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); + [[nodiscard]] std::shared_ptr EnqueueFunctionCall(const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName); + [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); + void RegisterEvent(const std::string& EventName, const LuaFunction& FunctionObject) const; 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); } @@ -255,7 +257,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); - std::vector Debug_GetStateFunctionQueue(); + std::deque Debug_GetStateFunctionQueue(); private: sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs); @@ -281,7 +283,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { std::thread mThread; std::queue>> mStateExecuteQueue; std::recursive_mutex mStateExecuteQueueMutex; - std::vector mStateFunctionQueue; + std::deque mStateFunctionQueue; //This has been changed from vector to deque; it has been benchmarked and improves the queue performance by a lot std::mutex mStateFunctionQueueMutex; std::condition_variable mStateFunctionQueueCond; TLuaEngine* mEngine; @@ -309,7 +311,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { std::vector> mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; - std::unordered_map>> mLuaEvents; + std::unordered_map>> mLuaEvents; std::recursive_mutex mLuaEventsMutex; std::vector mTimedEvents; std::recursive_mutex mTimedEventsMutex; diff --git a/src/Common.cpp b/src/Common.cpp index 1785b9e2..cea5471b 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -35,6 +35,8 @@ #include "CustomAssert.h" #include "Http.h" +#include + void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { std::unique_lock Lock(mShutdownHandlersMutex); if (Handler) { @@ -455,3 +457,10 @@ std::vector Comp(std::span input) { output.resize(output_size); return output; } + +std::string GetFunctionName(const LuaFunction& cb) { + if (const std::string* name = std::get_if(&cb)) { + return *name; + } + return "anonymous"; +} \ No newline at end of file diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index c71ff299..6622023c 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -436,7 +436,11 @@ void LuaAPI::MP::PrintRaw(sol::variadic_args Args) { } int LuaAPI::PanicHandler(lua_State* State) { - beammp_lua_error("PANIC: " + sol::stack::get(State, 1)); + std::optional message = sol::stack::get>(State, -1); + + if (message) { + beammp_lua_error("PANIC: " + *message); + } return 0; } diff --git a/src/TConsole.cpp b/src/TConsole.cpp index fb1dc8b0..51b2eac2 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -732,28 +732,28 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { } else if (cmd == "queued") { auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId); Application::Console().WriteRaw("Pending functions in State '" + mStateId + "'"); - std::unordered_map FunctionsCount; - std::vector FunctionsInOrder; - while (!QueuedFunctions.empty()) { - auto Tuple = QueuedFunctions.front(); - QueuedFunctions.erase(QueuedFunctions.begin()); - FunctionsInOrder.push_back(Tuple.FunctionName); - FunctionsCount[Tuple.FunctionName] += 1; + size_t FunctionsCount = QueuedFunctions.size(); + std::vector FunctionsInOrder; + for (int i = 0; i < FunctionsCount; ++i) { + FunctionsInOrder.push_back(QueuedFunctions[i].FunctionObject); } - std::set 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)"); + std::vector Uniques; + int Index = 0; + for (const LuaFunction Function : FunctionsInOrder) { + if (!(std::find(Uniques.begin(), Uniques.end(), Function) == Uniques.end())) { + std::string FunctionName = GetFunctionName(Function); + Uniques.push_back(Function); + if (Index != 0) { + Application::Console().WriteRaw(" " + FunctionName + " (" + std::to_string(Index) + "x)"); } else { - Application::Console().WriteRaw(" " + Function); + Application::Console().WriteRaw(" " + FunctionName); } } } 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") + ")"); + std::string FunctionName = GetFunctionName(Function.Function);; + Application::Console().WriteRaw(" '" + FunctionName + "' (Ready? " + (Function.Ready ? "Yes" : "No") + ", Error? " + (Function.Error ? "Yes: '" + Function.ErrorMessage + "'" : "No") + ")"); } } else if (cmd == "events") { auto Events = LuaAPI::MP::Engine->Debug_GetEventsForState(mStateId); @@ -761,7 +761,8 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { for (const auto& EventHandlerPair : Events) { Application::Console().WriteRaw(" Event '" + EventHandlerPair.first + "'"); for (const auto& Handler : EventHandlerPair.second) { - Application::Console().WriteRaw(" " + Handler); + std::string FunctionName = GetFunctionName(Handler);; + Application::Console().WriteRaw(" " + FunctionName); } } } else if (cmd == "help") { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 49dd4455..feb3d8d0 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -87,7 +87,7 @@ void TLuaEngine::operator()() { if (Ptr->Ready) { if (Ptr->Error) { if (Ptr->ErrorMessage != BeamMPFnNotFoundError) { - beammp_lua_error(Ptr->Function + ": " + Ptr->ErrorMessage); + beammp_lua_error(GetFunctionName(Ptr->Function) + ": " + Ptr->ErrorMessage); } } return true; @@ -104,15 +104,18 @@ void TLuaEngine::operator()() { while (!Application::IsShuttingDown()) { { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); + for (auto& Timer : mTimedEvents) { if (Timer.Expired()) { auto LastCompletionBeforeReset = Timer.LastCompletion; Timer.Reset(); - auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId); std::unique_lock StateLock(mLuaStatesMutex); + auto& StateData = mLuaStates[Timer.StateId]; + + auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId); std::unique_lock Lock2(mResultsToCheckMutex); for (auto& Handler : Handlers) { - auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCallFromCustomEvent(Handler, {}, Timer.EventName, Timer.Strategy); + auto Res = StateData->EnqueueFunctionCallFromCustomEvent(Handler, {}, Timer.EventName, Timer.Strategy); if (Res) { mResultsToCheck.push_back(Res); mResultsToCheckCond.notify_one(); @@ -179,14 +182,33 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) { return ""; } +void TLuaEngine::ClearEventsForState(const std::string& StateID) { + { + std::unique_lock Lock(mLuaEventsMutex); + for (auto& StateMap : mLuaEvents | std::views::values) { + StateMap.erase(StateID); + } + } + { + std::unique_lock Lock(mTimedEventsMutex); + for (auto it = mTimedEvents.begin(); it != mTimedEvents.end();) { + if (it->StateId == StateID) { + it = mTimedEvents.erase(it); + } else { + ++it; + } + } + } +} + void TLuaEngine::AddResultToCheck(const std::shared_ptr& Result) { std::unique_lock Lock(mResultsToCheckMutex); mResultsToCheck.push_back(Result); mResultsToCheckCond.notify_one(); } -std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { - std::unordered_map> Result; +std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { + std::unordered_map> Result; std::unique_lock Lock(mLuaEventsMutex); for (const auto& EventNameToEventMap : mLuaEvents) { for (const auto& IdSetOfHandlersPair : EventNameToEventMap.second) { @@ -207,10 +229,9 @@ std::queue>> TLuaEngine::Debug_ return Result; } -std::vector TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { - std::vector Result; +std::deque TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { std::unique_lock Lock(mLuaStatesMutex); - Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue(); + std::deque Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue(); return Result; } @@ -309,28 +330,29 @@ void TLuaEngine::WaitForAll(std::vector>& Results, c bool Cancelled = false; size_t ms = 0; std::set WarnedResults; + std::string FunctionName = GetFunctionName(Result->Function); while (!Result->Ready && !Cancelled) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ms += 10; 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("'" + FunctionName + "' in '" + Result->StateId + "' did not finish executing in time (took: " + std::to_string(ms) + "ms)."); Cancelled = true; } else if (ms > 1000 * 60) { - auto ResultId = Result->StateId + "_" + Result->Function; + auto ResultId = Result->StateId + "_" + FunctionName; if (WarnedResults.count(ResultId) == 0) { WarnedResults.insert(ResultId); - 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."); + beammp_lua_warn("'" + FunctionName + "' 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) { - beammp_lua_warn("'" + Result->Function + "' in '" + Result->StateId + "' failed to execute in time and was not waited for. It may still finish executing at a later time."); + beammp_lua_warn("'" + FunctionName + "' in '" + Result->StateId + "' failed to execute in time and was not waited for. It may still finish executing at a later time."); LuaAPI::MP::Engine->ReportErrors({ Result }); } else if (Result->Error) { if (Result->ErrorMessage != BeamMPFnNotFoundError) { - beammp_lua_error(Result->Function + ": " + Result->ErrorMessage); + beammp_lua_error(FunctionName + ": " + Result->ErrorMessage); } } } @@ -355,9 +377,9 @@ std::shared_ptr TLuaEngine::EnqueueScript(TLuaStateId StateID, const return mLuaStates.at(StateID)->EnqueueScript(Script); } -std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args, const std::string& EventName) { +std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName) { std::unique_lock Lock(mLuaStatesMutex); - return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args, EventName); + return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionObject, Args, EventName); } void TLuaEngine::CollectAndInitPlugins() { @@ -428,23 +450,18 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, beammp_debug("Creating lua state for state id \"" + StateId + "\""); auto DataPtr = std::make_unique(Name, StateId, *this); mLuaStates[StateId] = std::move(DataPtr); - RegisterEvent("onInit", StateId, "onInit"); - if (!DontCallOnInit) { - auto Res = EnqueueFunctionCall(StateId, "onInit", {}, "onInit"); - Res->WaitUntilReady(); - if (Res->Error && Res->ErrorMessage != TLuaEngine::BeamMPFnNotFoundError) { - beammp_lua_error("Calling \"onInit\" on \"" + StateId + "\" failed: " + Res->ErrorMessage); - } - } } } -void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName) { +void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const LuaFunction& FunctionObject) { std::unique_lock Lock(mLuaEventsMutex); - mLuaEvents[EventName][StateId].insert(FunctionName); + auto& Events = mLuaEvents[EventName][StateId]; + if (const auto it = std::find(Events.begin(), Events.end(), FunctionObject); it == Events.end()) { + Events.emplace(std::move(FunctionObject)); + } } -std::set TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { +std::set>>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { return mLuaEvents[EventName][StateId]; } @@ -491,7 +508,12 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string sol::variadic_results LocalArgs = JsonStringToArray(Str); for (const auto& Handler : MyHandlers) { - auto Fn = mStateView[Handler]; + auto Fn = sol::function(); + if (std::holds_alternative(Handler)) { + Fn = mStateView[std::get(Handler)]; + }else { + Fn = *std::get>(Handler); + } if (Fn.valid()) { auto LuaResult = Fn(LocalArgs); auto Result = std::make_shared(); @@ -541,12 +563,20 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& // TODO: make asynchronous? sol::table Result = mStateView.create_table(); int i = 1; - for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) { - auto Fn = mStateView[Handler]; + const auto Handlers = mEngine->GetEventHandlersForState(EventName, mStateId); + for (const auto& Handler : Handlers) { + auto Fn = sol::function(); + if (std::holds_alternative(Handler)) { + Fn = mStateView[std::get(Handler)]; + }else { + Fn = *std::get>(Handler); + } if (Fn.valid() && Fn.get_type() == sol::type::function) { auto FnRet = Fn(EventArgs); if (FnRet.valid()) { - Result.set(i, FnRet); + if (FnRet.return_count() > 0) { + Result.set(i, FnRet.get()); + } ++i; } else { sol::error Err = FnRet; @@ -831,8 +861,21 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI }); MPTable.set_function("GetOSName", &LuaAPI::MP::GetOSName); MPTable.set_function("GetServerVersion", &LuaAPI::MP::GetServerVersion); - MPTable.set_function("RegisterEvent", [this](const std::string& EventName, const std::string& FunctionName) { - RegisterEvent(EventName, FunctionName); + MPTable.set_function("RegisterEvent", [this](const std::string& EventName, sol::object FunctionObject) { + LuaFunction handler; + bool valid = false; + if (FunctionObject.is()) { + handler = FunctionObject.as(); + valid = true; + } + else if (FunctionObject.is()) { + auto ptr = std::make_shared(FunctionObject.as()); + handler = std::move(ptr); + valid = true; + } + if (valid) { + RegisterEvent(EventName, std::move(handler)); + } }); MPTable.set_function("TriggerGlobalEvent", [&](const std::string& EventName, sol::variadic_args EventArgs) -> sol::table { return Lua_TriggerGlobalEvent(EventName, EventArgs); @@ -1046,54 +1089,61 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueScript(const TLu return Result; } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy) { +std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName, CallStrategy Strategy) { + std::unique_lock Lock(mStateFunctionQueueMutex); + auto Iter = mStateFunctionQueue.end(); // 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; + [&EventName, &FunctionObject](const QueuedFunction& Element) { + return Element.EventName == EventName + && Element.FunctionObject == FunctionObject; }); } if (Iter == mStateFunctionQueue.end()) { auto Result = std::make_shared(); Result->StateId = mStateId; - Result->Function = FunctionName; - std::unique_lock Lock(mStateFunctionQueueMutex); - mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName }); + Result->Function = FunctionObject; + mStateFunctionQueue.push_back({ FunctionObject, Result, Args, EventName }); mStateFunctionQueueCond.notify_all(); return Result; - } else { - return nullptr; } + return nullptr; } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args, const std::string& EventName) { +std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const LuaFunction& FunctionObject, const std::vector& Args, const std::string& EventName) { auto Result = std::make_shared(); Result->StateId = mStateId; - Result->Function = FunctionName; + Result->Function = FunctionObject; std::unique_lock Lock(mStateFunctionQueueMutex); - mStateFunctionQueue.push_back({ FunctionName, Result, Args, EventName }); + mStateFunctionQueue.push_back({ FunctionObject, Result, Args, EventName }); mStateFunctionQueueCond.notify_all(); return Result; } -void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, const std::string& FunctionName) { - mEngine->RegisterEvent(EventName, mStateId, FunctionName); +void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, const LuaFunction& FunctionObject) const { + mEngine->RegisterEvent(EventName, mStateId, FunctionObject); } void TLuaEngine::StateThreadData::operator()() { RegisterThread("Lua:" + mStateId); while (!Application::IsShuttingDown()) { - { // StateExecuteQueue Scope - std::unique_lock Lock(mStateExecuteQueueMutex); - if (!mStateExecuteQueue.empty()) { - auto S = mStateExecuteQueue.front(); - mStateExecuteQueue.pop(); - Lock.unlock(); + { + using ItemType = std::remove_reference_t; + std::optional S; + + { // StateExecuteQueue Scope + std::unique_lock Lock(mStateExecuteQueueMutex); + if (!mStateExecuteQueue.empty()) { + S = std::move(mStateExecuteQueue.front()); + mStateExecuteQueue.pop(); + } + } + if (S.has_value()) { + std::lock_guard StateLock(mEngine->mLuaStatesMutex); { // Paths Scope - std::unique_lock Lock(mPathsMutex); + std::unique_lock PathLock(mPathsMutex); if (!mPaths.empty()) { std::stringstream PathAdditions; std::stringstream CPathAdditions; @@ -1114,20 +1164,22 @@ void TLuaEngine::StateThreadData::operator()() { auto PackageTable = StateView.globals().get("package"); PackageTable["path"] = PackageTable.get("path") + PathAdditions.str(); PackageTable["cpath"] = PackageTable.get("cpath") + CPathAdditions.str(); - StateView.globals()["package"] = PackageTable; } } sol::state_view StateView(mState); - auto Res = StateView.safe_script(*S.first.Content, sol::script_pass_on_error, S.first.FileName); + auto Res = StateView.safe_script(*S->first.Content, sol::script_pass_on_error, S->first.FileName); if (Res.valid()) { - S.second->Error = false; - S.second->Result = std::move(Res); + S->second->Error = false; + if (Res.return_count() > 0) { + S->second->Result = std::move(Res); + } } else { - S.second->Error = true; - sol::error Err = Res; - S.second->ErrorMessage = Err.what(); + S->second->Error = true; + sol::error err = Res; + S->second->ErrorMessage = err.what(); } - S.second->MarkAsReady(); + lua_settop(mState, 0); + S->second->MarkAsReady(); } } { // StateFunctionQueue Scope @@ -1138,15 +1190,28 @@ void TLuaEngine::StateThreadData::operator()() { if (NotExpired) { auto ProfStart = prof::now(); auto TheQueuedFunction = std::move(mStateFunctionQueue.front()); - mStateFunctionQueue.erase(mStateFunctionQueue.begin()); + mStateFunctionQueue.pop_front(); Lock.unlock(); - auto& FnName = TheQueuedFunction.FunctionName; + + std::lock_guard LuaLock(mEngine->mLuaStatesMutex); + sol::state_view StateView(mState); + auto& Result = TheQueuedFunction.Result; - auto Args = TheQueuedFunction.Args; - // TODO: Use TheQueuedFunction.EventName for errors, warnings, etc + auto& Args = TheQueuedFunction.Args; + auto& FnObject = TheQueuedFunction.FunctionObject; Result->StateId = mStateId; - sol::state_view StateView(mState); - auto Fn = StateView[FnName]; + lua_settop(mState, 0); + sol::main_protected_function Fn; + std::string profileName; + if (std::holds_alternative(FnObject)) { + auto name = std::get(FnObject); + Fn = StateView[name]; + profileName = name; + } else { + Fn = *std::get>(FnObject); + profileName = "[lua anonymous function]"; + } + if (Fn.valid() && Fn.get_type() == sol::type::function) { std::vector LuaArgs; for (const auto& Arg : Args) { @@ -1182,37 +1247,39 @@ void TLuaEngine::StateThreadData::operator()() { break; } } - auto Res = Fn(sol::as_args(LuaArgs)); + auto Res = Fn.call<>(sol::as_args(LuaArgs)); if (Res.valid()) { Result->Error = false; Result->Result = std::move(Res); } else { - Result->Error = true; - sol::error Err = Res; - Result->ErrorMessage = Err.what(); + if (lua_gettop(mState) > 0) { + if (sol::stack_object err_obj(mState, -1); err_obj.is()) { + Result->ErrorMessage = err_obj.as(); + } + } } - Result->MarkAsReady(); } else { Result->Error = true; Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later - Result->MarkAsReady(); } + Result->MarkAsReady(); + lua_settop(mState, 0); + auto ProfEnd = prof::now(); auto ProfDuration = prof::duration(ProfStart, ProfEnd); - mProfile.add_sample(FnName, ProfDuration); + mProfile.add_sample(profileName, ProfDuration); } } } } - std::queue>> TLuaEngine::StateThreadData::Debug_GetStateExecuteQueue() { std::unique_lock Lock(mStateExecuteQueueMutex); return mStateExecuteQueue; } -std::vector TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { +std::deque TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { std::unique_lock Lock(mStateFunctionQueueMutex); - return mStateFunctionQueue; + return std::deque(mStateFunctionQueue); } void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS, CallStrategy Strategy) { @@ -1224,6 +1291,10 @@ void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId Stat StateId, Strategy }; + if (std::ranges::any_of(mTimedEvents, [&](const auto& e) { return e.EventName == Event.EventName && e.StateId == Event.StateId; })) { + beammp_trace("event timer for \"" + EventName + "\" on \"" + StateId + "\" already exists"); + return; + } mTimedEvents.push_back(std::move(Event)); beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval"); } diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 049fd200..5309026f 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -67,6 +67,7 @@ void TPluginMonitor::operator()() { FileStream.read(Contents->data(), Contents->size()); TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string()); auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path()); + mEngine->ClearEventsForState(StateID); auto Res = mEngine->EnqueueScript(StateID, Chunk); Res->WaitUntilReady(); if (Res->Error) {