From 4bf89706b43ef71d9eb3e4b71e7eb1f0da131a44 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Mon, 20 Sep 2021 12:43:00 +0300 Subject: [PATCH] Lua: Add MP.CreateTimedEvent as CreateThread replacement --- Changelog.md | 1 + include/TLuaEngine.h | 22 ++++++++++++++++----- src/TLuaEngine.cpp | 47 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0ee3158..f1a6bbc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,7 @@ - ADDED `MP.HttpsPOST(host,port,target,body,content_type) -> status,body`: Does a synchronous HTTPS POST request - ADDED `MP.GetStateMemoryUsage() -> number`: Current memory usage of the current state in bytes - ADDED `MP.GetLuaMemoryUsage() -> number`: Current memory usage of all states combined, in bytes +- ADDED `MP.CreateTimedEvent(event,interval_ms)`: Replacement for `CreateThread` - calls the event in the given interval # v2.3.3 diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 4cdba0d..5fd60c9 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -75,12 +75,12 @@ public: void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); template [[nodiscard]] std::vector> TriggerEvent(const std::string& EventName, TLuaStateId IgnoreId, ArgsT&&... Args) { - std::unique_lock Lock(mEventsMutex); - if (mEvents.find(EventName) == mEvents.end()) { + std::unique_lock Lock(mLuaEventsMutex); + if (mLuaEvents.find(EventName) == mLuaEvents.end()) { return {}; } std::vector> Results; - for (const auto& Event : mEvents.at(EventName)) { + for (const auto& Event : mLuaEvents.at(EventName)) { for (const auto& Function : Event.second) { if (Event.first != IgnoreId) { Results.push_back(EnqueueFunctionCall(Event.first, Function, { TLuaArgTypes { std::forward(Args) }... })); @@ -90,6 +90,7 @@ public: return Results; } std::set GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); + void CreateTimedEvent(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS); static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; @@ -134,6 +135,15 @@ private: std::recursive_mutex mPathsMutex; }; + struct TimedEvent { + const std::chrono::high_resolution_clock::duration Duration {}; + std::chrono::high_resolution_clock::time_point LastCompletion { }; + std::string EventName; + TLuaStateId StateId; + bool Expired(); + void Reset(); + }; + TNetwork* mNetwork; TServer* mServer; std::atomic_bool mShutdown { false }; @@ -141,8 +151,10 @@ private: std::vector mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; - std::unordered_map>> mEvents; - std::recursive_mutex mEventsMutex; + std::unordered_map>> mLuaEvents; + std::recursive_mutex mLuaEventsMutex; + std::vector mTimedEvents; + std::recursive_mutex mTimedEventsMutex; }; //std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaPlugin* Caller, std::shared_ptr arg, bool Wait); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index d58de2e..a2b400e 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -42,9 +42,23 @@ void TLuaEngine::operator()() { beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage); } } - // this thread handles timers + // event loop while (!mShutdown) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + auto Before = std::chrono::high_resolution_clock::now(); + std::unique_lock Lock(mTimedEventsMutex); + for (auto& Timer : mTimedEvents) { + if (Timer.Expired()) { + Timer.Reset(); + std::unique_lock Lock2(mLuaStatesMutex); + beammp_ignore(mLuaStates[Timer.StateId]->EnqueueFunctionCall("TriggerLocalEvent", { Timer.EventName })); + } + } + // sleep for the remaining time to get to 1ms (our atom duration) + if (std::chrono::high_resolution_clock::duration Diff; + (Diff = Before - std::chrono::high_resolution_clock::now()) + < std::chrono::milliseconds(1)) { + std::this_thread::sleep_for(Diff); + } } } @@ -138,12 +152,12 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, } void TLuaEngine::RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName) { - std::unique_lock Lock(mEventsMutex); - mEvents[EventName][StateId].insert(FunctionName); + std::unique_lock Lock(mLuaEventsMutex); + mLuaEvents[EventName][StateId].insert(FunctionName); } std::set TLuaEngine::GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId) { - return mEvents[EventName][StateId]; + return mLuaEvents[EventName][StateId]; } sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) { @@ -343,6 +357,9 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi }); MPTable.set_function("Sleep", &LuaAPI::MP::Sleep); MPTable.set_function("PrintRaw", &LuaAPI::MP::PrintRaw); + MPTable.set_function("CreateTimedEvent", [&](const std::string& EventName, size_t IntervalMS) { + mEngine->CreateTimedEvent(EventName, mStateId, IntervalMS); + }); MPTable.set_function("Set", &LuaAPI::MP::Set); MPTable.set_function("HttpsGET", [&](const std::string& Host, int Port, const std::string& Target) -> std::tuple { unsigned Status; @@ -497,6 +514,17 @@ void TLuaEngine::StateThreadData::operator()() { } } +void TLuaEngine::CreateTimedEvent(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) { + std::unique_lock Lock(mTimedEventsMutex); + TimedEvent Event { + std::chrono::high_resolution_clock::duration { std::chrono::milliseconds(IntervalMS) }, + std::chrono::high_resolution_clock::now(), + EventName, + StateId + }; + mTimedEvents.push_back(std::move(Event)); +} + void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) { std::unique_lock Lock(mPathsMutex); mPaths.push(Path); @@ -513,3 +541,12 @@ TLuaChunk::TLuaChunk(std::shared_ptr Content, std::string FileName, , FileName(FileName) , PluginPath(PluginPath) { } + +bool TLuaEngine::TimedEvent::Expired() { + auto Waited = (std::chrono::high_resolution_clock::now() - LastCompletion); + return Waited < Duration; +} + +void TLuaEngine::TimedEvent::Reset() { + LastCompletion = std::chrono::high_resolution_clock::now(); +}