1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-21 08:23:53 +00:00
openmw/components/lua/storage.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

221 lines
8 KiB
C++
Raw Normal View History

2021-12-13 23:39:01 +00:00
#include "storage.hpp"
#include <filesystem>
#include <fstream>
#include <components/debug/debuglog.hpp>
namespace sol
{
template <>
2022-04-23 13:37:08 +00:00
struct is_automagical<LuaUtil::LuaStorage::SectionView> : std::false_type
{
};
2021-12-13 23:39:01 +00:00
}
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;
}
2022-04-23 13:37:08 +00:00
void LuaStorage::Section::runCallbacks(sol::optional<std::string_view> changedKey)
{
2022-05-19 19:49:14 +00:00
mStorage->mRunningCallbacks.insert(this);
2022-04-23 13:37:08 +00:00
mCallbacks.erase(std::remove_if(mCallbacks.begin(), mCallbacks.end(),
[&](const Callback& callback) {
bool valid = callback.isValid();
if (valid)
callback.tryCall(mSectionName, changedKey);
return !valid;
}),
mCallbacks.end());
2022-05-19 19:49:14 +00:00
mStorage->mRunningCallbacks.erase(this);
}
void LuaStorage::Section::throwIfCallbackRecursionIsTooDeep()
{
if (mStorage->mRunningCallbacks.count(this) > 0)
throw std::runtime_error(
"Storage handler shouldn't change the storage section it handles (leads to an infinite recursion)");
if (mStorage->mRunningCallbacks.size() > 10)
throw std::runtime_error(
"Too many subscribe callbacks triggering in a chain, likely an infinite recursion");
2022-04-23 13:37:08 +00:00
}
2021-12-13 23:39:01 +00:00
void LuaStorage::Section::set(std::string_view key, const sol::object& value)
{
2022-05-19 19:49:14 +00:00
throwIfCallbackRecursionIsTooDeep();
2022-04-23 13:37:08 +00:00
if (value != sol::nil)
mValues[std::string(key)] = Value(value);
else
{
auto it = mValues.find(key);
if (it != mValues.end())
mValues.erase(it);
}
2021-12-13 23:39:01 +00:00
if (mStorage->mListener)
2022-04-23 13:37:08 +00:00
mStorage->mListener->valueChanged(mSectionName, key, value);
runCallbacks(key);
2021-12-13 23:39:01 +00:00
}
2022-04-23 13:37:08 +00:00
void LuaStorage::Section::setAll(const sol::optional<sol::table>& values)
2021-12-13 23:39:01 +00:00
{
2022-05-19 19:49:14 +00:00
throwIfCallbackRecursionIsTooDeep();
2022-04-23 13:37:08 +00:00
mValues.clear();
if (values)
{
for (const auto& [k, v] : *values)
mValues[k.as<std::string>()] = Value(v);
}
if (mStorage->mListener)
mStorage->mListener->sectionReplaced(mSectionName, values);
runCallbacks(sol::nullopt);
2021-12-13 23:39:01 +00:00
}
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);
2022-04-23 13:37:08 +00:00
sol::usertype<SectionView> sview = lua.new_usertype<SectionView>("Section");
sview["get"] = [](sol::this_state s, const SectionView& section, std::string_view key) {
2021-12-13 23:39:01 +00:00
return section.mSection->get(key).getReadOnly(s);
};
2022-04-23 13:37:08 +00:00
sview["getCopy"] = [](sol::this_state s, const SectionView& section, std::string_view key) {
2021-12-13 23:39:01 +00:00
return section.mSection->get(key).getCopy(s);
};
2022-04-23 13:37:08 +00:00
sview["asTable"] = [](const SectionView& section) { return section.mSection->asTable(); };
2023-02-14 08:40:22 +00:00
sview["subscribe"] = [](const SectionView& section, const sol::table& callback) {
2022-04-23 13:37:08 +00:00
std::vector<Callback>& callbacks = section.mSection->mCallbacks;
if (!callbacks.empty() && callbacks.size() == callbacks.capacity())
{
callbacks.erase(
std::remove_if(callbacks.begin(), callbacks.end(), [&](const Callback& c) { return !c.isValid(); }),
callbacks.end());
}
2023-02-14 08:40:22 +00:00
callbacks.push_back(Callback::fromLua(callback));
2021-12-13 23:39:01 +00:00
};
2022-04-23 13:37:08 +00:00
sview["reset"] = [](const SectionView& section, const sol::optional<sol::table>& newValues) {
if (section.mReadOnly)
throw std::runtime_error("Access to storage is read only");
section.mSection->setAll(newValues);
2021-12-13 23:39:01 +00:00
};
2022-04-23 13:37:08 +00:00
sview["removeOnExit"] = [](const SectionView& section) {
if (section.mReadOnly)
throw std::runtime_error("Access to storage is read only");
section.mSection->mPermanent = false;
2021-12-13 23:39:01 +00:00
};
2022-04-23 13:37:08 +00:00
sview["set"] = [](const SectionView& section, std::string_view key, const sol::object& value) {
if (section.mReadOnly)
throw std::runtime_error("Access to storage is read only");
2021-12-13 23:39:01 +00:00
section.mSection->set(key, value);
};
}
2022-04-23 13:37:08 +00:00
void LuaStorage::clearTemporaryAndRemoveCallbacks()
2021-12-13 23:39:01 +00:00
{
auto it = mData.begin();
while (it != mData.end())
{
2022-04-23 13:37:08 +00:00
it->second->mCallbacks.clear();
2021-12-13 23:39:01 +00:00
if (!it->second->mPermanent)
{
it->second->mValues.clear();
2021-12-13 23:39:01 +00:00
it = mData.erase(it);
}
2021-12-13 23:39:01 +00:00
else
++it;
}
}
void LuaStorage::load(const std::filesystem::path& path)
2021-12-13 23:39:01 +00:00
{
assert(mData.empty()); // Shouldn't be used before loading
2021-12-13 23:39:01 +00:00
try
{
Log(Debug::Info) << "Loading Lua storage \"" << path << "\" (" << std::filesystem::file_size(path)
<< " bytes)";
2021-12-13 23:39:01 +00:00
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)
{
const std::shared_ptr<Section>& section = getSection(sectionName.as<std::string_view>());
2021-12-13 23:39:01 +00:00
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::filesystem::path& path) const
2021-12-13 23:39:01 +00:00
{
sol::table data(mLua, sol::create);
for (const auto& [sectionName, section] : mData)
{
2022-04-23 13:37:08 +00:00
if (section->mPermanent && !section->mValues.empty())
2021-12-13 23:39:01 +00:00
data[sectionName] = section->asTable();
}
std::string serializedData = serialize(data);
Log(Debug::Info) << "Saving Lua storage \"" << path << "\" (" << serializedData.size() << " bytes)";
2021-12-13 23:39:01 +00:00
std::ofstream fout(path, std::fstream::binary);
fout.write(serializedData.data(), serializedData.size());
fout.close();
}
const std::shared_ptr<LuaStorage::Section>& LuaStorage::getSection(std::string_view sectionName)
2021-12-13 23:39:01 +00:00
{
auto it = mData.find(sectionName);
if (it != mData.end())
return it->second;
auto section = std::make_shared<Section>(this, std::string(sectionName));
2021-12-13 23:39:01 +00:00
sectionName = section->mSectionName;
auto [newIt, _] = mData.emplace(sectionName, std::move(section));
return newIt->second;
2021-12-13 23:39:01 +00:00
}
2022-04-23 13:37:08 +00:00
sol::object LuaStorage::getSection(std::string_view sectionName, bool readOnly)
2021-12-13 23:39:01 +00:00
{
const std::shared_ptr<Section>& section = getSection(sectionName);
2022-04-23 13:37:08 +00:00
return sol::make_object<SectionView>(mLua, SectionView{ section, readOnly });
2021-12-13 23:39:01 +00:00
}
2022-04-23 13:37:08 +00:00
sol::table LuaStorage::getAllSections(bool readOnly)
2021-12-13 23:39:01 +00:00
{
sol::table res(mLua, sol::create);
for (const auto& [sectionName, _] : mData)
2022-04-23 13:37:08 +00:00
res[sectionName] = getSection(sectionName, readOnly);
2021-12-13 23:39:01 +00:00
return res;
}
}