Permanent storage for Lua data

psi29a-master-patch-54550
Petr Mikheev 2 years ago
parent 781b014183
commit a182fdeea1

@ -881,6 +881,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
}
mLuaManager->init();
mLuaManager->loadPermanentStorage(mCfgMgr.getUserConfigPath().string());
}
class OMW::Engine::LuaWorker
@ -1103,6 +1104,7 @@ void OMW::Engine::go()
// Save user settings
settings.saveUser(settingspath);
mLuaManager->savePermanentStorage(mCfgMgr.getUserConfigPath().string());
Log(Debug::Info) << "Quitting peacefully.";
}

@ -6,7 +6,9 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/store.hpp"
#include "eventqueue.hpp"
#include "worldview.hpp"
@ -47,7 +49,7 @@ namespace MWLua
{
auto* lua = context.mLua;
sol::table api(lua->sol(), sol::create);
api["API_REVISION"] = 13;
api["API_REVISION"] = 14;
api["quit"] = [lua]()
{
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
@ -85,6 +87,17 @@ namespace MWLua
{"Ammunition", MWWorld::InventoryStore::Slot_Ammunition}
}));
api["i18n"] = [i18n=context.mI18n](const std::string& context) { return i18n->getContext(context); };
const MWWorld::Store<ESM::GameSetting>* gmst = &MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
api["getGMST"] = [lua=context.mLua, gmst](const std::string& setting) -> sol::object
{
const ESM::Variant& value = gmst->find(setting)->mValue;
if (value.getType() == ESM::VT_String)
return sol::make_object<std::string>(lua->sol(), value.getString());
else if (value.getType() == ESM::VT_Int)
return sol::make_object<int>(lua->sol(), value.getInteger());
else
return sol::make_object<float>(lua->sol(), value.getFloat());
};
return LuaUtil::makeReadOnly(api);
}
@ -163,5 +176,29 @@ namespace MWLua
return LuaUtil::makeReadOnly(res);
}
sol::table initGlobalStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage)
{
sol::table res(context.mLua->sol(), sol::create);
res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getMutableSection(section); };
res["allGlobalSections"] = [globalStorage]() { return globalStorage->getAllSections(); };
return LuaUtil::makeReadOnly(res);
}
sol::table initLocalStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage)
{
sol::table res(context.mLua->sol(), sol::create);
res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
return LuaUtil::makeReadOnly(res);
}
sol::table initPlayerStoragePackage(const Context& context, LuaUtil::LuaStorage* globalStorage, LuaUtil::LuaStorage* playerStorage)
{
sol::table res(context.mLua->sol(), sol::create);
res["globalSection"] = [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
res["playerSection"] = [playerStorage](std::string_view section) { return playerStorage->getMutableSection(section); };
res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); };
return LuaUtil::makeReadOnly(res);
}
}

@ -4,6 +4,7 @@
#include <components/lua/luastate.hpp>
#include <components/lua/serialization.hpp>
#include <components/lua/scriptscontainer.hpp>
#include <components/lua/storage.hpp>
#include "context.hpp"
#include "eventqueue.hpp"
@ -25,6 +26,10 @@ namespace MWLua
sol::table initFieldGroup(const Context&, const QueryFieldGroup&);
sol::table initGlobalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
sol::table initLocalStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage);
sol::table initPlayerStoragePackage(const Context&, LuaUtil::LuaStorage* globalStorage, LuaUtil::LuaStorage* playerStorage);
// Implemented in nearbybindings.cpp
sol::table initNearbyPackage(const Context&);
@ -65,7 +70,6 @@ namespace MWLua
// Implemented in settingsbindings.cpp
sol::table initGlobalSettingsPackage(const Context&);
sol::table initLocalSettingsPackage(const Context&);
sol::table initPlayerSettingsPackage(const Context&);
// openmw.self package is implemented in localscripts.cpp

@ -1,5 +1,7 @@
#include "luamanagerimp.hpp"
#include <filesystem>
#include <components/debug/debuglog.hpp>
#include <components/esm/esmreader.hpp>
@ -69,6 +71,7 @@ namespace MWLua
initObjectBindingsForLocalScripts(localContext);
initCellBindingsForLocalScripts(localContext);
LocalScripts::initializeSelfPackage(localContext);
LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
mLua.addCommonPackage("openmw.async", getAsyncPackageInitializer(context));
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
@ -76,17 +79,37 @@ namespace MWLua
mLua.addCommonPackage("openmw.query", initQueryPackage(context));
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
mGlobalScripts.addPackage("openmw.settings", initGlobalSettingsPackage(context));
mGlobalScripts.addPackage("openmw.storage", initGlobalStoragePackage(context, &mGlobalStorage));
mCameraPackage = initCameraPackage(localContext);
mUserInterfacePackage = initUserInterfacePackage(localContext);
mInputPackage = initInputPackage(localContext);
mNearbyPackage = initNearbyPackage(localContext);
mLocalSettingsPackage = initLocalSettingsPackage(localContext);
mLocalSettingsPackage = initGlobalSettingsPackage(localContext);
mPlayerSettingsPackage = initPlayerSettingsPackage(localContext);
mLocalStoragePackage = initLocalStoragePackage(localContext, &mGlobalStorage);
mPlayerStoragePackage = initPlayerStoragePackage(localContext, &mGlobalStorage, &mPlayerStorage);
initConfiguration();
mInitialized = true;
}
void LuaManager::loadPermanentStorage(const std::string& userConfigPath)
{
auto globalPath = std::filesystem::path(userConfigPath) / "global_storage.bin";
auto playerPath = std::filesystem::path(userConfigPath) / "player_storage.bin";
if (std::filesystem::exists(globalPath))
mGlobalStorage.load(globalPath.string());
if (std::filesystem::exists(playerPath))
mPlayerStorage.load(playerPath.string());
}
void LuaManager::savePermanentStorage(const std::string& userConfigPath)
{
std::filesystem::path confDir(userConfigPath);
mGlobalStorage.save((confDir / "global_storage.bin").string());
mPlayerStorage.save((confDir / "player_storage.bin").string());
}
void LuaManager::update()
{
if (mPlayer.isEmpty())
@ -232,6 +255,8 @@ namespace MWLua
mPlayer = MWWorld::Ptr();
}
clearUserInterface();
mGlobalStorage.clearTemporary();
mPlayerStorage.clearTemporary();
}
void LuaManager::setupPlayer(const MWWorld::Ptr& ptr)
@ -346,11 +371,13 @@ namespace MWLua
scripts->addPackage("openmw.camera", mCameraPackage);
scripts->addPackage("openmw.input", mInputPackage);
scripts->addPackage("openmw.settings", mPlayerSettingsPackage);
scripts->addPackage("openmw.storage", mPlayerStoragePackage);
}
else
{
scripts = std::make_shared<LocalScripts>(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()), flag);
scripts->addPackage("openmw.settings", mLocalSettingsPackage);
scripts->addPackage("openmw.storage", mLocalStoragePackage);
}
scripts->addPackage("openmw.nearby", mNearbyPackage);
scripts->setSerializer(mLocalSerializer.get());

@ -4,8 +4,9 @@
#include <map>
#include <set>
#include <components/lua/luastate.hpp>
#include <components/lua/i18n.hpp>
#include <components/lua/luastate.hpp>
#include <components/lua/storage.hpp>
#include "../mwbase/luamanager.hpp"
@ -28,6 +29,9 @@ namespace MWLua
// Called by engine.cpp when the environment is fully initialized.
void init();
void loadPermanentStorage(const std::string& userConfigPath);
void savePermanentStorage(const std::string& userConfigPath);
// Called by engine.cpp every frame. For performance reasons it works in a separate
// thread (in parallel with osg Cull). Can not use scene graph.
void update();
@ -99,6 +103,8 @@ namespace MWLua
sol::table mInputPackage;
sol::table mLocalSettingsPackage;
sol::table mPlayerSettingsPackage;
sol::table mLocalStoragePackage;
sol::table mPlayerStoragePackage;
GlobalScripts mGlobalScripts{&mLua};
std::set<LocalScripts*> mActiveLocalScripts;
@ -139,6 +145,9 @@ namespace MWLua
std::vector<std::unique_ptr<Action>> mActionQueue;
std::unique_ptr<TeleportAction> mTeleportPlayerAction;
std::vector<std::string> mUIMessages;
LuaUtil::LuaStorage mGlobalStorage{mLua.sol()};
LuaUtil::LuaStorage mPlayerStorage{mLua.sol()};
};
}

@ -8,7 +8,7 @@
namespace MWLua
{
static sol::table initSettingsPackage(const Context& context, bool /*global*/, bool player)
static sol::table initSettingsPackage(const Context& context, bool player)
{
LuaUtil::LuaState* lua = context.mLua;
sol::table config(lua->sol(), sol::create);
@ -65,8 +65,7 @@ namespace MWLua
return LuaUtil::makeReadOnly(config);
}
sol::table initGlobalSettingsPackage(const Context& context) { return initSettingsPackage(context, true, false); }
sol::table initLocalSettingsPackage(const Context& context) { return initSettingsPackage(context, false, false); }
sol::table initPlayerSettingsPackage(const Context& context) { return initSettingsPackage(context, false, true); }
sol::table initGlobalSettingsPackage(const Context& context) { return initSettingsPackage(context, false); }
sol::table initPlayerSettingsPackage(const Context& context) { return initSettingsPackage(context, true); }
}

@ -24,6 +24,7 @@ if (GTEST_FOUND AND GMOCK_FOUND)
lua/test_querypackage.cpp
lua/test_configuration.cpp
lua/test_i18n.cpp
lua/test_storage.cpp
lua/test_ui_content.cpp

@ -0,0 +1,103 @@
#include <filesystem>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <components/lua/storage.hpp>
namespace
{
using namespace testing;
template <typename T>
T get(sol::state& lua, std::string luaCode)
{
return lua.safe_script("return " + luaCode).get<T>();
}
TEST(LuaUtilStorageTest, Basic)
{
sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test");
mLua.safe_script("mutable:set('x', 5)");
EXPECT_EQ(get<int>(mLua, "mutable:get('x')"), 5);
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 5);
EXPECT_FALSE(get<bool>(mLua, "mutable:wasChanged()"));
EXPECT_TRUE(get<bool>(mLua, "ro:wasChanged()"));
EXPECT_FALSE(get<bool>(mLua, "ro:wasChanged()"));
EXPECT_THROW(mLua.safe_script("ro:set('y', 3)"), std::exception);
mLua.safe_script("t1 = mutable:asTable()");
mLua.safe_script("t2 = ro:asTable()");
EXPECT_EQ(get<int>(mLua, "t1.x"), 5);
EXPECT_EQ(get<int>(mLua, "t2.x"), 5);
mLua.safe_script("mutable:reset()");
EXPECT_TRUE(get<bool>(mLua, "ro:get('x') == nil"));
mLua.safe_script("mutable:reset({x=4, y=7})");
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 4);
EXPECT_EQ(get<int>(mLua, "ro:get('y')"), 7);
EXPECT_FALSE(get<bool>(mLua, "mutable:wasChanged()"));
EXPECT_TRUE(get<bool>(mLua, "ro:wasChanged()"));
EXPECT_FALSE(get<bool>(mLua, "ro:wasChanged()"));
}
TEST(LuaUtilStorageTest, Table)
{
sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test");
mLua.safe_script("mutable:set('x', { y = 'abc', z = 7 })");
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7);
EXPECT_THROW(mLua.safe_script("mutable:get('x').z = 3"), std::exception);
EXPECT_NO_THROW(mLua.safe_script("mutable:getCopy('x').z = 3"));
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7);
EXPECT_EQ(get<int>(mLua, "ro:get('x').z"), 7);
EXPECT_EQ(get<std::string>(mLua, "ro:get('x').y"), "abc");
}
TEST(LuaUtilStorageTest, Saving)
{
sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary");
mLua.safe_script("temporary:removeOnExit()");
mLua.safe_script("permanent:set('x', 1)");
mLua.safe_script("temporary:set('y', 2)");
std::string tmpFile = (std::filesystem::temp_directory_path() / "test_storage.bin").string();
storage.save(tmpFile);
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_EQ(get<int>(mLua, "temporary:get('y')"), 2);
storage.clearTemporary();
mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary");
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil"));
mLua.safe_script("permanent:set('x', 3)");
mLua.safe_script("permanent:set('z', 4)");
LuaUtil::LuaStorage storage2(mLua);
mLua["permanent"] = storage2.getMutableSection("permanent");
mLua["temporary"] = storage2.getMutableSection("temporary");
storage2.load(tmpFile);
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(mLua, "permanent:get('z') == nil"));
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil"));
}
}

@ -29,7 +29,7 @@ endif (GIT_CHECKOUT)
# source files
add_component_dir (lua
luastate scriptscontainer utilpackage serialization configuration i18n
luastate scriptscontainer utilpackage serialization configuration i18n storage
)
add_component_dir (settings
@ -160,7 +160,7 @@ add_component_dir (fallback
add_component_dir (queries
query luabindings
)
add_component_dir (lua_ui
widget widgetlist element layers content
text textedit window

@ -0,0 +1,198 @@
#include "storage.hpp"
#include <filesystem>
#include <fstream>
#include <components/debug/debuglog.hpp>
namespace sol
{
template <>
struct is_automagical<LuaUtil::LuaStorage::SectionMutableView> : std::false_type {};
template <>
struct is_automagical<LuaUtil::LuaStorage::SectionReadOnlyView> : std::false_type {};
}
namespace LuaUtil
{
LuaStorage::Value LuaStorage::Section::sEmpty;
sol::object LuaStorage::Value::getCopy(lua_State* L) const
{
return deserialize(L, mSerializedValue);
}
sol::object LuaStorage::Value::getReadOnly(lua_State* L) const
{
if (mReadOnlyValue == sol::nil && !mSerializedValue.empty())
mReadOnlyValue = deserialize(L, mSerializedValue, nullptr, true);
return mReadOnlyValue;
}
const LuaStorage::Value& LuaStorage::Section::get(std::string_view key) const
{
auto it = mValues.find(key);
if (it != mValues.end())
return it->second;
else
return sEmpty;
}
void LuaStorage::Section::set(std::string_view key, const sol::object& value)
{
mValues[std::string(key)] = Value(value);
mChangeCounter++;
if (mStorage->mListener)
(*mStorage->mListener)(mSectionName, key, value);
}
bool LuaStorage::Section::wasChanged(int64_t& lastCheck)
{
bool res = lastCheck < mChangeCounter;
lastCheck = mChangeCounter;
return res;
}
sol::table LuaStorage::Section::asTable()
{
sol::table res(mStorage->mLua, sol::create);
for (const auto& [k, v] : mValues)
res[k] = v.getCopy(mStorage->mLua);
return res;
}
void LuaStorage::initLuaBindings(lua_State* L)
{
sol::state_view lua(L);
sol::usertype<SectionReadOnlyView> roView = lua.new_usertype<SectionReadOnlyView>("ReadOnlySection");
sol::usertype<SectionMutableView> mutableView = lua.new_usertype<SectionMutableView>("MutableSection");
roView["get"] = [](sol::this_state s, SectionReadOnlyView& section, std::string_view key)
{
return section.mSection->get(key).getReadOnly(s);
};
roView["getCopy"] = [](sol::this_state s, SectionReadOnlyView& section, std::string_view key)
{
return section.mSection->get(key).getCopy(s);
};
roView["wasChanged"] = [](SectionReadOnlyView& section) { return section.mSection->wasChanged(section.mLastCheck); };
roView["asTable"] = [](SectionReadOnlyView& section) { return section.mSection->asTable(); };
mutableView["get"] = [](sol::this_state s, SectionMutableView& section, std::string_view key)
{
return section.mSection->get(key).getReadOnly(s);
};
mutableView["getCopy"] = [](sol::this_state s, SectionMutableView& section, std::string_view key)
{
return section.mSection->get(key).getCopy(s);
};
mutableView["wasChanged"] = [](SectionMutableView& section) { return section.mSection->wasChanged(section.mLastCheck); };
mutableView["asTable"] = [](SectionMutableView& section) { return section.mSection->asTable(); };
mutableView["reset"] = [](SectionMutableView& section, sol::optional<sol::table> newValues)
{
section.mSection->mValues.clear();
if (newValues)
{
for (const auto& [k, v] : *newValues)
{
try
{
section.mSection->set(k.as<std::string_view>(), v);
}
catch (std::exception& e)
{
Log(Debug::Error) << "LuaUtil::LuaStorage::Section::reset(table): " << e.what();
}
}
}
section.mSection->mChangeCounter++;
section.mLastCheck = section.mSection->mChangeCounter;
};
mutableView["removeOnExit"] = [](SectionMutableView& section) { section.mSection->mPermanent = false; };
mutableView["set"] = [](SectionMutableView& section, std::string_view key, const sol::object& value)
{
if (section.mLastCheck == section.mSection->mChangeCounter)
section.mLastCheck++;
section.mSection->set(key, value);
};
}
void LuaStorage::clearTemporary()
{
auto it = mData.begin();
while (it != mData.end())
{
if (!it->second->mPermanent)
it = mData.erase(it);
else
++it;
}
}
void LuaStorage::load(const std::string& path)
{
mData.clear();
try
{
Log(Debug::Info) << "Loading Lua storage \"" << path << "\" (" << std::filesystem::file_size(path) << " bytes)";
std::ifstream fin(path, std::fstream::binary);
std::string serializedData((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
sol::table data = deserialize(mLua, serializedData);
for (const auto& [sectionName, sectionTable] : data)
{
Section* section = getSection(sectionName.as<std::string_view>());
for (const auto& [key, value] : sol::table(sectionTable))
section->set(key.as<std::string_view>(), value);
}
}
catch (std::exception& e)
{
Log(Debug::Error) << "Can not read \"" << path << "\": " << e.what();
}
}
void LuaStorage::save(const std::string& path) const
{
sol::table data(mLua, sol::create);
for (const auto& [sectionName, section] : mData)
{
if (section->mPermanent)
data[sectionName] = section->asTable();
}
std::string serializedData = serialize(data);
Log(Debug::Info) << "Saving Lua storage \"" << path << "\" (" << serializedData.size() << " bytes)";
std::ofstream fout(path, std::fstream::binary);
fout.write(serializedData.data(), serializedData.size());
fout.close();
}
LuaStorage::Section* LuaStorage::getSection(std::string_view sectionName)
{
auto it = mData.find(sectionName);
if (it != mData.end())
return it->second.get();
auto section = std::make_unique<Section>(this, std::string(sectionName));
sectionName = section->mSectionName;
auto [newIt, _] = mData.emplace(sectionName, std::move(section));
return newIt->second.get();
}
sol::object LuaStorage::getReadOnlySection(std::string_view sectionName)
{
Section* section = getSection(sectionName);
return sol::make_object<SectionReadOnlyView>(mLua, SectionReadOnlyView{section, section->mChangeCounter});
}
sol::object LuaStorage::getMutableSection(std::string_view sectionName)
{
Section* section = getSection(sectionName);
return sol::make_object<SectionMutableView>(mLua, SectionMutableView{section, section->mChangeCounter});
}
sol::table LuaStorage::getAllSections()
{
sol::table res(mLua, sol::create);
for (const auto& [sectionName, _] : mData)
res[sectionName] = getMutableSection(sectionName);
return res;
}
}

@ -0,0 +1,81 @@
#ifndef COMPONENTS_LUA_STORAGE_H
#define COMPONENTS_LUA_STORAGE_H
#include <map>
#include <sol/sol.hpp>
#include "serialization.hpp"
namespace LuaUtil
{
class LuaStorage
{
public:
static void initLuaBindings(lua_State*);
explicit LuaStorage(lua_State* lua) : mLua(lua) {}
void clearTemporary();
void load(const std::string& path);
void save(const std::string& path) const;
sol::object getReadOnlySection(std::string_view sectionName);
sol::object getMutableSection(std::string_view sectionName);
sol::table getAllSections();
void set(std::string_view section, std::string_view key, const sol::object& value) { getSection(section)->set(key, value); }
using ListenerFn = std::function<void(std::string_view, std::string_view, const sol::object&)>;
void setListener(ListenerFn fn) { mListener = std::move(fn); }
private:
class Value
{
public:
Value() {}
Value(const sol::object& value) : mSerializedValue(serialize(value)) {}
sol::object getCopy(lua_State* L) const;
sol::object getReadOnly(lua_State* L) const;
private:
std::string mSerializedValue;
mutable sol::object mReadOnlyValue = sol::nil;
};
struct Section
{
explicit Section(LuaStorage* storage, std::string name) : mStorage(storage), mSectionName(std::move(name)) {}
const Value& get(std::string_view key) const;
void set(std::string_view key, const sol::object& value);
bool wasChanged(int64_t& lastCheck);
sol::table asTable();
LuaStorage* mStorage;
std::string mSectionName;
std::map<std::string, Value, std::less<>> mValues;
bool mPermanent = true;
int64_t mChangeCounter = 0;
static Value sEmpty;
};
struct SectionMutableView
{
Section* mSection = nullptr;
int64_t mLastCheck = 0;
};
struct SectionReadOnlyView
{
Section* mSection = nullptr;
int64_t mLastCheck = 0;
};
Section* getSection(std::string_view sectionName);
lua_State* mLua;
std::map<std::string_view, std::unique_ptr<Section>> mData;
std::optional<ListenerFn> mListener;
};
}
#endif // COMPONENTS_LUA_STORAGE_H

@ -8,7 +8,7 @@ Lua API reference
engine_handlers
user_interface
openmw_util
openmw_settings
openmw_storage
openmw_core
openmw_async
openmw_query
@ -45,8 +45,8 @@ Player scripts are local scripts that are attached to a player.
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
| | | | that don't depend on the game world. |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.settings <Package openmw.settings>` | everywhere | | Access to GMST records in content files (implemented) and |
| | | | to mod settings (not implemented). |
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
| | | | between game sessions. |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+

@ -1,5 +0,0 @@
Package openmw.settings
=======================
.. raw:: html
:file: generated_html/openmw_settings.html

@ -0,0 +1,5 @@
Package openmw.storage
======================
.. raw:: html
:file: generated_html/openmw_storage.html

@ -334,8 +334,8 @@ Player scripts are local scripts that are attached to a player.
|:ref:`openmw.util <Package openmw.util>` | everywhere | | Defines utility functions and classes like 3D vectors, |
| | | | that don't depend on the game world. |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.settings <Package openmw.settings>` | everywhere | | Access to GMST records in content files (implemented) and |
| | | | to mod settings (not implemented). |
|:ref:`openmw.storage <Package openmw.storage>` | everywhere | | Storage API. In particular can be used to store data |
| | | | between game sessions. |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+
|:ref:`openmw.core <Package openmw.core>` | everywhere | | Functions that are common for both global and local scripts |
+---------------------------------------------------------+--------------------+---------------------------------------------------------------+

@ -46,6 +46,12 @@
-- @function [parent=#core] isWorldPaused
-- @return #boolean
-------------------------------------------------------------------------------
-- Get a GMST setting from content files.
-- @function [parent=#core] getGMST
-- @param #string setting Setting name
-- @return #any
-------------------------------------------------------------------------------
-- Return i18n formatting function for the given context.
-- It is based on `i18n.lua` library.

@ -1,14 +0,0 @@
-------------------------------------------------------------------------------
-- `openmw.settings` provides read-only access to GMST records in content files.
-- @module settings
-- @usage
-- local settings = require('openmw.settings')
-------------------------------------------------------------------------------
-- Get a GMST setting from content files.
-- @function [parent=#settings] getGMST
-- @param #string setting
return nil

@ -0,0 +1,96 @@
---
-- `openmw.storage` contains functions to work with permanent Lua storage.
-- @module storage
-- @usage
-- local storage = require('openmw.storage')
-- local myModData = storage.globalSection('MyModExample')
-- myModData:set("someVariable", 1.0)
-- myModData:set("anotherVariable", { exampleStr='abc', exampleBool=true })
-- local function update()
-- if myModCfg:checkChanged() then
-- print('Data was changes by another script:')
-- print('MyModExample.someVariable =', myModData:get('someVariable'))
-- print('MyModExample.anotherVariable.exampleStr =',
-- myModData:get('anotherVariable').exampleStr)
-- end
-- end
---
-- Get a section of the global storage; can be used by any script, but only global scripts can change values.
-- Creates the section if it doesn't exist.
-- @function [parent=#storage] globalSection
-- @param #string sectionName
-- @return #StorageSection
---
-- Get a section of the player storage; can be used by player scripts only.
-- Creates the section if it doesn't exist.
-- @function [parent=#storage] playerSection
-- @param #string sectionName
-- @return #StorageSection
---
-- Get all global sections as a table; can be used by global scripts only.
-- Note that adding/removing items to the returned table doesn't create or remove sections.
-- @function [parent=#storage] allGlobalSections
-- @return #table
---
-- Get all global sections as a table; can be used by player scripts only.
-- Note that adding/removing items to the returned table doesn't create or remove sections.
-- @function [parent=#storage] allPlayerSections
-- @return #table
---
-- A map `key -> value` that represents a storage section.
-- @type StorageSection
---
-- Get value by a string key; if value is a table makes it readonly.
-- @function [parent=#StorageSection] get
-- @param self
-- @param #string key
---
-- Get value by a string key; if value is a table returns a copy.
-- @function [parent=#StorageSection] getCopy
-- @param self
-- @param #string key
---
-- Return `True` if any value in this section was changed by another script since the last `wasChanged`.
-- @function [parent=#StorageSection] wasChanged
-- @param self
-- @return #boolean
---
-- Copy all values and return them as a table.
-- @function [parent=#StorageSection] asTable
-- @param self
-- @return #table
---
-- Remove all existing values and assign values from given (the arg is optional) table.
-- Note: `section:reset()` removes all values, but not the section itself. Use `section:removeOnExit()` to remove the section completely.
-- @function [parent=#StorageSection] reset
-- @param self
-- @param #table values (optional) New values
---
-- Make the whole section temporary: will be removed on exit or when load a save.
-- No section can be removed immediately because other scripts may use it at the moment.
-- 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.
-- @function [parent=#StorageSection] removeOnExit
-- @param self
---
-- Set value by a string key; can not be used for global storage from a local script.
-- @function [parent=#StorageSection] set
-- @param self
-- @param #string key
-- @param #any value
return nil
Loading…
Cancel
Save