1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2026-01-14 13:00:54 +00:00

Deduplicate stats cache usage

This commit is contained in:
Evil Eye 2026-01-11 13:34:21 +01:00
parent 9233f82113
commit 998065618f
4 changed files with 165 additions and 173 deletions

View file

@ -13,159 +13,144 @@
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
namespace
{
using SelfObject = MWLua::SelfObject;
using Index = const SelfObject::CachedStat::Index&;
constexpr std::array properties = { "condition", "enchantmentCharge", "soul" };
void valueErr(std::string_view prop, std::string type)
{
throw std::logic_error("'" + std::string(prop) + "'" + " received invalid value type (" + type + ")");
}
}
namespace MWLua
{
static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj)
namespace
{
if (!obj.mStatsCache.empty())
return; // was already added before
manager->addAction(
[obj = Object(obj)] {
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
if (scripts)
scripts->applyStatsCache();
},
"StatUpdateAction");
}
using Index = const SelfObject::CachedStat::Index&;
class ItemData
{
ObjectVariant mObject;
constexpr std::array properties = { "condition", "enchantmentCharge", "soul" };
public:
ItemData(const ObjectVariant& object)
: mObject(object)
void valueErr(std::string_view prop, std::string type)
{
throw std::logic_error("'" + std::string(prop) + "'" + " received invalid value type (" + type + ")");
}
sol::object get(const Context& context, std::string_view prop) const
class ItemData
{
if (mObject.isSelfObject())
ObjectVariant mObject;
public:
ItemData(const ObjectVariant& object)
: mObject(object)
{
SelfObject* self = mObject.asSelfObject();
auto it = self->mStatsCache.find({ &ItemData::setValue, std::monostate{}, prop });
if (it != self->mStatsCache.end())
return it->second;
}
return sol::make_object(context.mLua->unsafeState(), getValue(context, prop, mObject.ptr()));
}
void set(const Context& context, std::string_view prop, const sol::object& value) const
{
if (mObject.isGObject())
setValue({}, prop, mObject.ptr(), value);
else if (mObject.isSelfObject())
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }]
= sol::main_object(value);
}
else
throw std::runtime_error("Only global or self scripts can set the value");
}
static sol::object getValue(const Context& context, std::string_view prop, const MWWorld::Ptr& ptr)
{
if (prop == "condition")
{
if (ptr.mRef->getType() == ESM::REC_LIGH)
return sol::make_object(context.mLua->unsafeState(), ptr.getClass().getRemainingUsageTime(ptr));
else if (ptr.getClass().hasItemHealth(ptr))
return sol::make_object(context.mLua->unsafeState(),
ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder());
}
else if (prop == "enchantmentCharge")
{
const ESM::RefId& enchantmentName = ptr.getClass().getEnchantment(ptr);
if (enchantmentName.empty())
return sol::lua_nil;
float charge = ptr.getCellRef().getEnchantmentCharge();
const auto& store = MWBase::Environment::get().getESMStore();
const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
if (charge == -1) // return the full charge
return sol::make_object(
context.mLua->unsafeState(), MWMechanics::getEnchantmentCharge(*enchantment));
return sol::make_object(context.mLua->unsafeState(), charge);
}
else if (prop == "soul")
{
ESM::RefId soul = ptr.getCellRef().getSoul();
if (soul.empty())
return sol::lua_nil;
return sol::make_object(context.mLua->unsafeState(), soul.serializeText());
}
return sol::lua_nil;
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
if (prop == "condition")
sol::object get(const Context& context, std::string_view prop) const
{
if (value.get_type() == sol::type::number)
if (mObject.isSelfObject())
{
SelfObject* self = mObject.asSelfObject();
if (auto value = self->getCachedStat({ &ItemData::setValue, std::monostate{}, prop }))
return *value;
}
return sol::make_object(context.mLua->unsafeState(), getValue(context, prop, mObject.ptr()));
}
void set(const Context& context, std::string_view prop, const sol::object& value) const
{
if (mObject.isGObject())
setValue({}, prop, mObject.ptr(), value);
else if (mObject.isSelfObject())
{
SelfObject* obj = mObject.asSelfObject();
obj->cacheStat(*context.mLuaManager,
SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }, value);
}
else
throw std::runtime_error("Only global or self scripts can set the value");
}
static sol::object getValue(const Context& context, std::string_view prop, const MWWorld::Ptr& ptr)
{
if (prop == "condition")
{
float cond = LuaUtil::cast<float>(value);
if (ptr.mRef->getType() == ESM::REC_LIGH)
ptr.getClass().setRemainingUsageTime(ptr, cond);
return sol::make_object(context.mLua->unsafeState(), ptr.getClass().getRemainingUsageTime(ptr));
else if (ptr.getClass().hasItemHealth(ptr))
{
// if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0
ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond)));
ptr.getCellRef().setCharge(std::max(0, static_cast<int>(cond)));
}
return sol::make_object(context.mLua->unsafeState(),
ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder());
}
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
}
else if (prop == "enchantmentCharge")
{
if (value.get_type() == sol::type::lua_nil)
ptr.getCellRef().setEnchantmentCharge(-1);
else if (value.get_type() == sol::type::number)
ptr.getCellRef().setEnchantmentCharge(std::max(0.0f, LuaUtil::cast<float>(value)));
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
}
else if (prop == "soul")
{
if (value.get_type() == sol::type::lua_nil)
ptr.getCellRef().setSoul(ESM::RefId{});
else if (value.get_type() == sol::type::string)
else if (prop == "enchantmentCharge")
{
std::string_view souldId = LuaUtil::cast<std::string_view>(value);
ESM::RefId creature = ESM::RefId::deserializeText(souldId);
const auto& store = *MWBase::Environment::get().getESMStore();
const ESM::RefId& enchantmentName = ptr.getClass().getEnchantment(ptr);
// TODO: Add Support for NPC Souls
if (store.get<ESM::Creature>().search(creature))
ptr.getCellRef().setSoul(creature);
else
throw std::runtime_error("Cannot use non-existent creature as a soul: " + std::string(souldId));
if (enchantmentName.empty())
return sol::lua_nil;
float charge = ptr.getCellRef().getEnchantmentCharge();
const auto& store = MWBase::Environment::get().getESMStore();
const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
if (charge == -1) // return the full charge
return sol::make_object(
context.mLua->unsafeState(), MWMechanics::getEnchantmentCharge(*enchantment));
return sol::make_object(context.mLua->unsafeState(), charge);
}
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
else if (prop == "soul")
{
ESM::RefId soul = ptr.getCellRef().getSoul();
if (soul.empty())
return sol::lua_nil;
return sol::make_object(context.mLua->unsafeState(), soul.serializeText());
}
return sol::lua_nil;
}
}
};
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
if (prop == "condition")
{
if (value.get_type() == sol::type::number)
{
float cond = LuaUtil::cast<float>(value);
if (ptr.mRef->getType() == ESM::REC_LIGH)
ptr.getClass().setRemainingUsageTime(ptr, cond);
else if (ptr.getClass().hasItemHealth(ptr))
{
// if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0
ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond)));
ptr.getCellRef().setCharge(std::max(0, static_cast<int>(cond)));
}
}
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
}
else if (prop == "enchantmentCharge")
{
if (value.get_type() == sol::type::lua_nil)
ptr.getCellRef().setEnchantmentCharge(-1);
else if (value.get_type() == sol::type::number)
ptr.getCellRef().setEnchantmentCharge(std::max(0.0f, LuaUtil::cast<float>(value)));
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
}
else if (prop == "soul")
{
if (value.get_type() == sol::type::lua_nil)
ptr.getCellRef().setSoul(ESM::RefId{});
else if (value.get_type() == sol::type::string)
{
std::string_view souldId = LuaUtil::cast<std::string_view>(value);
ESM::RefId creature = ESM::RefId::deserializeText(souldId);
const auto& store = *MWBase::Environment::get().getESMStore();
// TODO: Add Support for NPC Souls
if (store.get<ESM::Creature>().search(creature))
ptr.getCellRef().setSoul(creature);
else
throw std::runtime_error(
"Cannot use non-existent creature as a soul: " + std::string(souldId));
}
else
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
}
}
};
}
}
namespace sol

View file

@ -16,6 +16,7 @@
#include "../mwworld/ptr.hpp"
#include "context.hpp"
#include "luamanagerimp.hpp"
namespace sol
{
@ -31,6 +32,20 @@ namespace sol
namespace MWLua
{
void SelfObject::cacheStat(LuaManager& manager, SelfObject::CachedStat key, sol::main_object value)
{
if (mStatsCache.empty())
{
manager.addAction(
[obj = Object(*this)] {
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
if (scripts)
scripts->applyStatsCache();
},
"StatUpdateAction");
}
mStatsCache[std::move(key)] = std::move(value);
}
void LocalScripts::initializeSelfPackage(const Context& context)
{

View file

@ -15,6 +15,8 @@
namespace MWLua
{
struct Context;
class LocalScripts;
class LuaManager;
struct SelfObject : public LObject
{
@ -52,9 +54,22 @@ namespace MWLua
, mIsActive(false)
{
}
const sol::main_object* getCachedStat(const CachedStat& key) const
{
auto it = mStatsCache.find(key);
if (it != mStatsCache.end())
return &it->second;
return nullptr;
}
void cacheStat(LuaManager&, CachedStat, sol::main_object);
MWBase::LuaManager::ActorControls mControls;
std::map<CachedStat, sol::main_object> mStatsCache;
bool mIsActive;
private:
friend class LocalScripts;
std::map<CachedStat, sol::main_object> mStatsCache;
};
class LocalScripts : public LuaUtil::ScriptsContainer

View file

@ -52,9 +52,8 @@ namespace
if (obj.isSelfObject())
{
SelfObject* self = obj.asSelfObject();
auto it = self->mStatsCache.find({ setter, index, prop });
if (it != self->mStatsCache.end())
return it->second;
if (auto value = self->getCachedStat({ setter, index, prop }))
return *value;
}
return sol::make_object(context.mLua->unsafeState(), getter(obj.ptr()));
}
@ -64,19 +63,6 @@ namespace MWLua
{
namespace
{
static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj)
{
if (!obj.mStatsCache.empty())
return; // was already added before
manager->addAction(
[obj = Object(obj)] {
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
if (scripts)
scripts->applyStatsCache();
},
"StatUpdateAction");
}
static void setCreatureValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
auto& stats = ptr.getClass().getCreatureStats(ptr);
@ -125,9 +111,8 @@ namespace MWLua
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }]
= sol::main_object(value);
obj->cacheStat(*context.mLuaManager,
SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }, value);
}
};
@ -160,10 +145,8 @@ namespace MWLua
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{
&setNpcValue, specialization, "skillIncreasesForSpecialization" }]
= sol::main_object(value);
obj->cacheStat(*context.mLuaManager,
SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }, value);
}
};
@ -186,9 +169,8 @@ namespace MWLua
void setCurrent(const Context& context, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }]
= sol::main_object(value);
obj->cacheStat(*context.mLuaManager,
SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }, value);
}
sol::object getProgress(const Context& context) const
@ -207,9 +189,8 @@ namespace MWLua
return;
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }]
= sol::main_object(value);
obj->cacheStat(
*context.mLuaManager, SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }, value);
}
SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const
@ -262,9 +243,8 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }]
= sol::main_object(value);
obj->cacheStat(
*context.mLuaManager, SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }, value);
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -324,9 +304,8 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }]
= sol::main_object(value);
obj->cacheStat(
*context.mLuaManager, SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }, value);
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -411,8 +390,7 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = sol::main_object(value);
obj->cacheStat(*context.mLuaManager, SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }, value);
}
static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -474,9 +452,8 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }]
= sol::main_object(value);
obj->cacheStat(*context.mLuaManager,
SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }, value);
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)