diff --git a/CMakeLists.txt b/CMakeLists.txt index 834668e92a..63ed9edfe1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 95) +set(OPENMW_LUA_API_REVISION 96) set(OPENMW_POSTPROCESSING_API_REVISION 3) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 0b760ccdf0..c0146234f5 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -42,6 +42,20 @@ namespace MWLua { + namespace + { + struct BoolScopeGuard + { + bool& mValue; + BoolScopeGuard(bool& value) + : mValue(value) + { + mValue = true; + } + + ~BoolScopeGuard() { mValue = false; } + }; + } static LuaUtil::LuaStateSettings createLuaStateSettings() { @@ -264,31 +278,33 @@ namespace MWLua // can teleport the player to the starting location before the first frame is rendered. mGlobalScripts.newGameStarted(); } + BoolScopeGuard updateGuard(mRunningSynchronizedUpdates); - // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. - mProcessingInputEvents = true; + MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); PlayerScripts* playerScripts = mPlayer.isEmpty() ? nullptr : dynamic_cast(mPlayer.getRefData().getLuaScripts()); - MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); - - for (const auto& event : mMenuInputEvents) - mMenuScripts.processInputEvent(event); - mMenuInputEvents.clear(); - if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu)) + // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. { - for (const auto& event : mInputEvents) - playerScripts->processInputEvent(event); + BoolScopeGuard processingGuard(mProcessingInputEvents); + + for (const auto& event : mMenuInputEvents) + mMenuScripts.processInputEvent(event); + mMenuInputEvents.clear(); + if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu)) + { + for (const auto& event : mInputEvents) + playerScripts->processInputEvent(event); + } + mInputEvents.clear(); + mLuaEvents.callMenuEventHandlers(); + double frameDuration = MWBase::Environment::get().getWorld()->getTimeManager()->isPaused() + ? 0.0 + : MWBase::Environment::get().getFrameDuration(); + mInputActions.update(frameDuration); + mMenuScripts.onFrame(frameDuration); + if (playerScripts) + playerScripts->onFrame(frameDuration); } - mInputEvents.clear(); - mLuaEvents.callMenuEventHandlers(); - double frameDuration = MWBase::Environment::get().getWorld()->getTimeManager()->isPaused() - ? 0.0 - : MWBase::Environment::get().getFrameDuration(); - mInputActions.update(frameDuration); - mMenuScripts.onFrame(frameDuration); - if (playerScripts) - playerScripts->onFrame(frameDuration); - mProcessingInputEvents = false; for (const auto& [message, mode] : mUIMessages) windowManager->messageBox(message, mode); @@ -316,7 +332,7 @@ namespace MWLua void LuaManager::applyDelayedActions() { - mApplyingDelayedActions = true; + BoolScopeGuard applyingGuard(mApplyingDelayedActions); for (DelayedAction& action : mActionQueue) action.apply(); mActionQueue.clear(); @@ -324,7 +340,6 @@ namespace MWLua if (mTeleportPlayerAction) mTeleportPlayerAction->apply(); mTeleportPlayerAction.reset(); - mApplyingDelayedActions = false; } void LuaManager::clear() diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index 42b18d236f..80c3163c80 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -125,7 +125,7 @@ namespace MWLua // Some changes to the game world can not be done from the scripting thread (because it runs in parallel with // OSG Cull), so we need to queue it and apply from the main thread. - void addAction(std::function action, std::string_view name = ""); + void addAction(std::function action, std::string_view name = {}); void addTeleportPlayerAction(std::function action); // Saving @@ -174,6 +174,8 @@ namespace MWLua void sendLocalEvent( const MWWorld::Ptr& target, const std::string& name, const std::optional& data = std::nullopt); + bool isSynchronizedUpdateRunning() const { return mRunningSynchronizedUpdates; } + private: void initConfiguration(); LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, @@ -187,6 +189,7 @@ namespace MWLua bool mApplyingDelayedActions = false; bool mNewGameStarted = false; bool mReloadAllScriptsRequested = false; + bool mRunningSynchronizedUpdates = false; LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::LuaState mLua; LuaUi::ResourceManager mUiResourceManager; diff --git a/apps/openmw/mwlua/menuscripts.cpp b/apps/openmw/mwlua/menuscripts.cpp index 6387a3dce1..c9134201d3 100644 --- a/apps/openmw/mwlua/menuscripts.cpp +++ b/apps/openmw/mwlua/menuscripts.cpp @@ -8,6 +8,7 @@ #include "../mwstate/character.hpp" #include "context.hpp" +#include "luamanagerimp.hpp" namespace MWLua { @@ -72,7 +73,9 @@ namespace MWLua return sol::nullopt; }; - api["saveGame"] = [](std::string_view description, sol::optional slotName) { + api["saveGame"] = [context](std::string_view description, sol::optional slotName) { + if (!context.mLuaManager->isSynchronizedUpdateRunning()) + throw std::runtime_error("menu.saveGame can only be used during engine or event handler processing"); MWBase::StateManager* manager = MWBase::Environment::get().getStateManager(); const MWState::Character* character = manager->getCurrentCharacter(); const MWState::Slot* slot = nullptr; diff --git a/components/lua/scriptscontainer.cpp b/components/lua/scriptscontainer.cpp index f5b5605e37..36bdbca8b1 100644 --- a/components/lua/scriptscontainer.cpp +++ b/components/lua/scriptscontainer.cpp @@ -47,7 +47,7 @@ namespace LuaUtil } } - void ScriptsContainer::printError(int scriptId, std::string_view msg, const std::exception& e) + void ScriptsContainer::printError(int scriptId, std::string_view msg, const std::exception& e) const { Log(Debug::Error) << mNamePrefix << "[" << scriptPath(scriptId) << "] " << msg << ": " << e.what(); } @@ -408,7 +408,7 @@ namespace LuaUtil void ScriptsContainer::save(ESM::LuaScripts& data) { - if (UnloadedData* unloadedData = std::get_if(&mData)) + if (const UnloadedData* unloadedData = std::get_if(&mData)) { data.mScripts = unloadedData->mScripts; return; diff --git a/components/lua/scriptscontainer.hpp b/components/lua/scriptscontainer.hpp index 8eaaf2955f..275c300ac9 100644 --- a/components/lua/scriptscontainer.hpp +++ b/components/lua/scriptscontainer.hpp @@ -271,7 +271,7 @@ namespace LuaUtil // Returns script by id (throws an exception if doesn't exist) Script& getScript(int scriptId); - void printError(int scriptId, std::string_view msg, const std::exception& e); + void printError(int scriptId, std::string_view msg, const std::exception& e) const; const VFS::Path::Normalized& scriptPath(int scriptId) const {