From a10665ba1706cf1d078e2d09f5e7ac1d1e32eba4 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Mon, 23 Feb 2026 20:24:38 +0100 Subject: [PATCH 01/14] implementation of anonymous function --- include/TLuaEngine.h | 20 ++++++------ src/TConsole.cpp | 30 ++++++++--------- src/TLuaEngine.cpp | 78 ++++++++++++++++++++++++-------------------- 3 files changed, 67 insertions(+), 61 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 47a5c760..cfdbfe76 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -76,7 +76,7 @@ struct TLuaResult { std::string ErrorMessage; sol::object Result { sol::lua_nil }; TLuaStateId StateId; - std::string Function; + sol::object Function; std::shared_ptr ReadyMutex { std::make_shared() }; @@ -112,7 +112,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { }; struct QueuedFunction { - std::string FunctionName; + sol::object FunctionObject; std::shared_ptr Result; std::vector Args; std::string EventName; // optional, may be empty @@ -167,9 +167,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 sol::object& 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 sol::object& FunctionObject); /** * * @tparam ArgsT Template Arguments for the event (Metadata) todo: figure out what this means @@ -213,7 +213,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { } return Results; } - std::set GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); + std::vector>> 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); @@ -226,7 +226,7 @@ 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::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); @@ -243,9 +243,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 sol::object& FunctionObject, const std::vector& Args, const std::string& EventName); + [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const sol::object& FunctionObject, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); + void RegisterEvent(const std::string& EventName, const sol::object& 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); } @@ -309,7 +309,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/TConsole.cpp b/src/TConsole.cpp index fb1dc8b0..44f80891 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -732,28 +732,26 @@ 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 sol::object Function : FunctionsInOrder) { + if (!(std::find(Uniques.begin(), Uniques.end(), Function) == Uniques.end())) { + Uniques.push_back(Function); + if (Index != 0) { + Application::Console().WriteRaw(" " + Function.as() + " (" + std::to_string(Index) + "x)"); } else { - Application::Console().WriteRaw(" " + Function); + Application::Console().WriteRaw(" " + Function.as()); } } } 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") + ")"); + Application::Console().WriteRaw(" '" + Function.Function.as() + "' (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 +759,7 @@ 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); + Application::Console().WriteRaw(" " + Handler.as()); } } } else if (cmd == "help") { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 49dd4455..3f343747 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(Ptr->Function.as() + ": " + Ptr->ErrorMessage); } } return true; @@ -185,8 +185,8 @@ void TLuaEngine::AddResultToCheck(const std::shared_ptr& 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) { @@ -314,23 +314,23 @@ void TLuaEngine::WaitForAll(std::vector>& Results, c 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("'" + Result->Function.as() + "' 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 + "_" + Result->Function.as(); 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("'" + Result->Function.as() + "' 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("'" + Result->Function.as() + "' 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(Result->Function.as() + ": " + Result->ErrorMessage); } } } @@ -355,9 +355,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 sol::object& 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 +428,15 @@ 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 sol::object& FunctionObject) { std::unique_lock Lock(mLuaEventsMutex); - mLuaEvents[EventName][StateId].insert(FunctionName); + mLuaEvents[EventName][StateId].emplace_back(FunctionObject); } -std::set TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { +std::vector>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { return mLuaEvents[EventName][StateId]; } @@ -542,7 +534,12 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& sol::table Result = mStateView.create_table(); int i = 1; for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) { - auto Fn = mStateView[Handler]; + auto Fn = sol::function(); + if (Handler.is()) { + Fn = mStateView[Handler]; + }else { + Fn = Handler; + } if (Fn.valid() && Fn.get_type() == sol::type::function) { auto FnRet = Fn(EventArgs); if (FnRet.valid()) { @@ -831,8 +828,8 @@ 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, const sol::object& FunctionObject) { + RegisterEvent(EventName, FunctionObject); }); MPTable.set_function("TriggerGlobalEvent", [&](const std::string& EventName, sol::variadic_args EventArgs) -> sol::table { return Lua_TriggerGlobalEvent(EventName, EventArgs); @@ -1046,7 +1043,7 @@ 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 sol::object& FunctionObject, const std::vector& Args, const std::string& EventName, CallStrategy Strategy) { // TODO: Document all this decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end(); if (Strategy == CallStrategy::BestEffort) { @@ -1058,9 +1055,9 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom if (Iter == mStateFunctionQueue.end()) { 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; } else { @@ -1068,18 +1065,18 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom } } -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 sol::object& 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 sol::object& FunctionObject) const { + mEngine->RegisterEvent(EventName, mStateId, FunctionObject); } void TLuaEngine::StateThreadData::operator()() { @@ -1140,13 +1137,18 @@ void TLuaEngine::StateThreadData::operator()() { auto TheQueuedFunction = std::move(mStateFunctionQueue.front()); mStateFunctionQueue.erase(mStateFunctionQueue.begin()); Lock.unlock(); - auto& FnName = TheQueuedFunction.FunctionName; auto& Result = TheQueuedFunction.Result; auto Args = TheQueuedFunction.Args; // TODO: Use TheQueuedFunction.EventName for errors, warnings, etc Result->StateId = mStateId; sol::state_view StateView(mState); - auto Fn = StateView[FnName]; + + auto Fn = sol::function(); + if (TheQueuedFunction.FunctionObject.is()) { + Fn = StateView[TheQueuedFunction.FunctionObject]; + }else { + Fn = TheQueuedFunction.FunctionObject; + } if (Fn.valid() && Fn.get_type() == sol::type::function) { std::vector LuaArgs; for (const auto& Arg : Args) { @@ -1199,7 +1201,13 @@ void TLuaEngine::StateThreadData::operator()() { } auto ProfEnd = prof::now(); auto ProfDuration = prof::duration(ProfStart, ProfEnd); - mProfile.add_sample(FnName, ProfDuration); + std::string profileName; + if (TheQueuedFunction.FunctionObject.is()) { + profileName = TheQueuedFunction.FunctionObject.as(); + } else { + profileName = "[lua anonymous function]"; + } + mProfile.add_sample(profileName, ProfDuration); } } } From f95037f38713552536a393b3a7e4b2a38ec6f90a Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Mon, 23 Feb 2026 23:59:18 +0100 Subject: [PATCH 02/14] fix register event to overwrite the function, fix EnqueueFunctionCallFromCustomEvent that was crashing the server on script reload, mStateFunctionQueue is now a deque additionally createEventTimer cant register the same event more than one time now --- include/TLuaEngine.h | 6 +++--- src/TLuaEngine.cpp | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index cfdbfe76..107ecfd3 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -228,7 +228,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { // Debugging functions (slow) 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: @@ -255,7 +255,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 +281,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; std::mutex mStateFunctionQueueMutex; std::condition_variable mStateFunctionQueueCond; TLuaEngine* mEngine; diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 3f343747..d4da5c89 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -207,10 +207,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; } @@ -433,7 +432,7 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const sol::object& FunctionObject) { std::unique_lock Lock(mLuaEventsMutex); - mLuaEvents[EventName][StateId].emplace_back(FunctionObject); + mLuaEvents[EventName][StateId] = { FunctionObject }; } std::vector>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { @@ -1044,8 +1043,9 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueScript(const TLu } std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const sol::object& 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) { @@ -1056,13 +1056,11 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom auto Result = std::make_shared(); Result->StateId = mStateId; Result->Function = FunctionObject; - std::unique_lock Lock(mStateFunctionQueueMutex); mStateFunctionQueue.push_back({ FunctionObject, Result, Args, EventName }); mStateFunctionQueueCond.notify_all(); return Result; - } else { - return nullptr; } + return nullptr; } std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const sol::object& FunctionObject, const std::vector& Args, const std::string& EventName) { @@ -1135,7 +1133,7 @@ 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& Result = TheQueuedFunction.Result; auto Args = TheQueuedFunction.Args; @@ -1218,9 +1216,9 @@ std::queue>> TLuaEngine::StateT 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) { @@ -1232,6 +1230,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"); } From 6f5d874a95115634cf1991de9073ed37e2cf4b33 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Thu, 26 Feb 2026 14:05:23 +0100 Subject: [PATCH 03/14] fixed events overwrite, now using std::variant with sol::main_protected_function, std::string --- include/Common.h | 5 +-- include/TLuaEngine.h | 23 ++++++------ src/Common.cpp | 9 +++++ src/TConsole.cpp | 17 +++++---- src/TLuaEngine.cpp | 86 ++++++++++++++++++++++++-------------------- 5 files changed, 81 insertions(+), 59 deletions(-) diff --git a/include/Common.h b/include/Common.h index abb866df..3ae1b563 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; 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 107ecfd3..830590f2 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; - sol::object 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 { - sol::object FunctionObject; + 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 sol::object& FunctionObject, 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 sol::object& FunctionObject); + 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 {}; } @@ -213,7 +214,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { } return Results; } - std::vector>> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); + std::vector>, 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); @@ -226,7 +227,7 @@ 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::deque Debug_GetStateFunctionQueueForState(TLuaStateId StateId); std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); @@ -243,9 +244,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 sol::object& FunctionObject, const std::vector& Args, const std::string& EventName); - [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const sol::object& FunctionObject, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); - void RegisterEvent(const std::string& EventName, const sol::object& FunctionObject) const; + [[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); } @@ -309,7 +310,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/TConsole.cpp b/src/TConsole.cpp index 44f80891..f6596089 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -733,25 +733,27 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId); Application::Console().WriteRaw("Pending functions in State '" + mStateId + "'"); size_t FunctionsCount = QueuedFunctions.size(); - std::vector FunctionsInOrder; + std::vector FunctionsInOrder; for (int i = 0; i < FunctionsCount; ++i) { FunctionsInOrder.push_back(QueuedFunctions[i].FunctionObject); } - std::vector Uniques; + std::vector Uniques; int Index = 0; - for (const sol::object Function : FunctionsInOrder) { + 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(" " + Function.as() + " (" + std::to_string(Index) + "x)"); + Application::Console().WriteRaw(" " + functionName + " (" + std::to_string(Index) + "x)"); } else { - Application::Console().WriteRaw(" " + Function.as()); + 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.as() + "' (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); @@ -759,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.as()); + std::string functionName = GetFunctionName(Handler);; + Application::Console().WriteRaw(" " + functionName); } } } else if (cmd == "help") { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index d4da5c89..df07065d 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.as() + ": " + 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(); @@ -185,8 +188,8 @@ void TLuaEngine::AddResultToCheck(const std::shared_ptr& 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) { @@ -308,28 +311,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.as() + "' 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.as(); + auto ResultId = Result->StateId + "_" + functionName; if (WarnedResults.count(ResultId) == 0) { WarnedResults.insert(ResultId); - beammp_lua_warn("'" + Result->Function.as() + "' 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.as() + "' 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.as() + ": " + Result->ErrorMessage); + beammp_lua_error(functionName + ": " + Result->ErrorMessage); } } } @@ -354,7 +358,7 @@ std::shared_ptr TLuaEngine::EnqueueScript(TLuaStateId StateID, const return mLuaStates.at(StateID)->EnqueueScript(Script); } -std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const sol::object& FunctionObject, 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(FunctionObject, Args, EventName); } @@ -430,12 +434,12 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, } } -void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const sol::object& FunctionObject) { +void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const LuaFunction& FunctionObject) { std::unique_lock Lock(mLuaEventsMutex); - mLuaEvents[EventName][StateId] = { FunctionObject }; + mLuaEvents[EventName][StateId].push_back(std::move(FunctionObject)); } -std::vector>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { +std::vector>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { return mLuaEvents[EventName][StateId]; } @@ -534,10 +538,10 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string& int i = 1; for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) { auto Fn = sol::function(); - if (Handler.is()) { - Fn = mStateView[Handler]; + if (std::holds_alternative(Handler)) { + Fn = mStateView[std::get(Handler)]; }else { - Fn = Handler; + Fn = std::get(Handler); } if (Fn.valid() && Fn.get_type() == sol::type::function) { auto FnRet = Fn(EventArgs); @@ -827,7 +831,7 @@ 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 sol::object& FunctionObject) { + MPTable.set_function("RegisterEvent", [this](const std::string& EventName, const sol::main_object& FunctionObject) { RegisterEvent(EventName, FunctionObject); }); MPTable.set_function("TriggerGlobalEvent", [&](const std::string& EventName, sol::variadic_args EventArgs) -> sol::table { @@ -1042,7 +1046,7 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueScript(const TLu return Result; } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const sol::object& FunctionObject, 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 @@ -1063,7 +1067,7 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom return nullptr; } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const sol::object& FunctionObject, 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 = FunctionObject; @@ -1073,8 +1077,8 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(con return Result; } -void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, const sol::object& FunctionObject) const { - mEngine->RegisterEvent(EventName, mStateId, FunctionObject); +void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, const LuaFunction& FunctionObject) const { + mEngine->RegisterEvent(EventName, mStateId, FunctionObject); } void TLuaEngine::StateThreadData::operator()() { @@ -1133,22 +1137,30 @@ void TLuaEngine::StateThreadData::operator()() { if (NotExpired) { auto ProfStart = prof::now(); auto TheQueuedFunction = std::move(mStateFunctionQueue.front()); - mStateFunctionQueue.pop_front(); + mStateFunctionQueue.erase(mStateFunctionQueue.begin()); Lock.unlock(); auto& Result = TheQueuedFunction.Result; auto Args = TheQueuedFunction.Args; // TODO: Use TheQueuedFunction.EventName for errors, warnings, etc + auto& FnObject = TheQueuedFunction.FunctionObject; Result->StateId = mStateId; + std::lock_guard LuaLock(mEngine->mLuaStatesMutex); sol::state_view StateView(mState); - - auto Fn = sol::function(); - if (TheQueuedFunction.FunctionObject.is()) { - Fn = StateView[TheQueuedFunction.FunctionObject]; - }else { - Fn = TheQueuedFunction.FunctionObject; + auto Fn = sol::main_protected_function(); + std::string profileName; + if (std::holds_alternative(FnObject)) { + std::string name = std::get(FnObject); + beammp_debug("Function call name: " + name); + Fn = StateView[name]; + profileName = name; + } else { + Fn = std::get(FnObject); + profileName = "[lua anonymous function]"; } + + std::vector LuaArgs; + if (Fn.valid() && Fn.get_type() == sol::type::function) { - std::vector LuaArgs; for (const auto& Arg : Args) { if (Arg.valueless_by_exception()) { continue; @@ -1182,7 +1194,7 @@ 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); @@ -1191,20 +1203,16 @@ void TLuaEngine::StateThreadData::operator()() { sol::error Err = Res; Result->ErrorMessage = Err.what(); } - Result->MarkAsReady(); } else { Result->Error = true; Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later - Result->MarkAsReady(); } + Result->MarkAsReady(); + LuaArgs.clear(); + lua_settop(mState, 0); + auto ProfEnd = prof::now(); auto ProfDuration = prof::duration(ProfStart, ProfEnd); - std::string profileName; - if (TheQueuedFunction.FunctionObject.is()) { - profileName = TheQueuedFunction.FunctionObject.as(); - } else { - profileName = "[lua anonymous function]"; - } mProfile.add_sample(profileName, ProfDuration); } } From bed10fbaa777d8afcad6d0e575b305d114988b4d Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Thu, 26 Feb 2026 14:05:42 +0100 Subject: [PATCH 04/14] fixed stack overflow on the panic handler --- src/LuaAPI.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index c71ff299..d0b1a134 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -436,7 +436,10 @@ void LuaAPI::MP::PrintRaw(sol::variadic_args Args) { } int LuaAPI::PanicHandler(lua_State* State) { - beammp_lua_error("PANIC: " + sol::stack::get(State, 1)); + const char* message = lua_tostring(State, -1); + if (message) { + beammp_lua_error("PANIC: " + std::string(message)); + } return 0; } From 9ca0ce7cfd3db6beafb950b88d01314a6cee22a1 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Thu, 26 Feb 2026 14:16:32 +0100 Subject: [PATCH 05/14] fixed set_function RegisterEvent and removed a beammp debug message --- src/TLuaEngine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index df07065d..a0c61166 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -831,7 +831,7 @@ 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 sol::main_object& FunctionObject) { + MPTable.set_function("RegisterEvent", [this](const std::string& EventName, const LuaFunction& FunctionObject) { RegisterEvent(EventName, FunctionObject); }); MPTable.set_function("TriggerGlobalEvent", [&](const std::string& EventName, sol::variadic_args EventArgs) -> sol::table { @@ -1150,7 +1150,6 @@ void TLuaEngine::StateThreadData::operator()() { std::string profileName; if (std::holds_alternative(FnObject)) { std::string name = std::get(FnObject); - beammp_debug("Function call name: " + name); Fn = StateView[name]; profileName = name; } else { From 1a46dfd708fb48f37ad3efd24d161980f27ede59 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Thu, 26 Feb 2026 14:20:17 +0100 Subject: [PATCH 06/14] lock name change --- src/TLuaEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index a0c61166..4eb3a03d 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1092,7 +1092,7 @@ void TLuaEngine::StateThreadData::operator()() { Lock.unlock(); { // Paths Scope - std::unique_lock Lock(mPathsMutex); + std::unique_lock PathLock(mPathsMutex); if (!mPaths.empty()) { std::stringstream PathAdditions; std::stringstream CPathAdditions; From 144b862fb2f7710547679a0924b251e6a3aa0c6a Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Thu, 26 Feb 2026 14:38:54 +0100 Subject: [PATCH 07/14] clear events on reload --- include/TLuaEngine.h | 1 + src/TLuaEngine.cpp | 12 +++++++++++- src/TPluginMonitor.cpp | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 830590f2..be28a0bb 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -219,6 +219,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { 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"; diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 4eb3a03d..f479b965 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -182,6 +182,13 @@ 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); + } +} + void TLuaEngine::AddResultToCheck(const std::shared_ptr& Result) { std::unique_lock Lock(mResultsToCheckMutex); mResultsToCheck.push_back(Result); @@ -436,7 +443,10 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const LuaFunction& FunctionObject) { std::unique_lock Lock(mLuaEventsMutex); - mLuaEvents[EventName][StateId].push_back(std::move(FunctionObject)); + auto& Events = mLuaEvents[EventName][StateId]; + if (const auto it = std::find(Events.begin(), Events.end(), FunctionObject); it == Events.end()) { + Events.push_back(std::move(FunctionObject)); + } } std::vector>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { 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) { From 1a89f8fe724e0c8ba5410cb03abd9c9a53199bde Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Tue, 10 Mar 2026 19:19:37 +0100 Subject: [PATCH 08/14] Fixed events and related functions. Fixed stack exceptions and lua panic inside StateThreadData::operator()() --- include/Common.h | 2 +- include/TLuaEngine.h | 4 +- src/TLuaEngine.cpp | 113 ++++++++++++++++++++++++++++--------------- 3 files changed, 76 insertions(+), 43 deletions(-) diff --git a/include/Common.h b/include/Common.h index 3ae1b563..44bb7dfb 100644 --- a/include/Common.h +++ b/include/Common.h @@ -41,7 +41,7 @@ namespace fs = std::filesystem; #include "Settings.h" #include "TConsole.h" -using LuaFunction = std::variant; +using LuaFunction = std::variant, std::string>; struct Version { uint8_t major; uint8_t minor; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index be28a0bb..c4f4c740 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -203,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; @@ -214,7 +214,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { } return Results; } - std::vector>, std::string>> GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); + std::vector>>, 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); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index f479b965..027755e1 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -183,9 +183,21 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) { } 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(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; + } + } } } @@ -449,7 +461,7 @@ void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId } } -std::vector>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { +std::vector>>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { return mLuaEvents[EventName][StateId]; } @@ -496,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(); @@ -546,17 +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)) { + 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); + 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; @@ -841,8 +861,8 @@ 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 LuaFunction& FunctionObject) { - RegisterEvent(EventName, FunctionObject); + MPTable.set_function("RegisterEvent", [this](const std::string& EventName, sol::main_protected_function FunctionObject) { + RegisterEvent(EventName, std::make_shared(std::move(FunctionObject))); }); MPTable.set_function("TriggerGlobalEvent", [&](const std::string& EventName, sol::variadic_args EventArgs) -> sol::table { return Lua_TriggerGlobalEvent(EventName, EventArgs); @@ -1062,8 +1082,9 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom // TODO: Document all this 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()) { @@ -1094,13 +1115,20 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co 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 PathLock(mPathsMutex); if (!mPaths.empty()) { @@ -1123,20 +1151,24 @@ 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(); + if (lua_gettop(mState) > 0) { + if (sol::stack_object err_obj(mState, -1); err_obj.is()) { + S->second->ErrorMessage = err_obj.as(); + } + } } - S.second->MarkAsReady(); + lua_settop(mState, 0); + S->second->MarkAsReady(); } } { // StateFunctionQueue Scope @@ -1149,27 +1181,28 @@ void TLuaEngine::StateThreadData::operator()() { auto TheQueuedFunction = std::move(mStateFunctionQueue.front()); mStateFunctionQueue.erase(mStateFunctionQueue.begin()); Lock.unlock(); + + 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; - std::lock_guard LuaLock(mEngine->mLuaStatesMutex); - sol::state_view StateView(mState); - auto Fn = sol::main_protected_function(); + lua_settop(mState, 0); + sol::main_protected_function Fn; std::string profileName; if (std::holds_alternative(FnObject)) { - std::string name = std::get(FnObject); + auto name = std::get(FnObject); Fn = StateView[name]; profileName = name; } else { - Fn = std::get(FnObject); + Fn = *std::get>(FnObject); profileName = "[lua anonymous function]"; } - std::vector LuaArgs; - if (Fn.valid() && Fn.get_type() == sol::type::function) { + std::vector LuaArgs; for (const auto& Arg : Args) { if (Arg.valueless_by_exception()) { continue; @@ -1208,16 +1241,17 @@ void TLuaEngine::StateThreadData::operator()() { 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(); + } + } } } else { Result->Error = true; Result->ErrorMessage = BeamMPFnNotFoundError; // special error kind that we can ignore later } Result->MarkAsReady(); - LuaArgs.clear(); lua_settop(mState, 0); auto ProfEnd = prof::now(); @@ -1227,7 +1261,6 @@ void TLuaEngine::StateThreadData::operator()() { } } } - std::queue>> TLuaEngine::StateThreadData::Debug_GetStateExecuteQueue() { std::unique_lock Lock(mStateExecuteQueueMutex); return mStateExecuteQueue; From 680369e9e40327773e2fa3e152e29b35a14a13ef Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Sat, 21 Mar 2026 21:17:52 +0100 Subject: [PATCH 09/14] pascal case --- src/TConsole.cpp | 14 +++++++------- src/TLuaEngine.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/TConsole.cpp b/src/TConsole.cpp index f6596089..51b2eac2 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -741,19 +741,19 @@ void TConsole::HandleLuaInternalCommand(const std::string& cmd) { int Index = 0; for (const LuaFunction Function : FunctionsInOrder) { if (!(std::find(Uniques.begin(), Uniques.end(), Function) == Uniques.end())) { - std::string functionName = GetFunctionName(Function); + std::string FunctionName = GetFunctionName(Function); Uniques.push_back(Function); if (Index != 0) { - Application::Console().WriteRaw(" " + functionName + " (" + std::to_string(Index) + "x)"); + Application::Console().WriteRaw(" " + FunctionName + " (" + std::to_string(Index) + "x)"); } else { - Application::Console().WriteRaw(" " + functionName); + 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)) { - std::string functionName = GetFunctionName(Function.Function);; - Application::Console().WriteRaw(" '" + functionName + "' (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,8 +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) { - std::string functionName = GetFunctionName(Handler);; - Application::Console().WriteRaw(" " + functionName); + std::string FunctionName = GetFunctionName(Handler);; + Application::Console().WriteRaw(" " + FunctionName); } } } else if (cmd == "help") { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 027755e1..cf73c20f 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -330,29 +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); + 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("'" + functionName + "' 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 + "_" + functionName; + auto ResultId = Result->StateId + "_" + FunctionName; if (WarnedResults.count(ResultId) == 0) { WarnedResults.insert(ResultId); - 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."); + 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("'" + functionName + "' 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(functionName + ": " + Result->ErrorMessage); + beammp_lua_error(FunctionName + ": " + Result->ErrorMessage); } } } From e05372f7739ad6cfc909efe8d14eab20f46f0881 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Sat, 21 Mar 2026 22:34:34 +0100 Subject: [PATCH 10/14] pop --- src/TLuaEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index cf73c20f..069f1a5a 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1179,7 +1179,7 @@ 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(); std::lock_guard LuaLock(mEngine->mLuaStatesMutex); From e6bdda0534e72d462b25bcb7ac435c49151a8280 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Mon, 23 Mar 2026 10:46:40 +0100 Subject: [PATCH 11/14] commenting the vector to deque change --- include/TLuaEngine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index c4f4c740..91b57144 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -283,7 +283,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { std::thread mThread; std::queue>> mStateExecuteQueue; std::recursive_mutex mStateExecuteQueueMutex; - std::deque 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; From a2ca6c60468becea6ebf873db759cbe5fb66155b Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Fri, 10 Apr 2026 15:59:29 +0200 Subject: [PATCH 12/14] fix strings functions (woops) --- src/TLuaEngine.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 069f1a5a..4ae8c513 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -861,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, sol::main_protected_function FunctionObject) { - RegisterEvent(EventName, std::make_shared(std::move(FunctionObject))); + 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); From 95b2f39a718041a5f67b0e20ba41e8ac2358d007 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Fri, 10 Apr 2026 16:44:39 +0200 Subject: [PATCH 13/14] fix error handling, using sol for the panic handler --- src/LuaAPI.cpp | 5 +++-- src/TLuaEngine.cpp | 8 +++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index d0b1a134..6622023c 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -436,9 +436,10 @@ void LuaAPI::MP::PrintRaw(sol::variadic_args Args) { } int LuaAPI::PanicHandler(lua_State* State) { - const char* message = lua_tostring(State, -1); + std::optional message = sol::stack::get>(State, -1); + if (message) { - beammp_lua_error("PANIC: " + std::string(message)); + beammp_lua_error("PANIC: " + *message); } return 0; } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 4ae8c513..8a6386df 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -1174,11 +1174,9 @@ void TLuaEngine::StateThreadData::operator()() { S->second->Result = std::move(Res); } } else { - if (lua_gettop(mState) > 0) { - if (sol::stack_object err_obj(mState, -1); err_obj.is()) { - S->second->ErrorMessage = err_obj.as(); - } - } + S->second->Error = true; + sol::error err = Res; + S->second->ErrorMessage = err.what(); } lua_settop(mState, 0); S->second->MarkAsReady(); From 25635e64f895ce9c6dd29dab8795060dc3e13d19 Mon Sep 17 00:00:00 2001 From: boubouleuh Date: Fri, 10 Apr 2026 17:04:41 +0200 Subject: [PATCH 14/14] change vector back to set --- include/TLuaEngine.h | 4 ++-- src/TLuaEngine.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 91b57144..56a17288 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -214,7 +214,7 @@ class TLuaEngine : public std::enable_shared_from_this, IThreaded { } return Results; } - std::vector>>, 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 CancelEventTimers(const std::string& EventName, TLuaStateId StateId); sol::state_view GetStateForPlugin(const fs::path& PluginPath); @@ -311,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/TLuaEngine.cpp b/src/TLuaEngine.cpp index 8a6386df..feb3d8d0 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -457,11 +457,11 @@ void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId std::unique_lock Lock(mLuaEventsMutex); auto& Events = mLuaEvents[EventName][StateId]; if (const auto it = std::find(Events.begin(), Events.end(), FunctionObject); it == Events.end()) { - Events.push_back(std::move(FunctionObject)); + Events.emplace(std::move(FunctionObject)); } } -std::vector>>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { +std::set>>, std::string>> TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { return mLuaEvents[EventName][StateId]; }