Merge branch 'saveaction' into 'master'

Turn menu.saveGame into a delayed action

See merge request OpenMW/openmw!4924
This commit is contained in:
Evil Eye 2025-09-20 08:18:19 +00:00
commit ad07a7d2f3
6 changed files with 49 additions and 28 deletions

View File

@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 50) set(OPENMW_VERSION_MINOR 50)
set(OPENMW_VERSION_RELEASE 0) 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_POSTPROCESSING_API_REVISION 3)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")

View File

@ -42,6 +42,20 @@
namespace MWLua namespace MWLua
{ {
namespace
{
struct BoolScopeGuard
{
bool& mValue;
BoolScopeGuard(bool& value)
: mValue(value)
{
mValue = true;
}
~BoolScopeGuard() { mValue = false; }
};
}
static LuaUtil::LuaStateSettings createLuaStateSettings() static LuaUtil::LuaStateSettings createLuaStateSettings()
{ {
@ -264,31 +278,33 @@ namespace MWLua
// can teleport the player to the starting location before the first frame is rendered. // can teleport the player to the starting location before the first frame is rendered.
mGlobalScripts.newGameStarted(); mGlobalScripts.newGameStarted();
} }
BoolScopeGuard updateGuard(mRunningSynchronizedUpdates);
// We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
mProcessingInputEvents = true;
PlayerScripts* playerScripts PlayerScripts* playerScripts
= mPlayer.isEmpty() ? nullptr : dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts()); = mPlayer.isEmpty() ? nullptr : dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency.
for (const auto& event : mMenuInputEvents)
mMenuScripts.processInputEvent(event);
mMenuInputEvents.clear();
if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu))
{ {
for (const auto& event : mInputEvents) BoolScopeGuard processingGuard(mProcessingInputEvents);
playerScripts->processInputEvent(event);
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) for (const auto& [message, mode] : mUIMessages)
windowManager->messageBox(message, mode); windowManager->messageBox(message, mode);
@ -316,7 +332,7 @@ namespace MWLua
void LuaManager::applyDelayedActions() void LuaManager::applyDelayedActions()
{ {
mApplyingDelayedActions = true; BoolScopeGuard applyingGuard(mApplyingDelayedActions);
for (DelayedAction& action : mActionQueue) for (DelayedAction& action : mActionQueue)
action.apply(); action.apply();
mActionQueue.clear(); mActionQueue.clear();
@ -324,7 +340,6 @@ namespace MWLua
if (mTeleportPlayerAction) if (mTeleportPlayerAction)
mTeleportPlayerAction->apply(); mTeleportPlayerAction->apply();
mTeleportPlayerAction.reset(); mTeleportPlayerAction.reset();
mApplyingDelayedActions = false;
} }
void LuaManager::clear() void LuaManager::clear()

View File

@ -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 // 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. // OSG Cull), so we need to queue it and apply from the main thread.
void addAction(std::function<void()> action, std::string_view name = ""); void addAction(std::function<void()> action, std::string_view name = {});
void addTeleportPlayerAction(std::function<void()> action); void addTeleportPlayerAction(std::function<void()> action);
// Saving // Saving
@ -174,6 +174,8 @@ namespace MWLua
void sendLocalEvent( void sendLocalEvent(
const MWWorld::Ptr& target, const std::string& name, const std::optional<sol::table>& data = std::nullopt); const MWWorld::Ptr& target, const std::string& name, const std::optional<sol::table>& data = std::nullopt);
bool isSynchronizedUpdateRunning() const { return mRunningSynchronizedUpdates; }
private: private:
void initConfiguration(); void initConfiguration();
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr,
@ -187,6 +189,7 @@ namespace MWLua
bool mApplyingDelayedActions = false; bool mApplyingDelayedActions = false;
bool mNewGameStarted = false; bool mNewGameStarted = false;
bool mReloadAllScriptsRequested = false; bool mReloadAllScriptsRequested = false;
bool mRunningSynchronizedUpdates = false;
LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::ScriptsConfiguration mConfiguration;
LuaUtil::LuaState mLua; LuaUtil::LuaState mLua;
LuaUi::ResourceManager mUiResourceManager; LuaUi::ResourceManager mUiResourceManager;

View File

@ -8,6 +8,7 @@
#include "../mwstate/character.hpp" #include "../mwstate/character.hpp"
#include "context.hpp" #include "context.hpp"
#include "luamanagerimp.hpp"
namespace MWLua namespace MWLua
{ {
@ -72,7 +73,9 @@ namespace MWLua
return sol::nullopt; return sol::nullopt;
}; };
api["saveGame"] = [](std::string_view description, sol::optional<std::string_view> slotName) { api["saveGame"] = [context](std::string_view description, sol::optional<std::string_view> 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(); MWBase::StateManager* manager = MWBase::Environment::get().getStateManager();
const MWState::Character* character = manager->getCurrentCharacter(); const MWState::Character* character = manager->getCurrentCharacter();
const MWState::Slot* slot = nullptr; const MWState::Slot* slot = nullptr;

View File

@ -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(); Log(Debug::Error) << mNamePrefix << "[" << scriptPath(scriptId) << "] " << msg << ": " << e.what();
} }
@ -408,7 +408,7 @@ namespace LuaUtil
void ScriptsContainer::save(ESM::LuaScripts& data) void ScriptsContainer::save(ESM::LuaScripts& data)
{ {
if (UnloadedData* unloadedData = std::get_if<UnloadedData>(&mData)) if (const UnloadedData* unloadedData = std::get_if<UnloadedData>(&mData))
{ {
data.mScripts = unloadedData->mScripts; data.mScripts = unloadedData->mScripts;
return; return;

View File

@ -271,7 +271,7 @@ namespace LuaUtil
// Returns script by id (throws an exception if doesn't exist) // Returns script by id (throws an exception if doesn't exist)
Script& getScript(int scriptId); 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 const VFS::Path::Normalized& scriptPath(int scriptId) const
{ {