From e5051d60364ea5209d6dae00339ffbb5b3d2d4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Sun, 24 Aug 2025 20:59:09 +0700 Subject: [PATCH 1/6] added main class and async timer logic --- apps/openmw/mwworld/asynctimer.cpp | 53 ++++++++++++++++++++++++++++++ apps/openmw/mwworld/asynctimer.hpp | 41 +++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 apps/openmw/mwworld/asynctimer.cpp create mode 100644 apps/openmw/mwworld/asynctimer.hpp diff --git a/apps/openmw/mwworld/asynctimer.cpp b/apps/openmw/mwworld/asynctimer.cpp new file mode 100644 index 0000000000..7d7d39f390 --- /dev/null +++ b/apps/openmw/mwworld/asynctimer.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +#include "asynctimer.hpp" + +namespace MWWorld +{ + AsyncTimer::AsyncTimer() = default; + + AsyncTimer::~AsyncTimer() = default; + + void AsyncTimer::update_timer(){ + auto time_now = std::chrono::steady_clock::now(); + + std::vector expired_callbacks; + + while(!mTasks.empty() && mTasks.begin()->trigger_time <= time_now){ + expired_callbacks.push_back(mTasks.begin()->callback); + mTasks.erase(mTasks.begin()); + } + + for(auto& callback : expired_callbacks){ + callback(); + } + } + + AsyncTimer::TaskId AsyncTimer::set_task(float delay, Callback callback){ + if(delay < 0 || !callback) return -1; + + auto triggerTime = std::chrono::steady_clock::now() + + std::chrono::milliseconds(static_cast(delay * 1000)); + + AsyncTimer::TaskId new_id = mNext_id++; + + mTasks.insert({triggerTime, std::move(callback), new_id}); + return new_id; + } + + void AsyncTimer::cancel_task(AsyncTimer::TaskId id){ + if(id < 0) return; + + for(auto it = mTasks.begin(); it != mTasks.end(); ++it){ + if(it->id == id){ + mTasks.erase(it); + break; + } + } + } +} \ No newline at end of file diff --git a/apps/openmw/mwworld/asynctimer.hpp b/apps/openmw/mwworld/asynctimer.hpp new file mode 100644 index 0000000000..d5fd473908 --- /dev/null +++ b/apps/openmw/mwworld/asynctimer.hpp @@ -0,0 +1,41 @@ +#include +#include +#include + +namespace MWWorld +{ + // @brief Async timer that works when the game is paused + class AsyncTimer + { + public: + AsyncTimer(); + ~AsyncTimer(); + + using TimePoint = std::chrono::steady_clock::time_point; + using Callback = std::function; + using TaskId = uint64_t; + struct TimerTask{ + TimePoint trigger_time; + Callback callback; + TaskId id; + + // Comparator for ordering tasks by execution time (earliest first) + bool operator < (const TimerTask& other) const{ + return trigger_time < other.trigger_time; + } + }; + + // @brief Updates timer state and executes due tasks + void update_timer(); + + // @brief Schedules a new task for delayed execution + TaskId set_task(float delay, Callback callback); + + // @brief Cancels a task by id + void cancel_task(TaskId id); + + private: + std::multiset mTasks; + TaskId mNext_id = 0; + }; +} \ No newline at end of file From d169049b26c7c54a82fcb4146902225ab34d7f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Sun, 24 Aug 2025 21:13:09 +0700 Subject: [PATCH 2/6] add async timer to enviroment --- apps/openmw/mwbase/environment.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 94f918a60b..f2ec6f12fc 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -20,6 +20,7 @@ namespace MWWorld class ESMStore; class WorldModel; class Scene; + class AsyncTimer; } namespace MWBase @@ -55,6 +56,7 @@ namespace MWBase Journal* mJournal = nullptr; InputManager* mInputManager = nullptr; StateManager* mStateManager = nullptr; + MWWorld::AsyncTimer* mAsyncTimer = nullptr; LuaManager* mLuaManager = nullptr; Resource::ResourceSystem* mResourceSystem = nullptr; L10n::Manager* mL10nManager = nullptr; @@ -91,6 +93,8 @@ namespace MWBase void setStateManager(StateManager& value) { mStateManager = &value; } + void setAsyncTimer(MWWorld::AsyncTimer& value) { mAsyncTimer = &value; } + void setLuaManager(LuaManager& value) { mLuaManager = &value; } void setResourceSystem(Resource::ResourceSystem& value) { mResourceSystem = &value; } @@ -118,6 +122,8 @@ namespace MWBase Misc::NotNullPtr getStateManager() const { return mStateManager; } + Misc::NotNullPtr getAsyncTimer() const { return mAsyncTimer; } + Misc::NotNullPtr getLuaManager() const { return mLuaManager; } Misc::NotNullPtr getResourceSystem() const { return mResourceSystem; } From 77f6e7f0aa237953a42263bf4bd554c081d334a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Sun, 24 Aug 2025 21:40:53 +0700 Subject: [PATCH 3/6] feat: implement include guard in asynctimer.hpp --- apps/openmw/mwworld/asynctimer.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/asynctimer.hpp b/apps/openmw/mwworld/asynctimer.hpp index d5fd473908..db8240899a 100644 --- a/apps/openmw/mwworld/asynctimer.hpp +++ b/apps/openmw/mwworld/asynctimer.hpp @@ -1,3 +1,6 @@ +#ifndef GAME_MWWORLD_ASYNCTIMER_H +#define GAME_MWWORLD_ASYNCTIMER_H + #include #include #include @@ -38,4 +41,5 @@ namespace MWWorld std::multiset mTasks; TaskId mNext_id = 0; }; -} \ No newline at end of file +} +#endif /* GAME_MWWORLD_ASYNCTIMER_H */ \ No newline at end of file From f472a043ad4c1df6a24f3be9e608b515dd364c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Sun, 24 Aug 2025 21:52:42 +0700 Subject: [PATCH 4/6] update_timer is connect to the main engine loop --- apps/openmw/engine.cpp | 6 ++++++ apps/openmw/engine.hpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2ebfdeb8ae..b83fd0686d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -69,6 +69,7 @@ #include "mwworld/class.hpp" #include "mwworld/datetimemanager.hpp" +#include "mwworld/asynctimer.hpp" #include "mwworld/worldimp.hpp" #include "mwrender/vismask.hpp" @@ -721,6 +722,9 @@ void OMW::Engine::prepareEngine() mStateManager = std::make_unique(mCfgMgr.getUserDataPath() / "saves", mContentFiles); mEnvironment.setStateManager(*mStateManager); + mAsyncTimer = std::make_unique(); + mEnvironment.setAsyncTimer(*mAsyncTimer); + const bool stereoEnabled = Settings::stereo().mStereoEnabled || osg::DisplaySettings::instance().get()->getStereo(); mStereoManager = std::make_unique( mViewer, stereoEnabled, Settings::camera().mNearClip, Settings::camera().mViewingDistance); @@ -1051,6 +1055,8 @@ void OMW::Engine::go() timeManager.setRenderingSimulationTime(timeManager.getRenderingSimulationTime() + dt); } + mAsyncTimer->update_timer(); + if (stats) { // The delay is required because rendering happens in parallel to the main thread and stats from there is diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 97b6a78ee9..467be8b5bc 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -91,6 +91,7 @@ namespace MWSound namespace MWWorld { class World; + class AsyncTimer; } namespace MWScript @@ -139,6 +140,7 @@ namespace OMW std::unique_ptr mJournal; std::unique_ptr mInputManager; std::unique_ptr mStateManager; + std::unique_ptr mAsyncTimer; std::unique_ptr mLuaManager; std::unique_ptr mLuaWorker; std::unique_ptr mL10nManager; From 66d03886cf66226b50a6d487fff6bdb3c9278638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Mon, 25 Aug 2025 12:43:07 +0700 Subject: [PATCH 5/6] refactor: normalize function naming convention --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwworld/asynctimer.cpp | 6 +++--- apps/openmw/mwworld/asynctimer.hpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b83fd0686d..a39ffd6bd0 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1055,7 +1055,7 @@ void OMW::Engine::go() timeManager.setRenderingSimulationTime(timeManager.getRenderingSimulationTime() + dt); } - mAsyncTimer->update_timer(); + mAsyncTimer->updateTimer(); if (stats) { diff --git a/apps/openmw/mwworld/asynctimer.cpp b/apps/openmw/mwworld/asynctimer.cpp index 7d7d39f390..104c459fd6 100644 --- a/apps/openmw/mwworld/asynctimer.cpp +++ b/apps/openmw/mwworld/asynctimer.cpp @@ -13,7 +13,7 @@ namespace MWWorld AsyncTimer::~AsyncTimer() = default; - void AsyncTimer::update_timer(){ + void AsyncTimer::updateTimer(){ auto time_now = std::chrono::steady_clock::now(); std::vector expired_callbacks; @@ -28,7 +28,7 @@ namespace MWWorld } } - AsyncTimer::TaskId AsyncTimer::set_task(float delay, Callback callback){ + AsyncTimer::TaskId AsyncTimer::setTask(float delay, Callback callback){ if(delay < 0 || !callback) return -1; auto triggerTime = std::chrono::steady_clock::now() + @@ -40,7 +40,7 @@ namespace MWWorld return new_id; } - void AsyncTimer::cancel_task(AsyncTimer::TaskId id){ + void AsyncTimer::cancelTask(AsyncTimer::TaskId id){ if(id < 0) return; for(auto it = mTasks.begin(); it != mTasks.end(); ++it){ diff --git a/apps/openmw/mwworld/asynctimer.hpp b/apps/openmw/mwworld/asynctimer.hpp index db8240899a..b2f44f957d 100644 --- a/apps/openmw/mwworld/asynctimer.hpp +++ b/apps/openmw/mwworld/asynctimer.hpp @@ -29,13 +29,13 @@ namespace MWWorld }; // @brief Updates timer state and executes due tasks - void update_timer(); + void updateTimer(); // @brief Schedules a new task for delayed execution - TaskId set_task(float delay, Callback callback); + TaskId setTask(float delay, Callback callback); // @brief Cancels a task by id - void cancel_task(TaskId id); + void cancelTask(TaskId id); private: std::multiset mTasks; From d34ff1b20e45714adff4c047c5232dc67ea96130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9D=D0=B0=D0=B9?= =?UTF-8?q?=D0=B1=D0=B0=D1=83=D1=8D=D1=80?= Date: Mon, 25 Aug 2025 23:25:06 +0700 Subject: [PATCH 6/6] update CMakeLists --- apps/openmw/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 9a5669c870..45fb5ecdaa 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -83,7 +83,7 @@ add_openmw_dir (mwworld store esmstore fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref weather projectilemanager cellpreloader datetimemanager groundcoverstore magiceffects cell ptrregistry - positioncellgrid + positioncellgrid asynctimer ) add_openmw_dir (mwphysics