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_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 "")

View File

@ -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<PlayerScripts*>(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()

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
// 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);
// Saving
@ -174,6 +174,8 @@ namespace MWLua
void sendLocalEvent(
const MWWorld::Ptr& target, const std::string& name, const std::optional<sol::table>& 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;

View File

@ -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<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();
const MWState::Character* character = manager->getCurrentCharacter();
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();
}
@ -408,7 +408,7 @@ namespace LuaUtil
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;
return;

View File

@ -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
{