Only allow saving in synchronizedUpdateUnsafe

This commit is contained in:
Evil Eye 2025-09-16 22:02:23 +02:00
parent 32f54eb555
commit 6d5da282a5
5 changed files with 30 additions and 25 deletions

View File

@ -42,6 +42,20 @@
namespace MWLua namespace MWLua
{ {
namespace
{
struct AllowSaving
{
bool& mAllowSaving;
AllowSaving(bool& allowSaving)
: mAllowSaving(allowSaving)
{
mAllowSaving = true;
}
~AllowSaving() { mAllowSaving = false; }
};
}
static LuaUtil::LuaStateSettings createLuaStateSettings() static LuaUtil::LuaStateSettings createLuaStateSettings()
{ {
@ -264,6 +278,7 @@ 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();
} }
AllowSaving savingGuard(mAllowSaving);
// We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency.
mProcessingInputEvents = true; mProcessingInputEvents = true;
@ -316,8 +331,6 @@ namespace MWLua
void LuaManager::applyDelayedActions() void LuaManager::applyDelayedActions()
{ {
if (mApplyingDelayedActions)
return;
mApplyingDelayedActions = true; mApplyingDelayedActions = true;
for (DelayedAction& action : mActionQueue) for (DelayedAction& action : mActionQueue)
action.apply(); action.apply();
@ -326,9 +339,6 @@ namespace MWLua
if (mTeleportPlayerAction) if (mTeleportPlayerAction)
mTeleportPlayerAction->apply(); mTeleportPlayerAction->apply();
mTeleportPlayerAction.reset(); mTeleportPlayerAction.reset();
for (DelayedAction& action : mSaveActionQueue)
action.apply();
mSaveActionQueue.clear();
mApplyingDelayedActions = false; mApplyingDelayedActions = false;
} }
@ -829,11 +839,6 @@ namespace MWLua
mTeleportPlayerAction = DelayedAction(&mLua, std::move(action), "TeleportPlayer"); mTeleportPlayerAction = DelayedAction(&mLua, std::move(action), "TeleportPlayer");
} }
void LuaManager::addSaveGameAction(std::function<void()> action)
{
mSaveActionQueue.emplace_back(&mLua, std::move(action), "SaveGame");
}
void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const
{ {
stats.setAttribute(frameNumber, "Lua UsedMemory", mLua.getTotalMemoryUsage()); stats.setAttribute(frameNumber, "Lua UsedMemory", mLua.getTotalMemoryUsage());

View File

@ -127,7 +127,6 @@ namespace MWLua
// 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);
void addSaveGameAction(std::function<void()> action);
// Saving // Saving
void write(ESM::ESMWriter& writer, Loading::Listener& progress) override; void write(ESM::ESMWriter& writer, Loading::Listener& progress) override;
@ -175,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 savingAllowed() const { return mAllowSaving; }
private: private:
void initConfiguration(); void initConfiguration();
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr,
@ -188,6 +189,7 @@ namespace MWLua
bool mApplyingDelayedActions = false; bool mApplyingDelayedActions = false;
bool mNewGameStarted = false; bool mNewGameStarted = false;
bool mReloadAllScriptsRequested = false; bool mReloadAllScriptsRequested = false;
bool mAllowSaving = false;
LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::ScriptsConfiguration mConfiguration;
LuaUtil::LuaState mLua; LuaUtil::LuaState mLua;
LuaUi::ResourceManager mUiResourceManager; LuaUi::ResourceManager mUiResourceManager;
@ -235,7 +237,6 @@ namespace MWLua
}; };
std::vector<DelayedAction> mActionQueue; std::vector<DelayedAction> mActionQueue;
std::optional<DelayedAction> mTeleportPlayerAction; std::optional<DelayedAction> mTeleportPlayerAction;
std::vector<DelayedAction> mSaveActionQueue;
std::vector<std::pair<std::string, MWGui::ShowInDialogueMode>> mUIMessages; std::vector<std::pair<std::string, MWGui::ShowInDialogueMode>> mUIMessages;
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages; std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;
std::optional<ObjectId> mDelayedUiModeChangedArg; std::optional<ObjectId> mDelayedUiModeChangedArg;

View File

@ -73,16 +73,15 @@ namespace MWLua
return sol::nullopt; return sol::nullopt;
}; };
api["saveGame"] = [context](std::string description, sol::optional<std::string> slotName) { api["saveGame"] = [context](std::string_view description, sol::optional<std::string_view> slotName) {
context.mLuaManager->addSaveGameAction( if (!context.mLuaManager->savingAllowed())
[description = std::move(description), slotName = std::move(slotName)]() { throw std::runtime_error("The game cannot be saved at the moment");
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;
if (slotName) if (slotName)
slot = findSlot(character, *slotName); slot = findSlot(character, *slotName);
manager->saveGame(description, slot); manager->saveGame(description, slot);
});
}; };
auto getSaves = [](sol::state_view currentState, const MWState::Character& character) { auto getSaves = [](sol::state_view currentState, const MWState::Character& character) {

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