diff --git a/CMakeLists.txt b/CMakeLists.txt index 76aede04c9..ca25fd05ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_RELEASE 0) -set(OPENMW_LUA_API_REVISION 55) +set(OPENMW_LUA_API_REVISION 56) set(OPENMW_POSTPROCESSING_API_REVISION 1) set(OPENMW_VERSION_COMMITHASH "") diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 4e16b396cd..256a11a0b6 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -112,13 +112,12 @@ namespace MWLua mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end()); LuaUtil::LuaStorage::initLuaBindings(mLua.sol()); - mGlobalScripts.addPackage( - "openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua.sol(), &mGlobalStorage)); + mGlobalScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua, &mGlobalStorage)); mMenuScripts.addPackage( - "openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage)); - mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua.sol(), &mGlobalStorage); + "openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua, &mGlobalStorage, &mPlayerStorage)); + mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua, &mGlobalStorage); mPlayerPackages["openmw.storage"] - = LuaUtil::LuaStorage::initPlayerPackage(mLua.sol(), &mGlobalStorage, &mPlayerStorage); + = LuaUtil::LuaStorage::initPlayerPackage(mLua, &mGlobalStorage, &mPlayerStorage); mPlayerStorage.setActive(true); mGlobalStorage.setActive(false); diff --git a/components/lua/storage.cpp b/components/lua/storage.cpp index dd53fdffcb..db81b6e172 100644 --- a/components/lua/storage.cpp +++ b/components/lua/storage.cpp @@ -17,6 +17,15 @@ namespace LuaUtil { LuaStorage::Value LuaStorage::Section::sEmpty; + void LuaStorage::registerLifeTime(LuaUtil::LuaState& luaState, sol::table& res) + { + res["LIFE_TIME"] = LuaUtil::makeStrictReadOnly(luaState.tableFromPairs({ + { "Persistent", Section::LifeTime::Persistent }, + { "GameSession", Section::LifeTime::GameSession }, + { "Temporary", Section::LifeTime::Temporary }, + })); + } + sol::object LuaStorage::Value::getCopy(lua_State* L) const { return deserialize(L, mSerializedValue); @@ -142,7 +151,12 @@ namespace LuaUtil sview["removeOnExit"] = [](const SectionView& section) { if (section.mReadOnly) throw std::runtime_error("Access to storage is read only"); - section.mSection->mPermanent = false; + section.mSection->mLifeTime = Section::Temporary; + }; + sview["setLifeTime"] = [](const SectionView& section, Section::LifeTime lifeTime) { + if (section.mReadOnly) + throw std::runtime_error("Access to storage is read only"); + section.mSection->mLifeTime = lifeTime; }; sview["set"] = [](const SectionView& section, std::string_view key, const sol::object& value) { if (section.mReadOnly) @@ -151,26 +165,33 @@ namespace LuaUtil }; } - sol::table LuaStorage::initGlobalPackage(lua_State* lua, LuaStorage* globalStorage) + sol::table LuaStorage::initGlobalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage) { - sol::table res(lua, sol::create); + sol::table res(luaState.sol(), sol::create); + registerLifeTime(luaState, res); + res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getMutableSection(section); }; res["allGlobalSections"] = [globalStorage]() { return globalStorage->getAllSections(); }; return LuaUtil::makeReadOnly(res); } - sol::table LuaStorage::initLocalPackage(lua_State* lua, LuaStorage* globalStorage) + sol::table LuaStorage::initLocalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage) { - sol::table res(lua, sol::create); + sol::table res(luaState.sol(), sol::create); + registerLifeTime(luaState, res); + res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; return LuaUtil::makeReadOnly(res); } - sol::table LuaStorage::initPlayerPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage) + sol::table LuaStorage::initPlayerPackage( + LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage) { - sol::table res(lua, sol::create); + sol::table res(luaState.sol(), sol::create); + registerLifeTime(luaState, res); + res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); }; res["playerSection"] @@ -179,9 +200,12 @@ namespace LuaUtil return LuaUtil::makeReadOnly(res); } - sol::table LuaStorage::initMenuPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage) + sol::table LuaStorage::initMenuPackage( + LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage) { - sol::table res(lua, sol::create); + sol::table res(luaState.sol(), sol::create); + registerLifeTime(luaState, res); + res["playerSection"] = [playerStorage](std::string_view section) { return playerStorage->getMutableSection(section, /*forMenuScripts=*/true); }; @@ -199,7 +223,7 @@ namespace LuaUtil it->second->mCallbacks.clear(); // Note that we don't clear menu callbacks for permanent sections // because starting/loading a game doesn't reset menu scripts. - if (!it->second->mPermanent) + if (it->second->mLifeTime == Section::Temporary) { it->second->mMenuScriptsCallbacks.clear(); it->second->mValues.clear(); @@ -238,7 +262,7 @@ namespace LuaUtil sol::table data(mLua, sol::create); for (const auto& [sectionName, section] : mData) { - if (section->mPermanent && !section->mValues.empty()) + if (section->mLifeTime == Section::Persistent && !section->mValues.empty()) data[sectionName] = section->asTable(); } std::string serializedData = serialize(data); diff --git a/components/lua/storage.hpp b/components/lua/storage.hpp index 75e0e14a16..061a5aace3 100644 --- a/components/lua/storage.hpp +++ b/components/lua/storage.hpp @@ -14,11 +14,13 @@ namespace LuaUtil class LuaStorage { public: - static void initLuaBindings(lua_State*); - static sol::table initGlobalPackage(lua_State* lua, LuaStorage* globalStorage); - static sol::table initLocalPackage(lua_State* lua, LuaStorage* globalStorage); - static sol::table initPlayerPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage); - static sol::table initMenuPackage(lua_State* lua, LuaStorage* globalStorage, LuaStorage* playerStorage); + static void initLuaBindings(lua_State* L); + static sol::table initGlobalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage); + static sol::table initLocalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage); + static sol::table initPlayerPackage( + LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage); + static sol::table initMenuPackage( + LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage); explicit LuaStorage(lua_State* lua) : mLua(lua) @@ -78,6 +80,13 @@ namespace LuaUtil struct Section { + enum LifeTime + { + Persistent, + GameSession, + Temporary + }; + explicit Section(LuaStorage* storage, std::string name) : mStorage(storage) , mSectionName(std::move(name)) @@ -96,7 +105,7 @@ namespace LuaUtil std::vector mCallbacks; std::vector mMenuScriptsCallbacks; // menu callbacks are in a separate vector because we don't // remove them in clear() - bool mPermanent = true; + LifeTime mLifeTime = Persistent; static Value sEmpty; void checkIfActive() const { mStorage->checkIfActive(); } @@ -120,6 +129,7 @@ namespace LuaUtil if (!mActive) throw std::logic_error("Trying to access inactive storage"); } + static void registerLifeTime(LuaUtil::LuaState& luaState, sol::table& res); }; } diff --git a/files/lua_api/openmw/storage.lua b/files/lua_api/openmw/storage.lua index 2335719be8..575b0f83d9 100644 --- a/files/lua_api/openmw/storage.lua +++ b/files/lua_api/openmw/storage.lua @@ -15,6 +15,15 @@ -- end -- end)) +--- Possible @{#LifeTime} values +-- @field [parent=#storage] #LifeTime LIFE_TIME + +--- `storage.LIFE_TIME` +-- @type LifeTime +-- @field #number Persistent "0" Data is stored for the whole game session and remains on disk after quitting the game +-- @field #number GameSession "1" Data is stored for the whole game session +-- @field #number Temporary "2" Data is stored until script context reset + --- -- Get a section of the global storage; can be used by any script, but only global scripts can change values. -- Menu scripts can only access it when a game is running. @@ -83,12 +92,28 @@ -- @param #table values (optional) New values --- --- Make the whole section temporary: will be removed on exit or when load a save. +-- (DEPRECATED, use `setLifeTime(openmw.storage.LIFE_TIME.Temporary)`) Make the whole section temporary: will be removed on exit or when load a save. -- Temporary sections have the same interface to get/set values, the only difference is they will not -- be saved to the permanent storage on exit. --- This function can not be used for a global storage section from a local script. +-- This function can be used for a global storage section from a global script or for a player storage section from a player or menu script. -- @function [parent=#StorageSection] removeOnExit -- @param self +-- @usage +-- local storage = require('openmw.storage') +-- local myModData = storage.globalSection('MyModExample') +-- myModData:removeOnExit() + +--- +-- Set the life time of given storage section. +-- New sections initially have a Persistent life time. +-- This function can be used for a global storage section from a global script or for a player storage section from a player or menu script. +-- @function [parent=#StorageSection] setLifeTime +-- @param self +-- @param #LifeTime lifeTime Section life time +-- @usage +-- local storage = require('openmw.storage') +-- local myModData = storage.globalSection('MyModExample') +-- myModData:setLifeTime(storage.LIFE_TIME.Temporary) --- -- Set value by a string key; can not be used for global storage from a local script.