From 85dc0ec4816f50311433dde6cc9e3fb569d6448c Mon Sep 17 00:00:00 2001 From: "Mitten.O" Date: Thu, 6 Apr 2023 22:08:16 +0300 Subject: [PATCH] Create a custom Lua usertype to expose a record store as a read-only array --- apps/openmw/mwlua/types/activator.cpp | 2 +- apps/openmw/mwlua/types/apparatus.cpp | 2 +- apps/openmw/mwlua/types/armor.cpp | 2 +- apps/openmw/mwlua/types/book.cpp | 2 +- apps/openmw/mwlua/types/clothing.cpp | 2 +- apps/openmw/mwlua/types/container.cpp | 2 +- apps/openmw/mwlua/types/creature.cpp | 2 +- apps/openmw/mwlua/types/door.cpp | 2 +- apps/openmw/mwlua/types/ingredient.cpp | 2 +- apps/openmw/mwlua/types/light.cpp | 2 +- apps/openmw/mwlua/types/lockpick.cpp | 2 +- apps/openmw/mwlua/types/misc.cpp | 2 +- apps/openmw/mwlua/types/npc.cpp | 2 +- apps/openmw/mwlua/types/potion.cpp | 2 +- apps/openmw/mwlua/types/probe.cpp | 2 +- apps/openmw/mwlua/types/repair.cpp | 2 +- apps/openmw/mwlua/types/static.cpp | 2 +- apps/openmw/mwlua/types/types.hpp | 34 +++++++++++++++++++++++++- apps/openmw/mwlua/types/weapon.cpp | 2 +- apps/openmw/mwworld/store.cpp | 5 ++++ apps/openmw/mwworld/store.hpp | 1 + 21 files changed, 57 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwlua/types/activator.cpp b/apps/openmw/mwlua/types/activator.cpp index 88cf4d0f29..72baeffac5 100644 --- a/apps/openmw/mwlua/types/activator.cpp +++ b/apps/openmw/mwlua/types/activator.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(activator); + addRecordFunctionBinding(activator, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Activator"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/apparatus.cpp b/apps/openmw/mwlua/types/apparatus.cpp index 0747d97d4c..10bdbcdd29 100644 --- a/apps/openmw/mwlua/types/apparatus.cpp +++ b/apps/openmw/mwlua/types/apparatus.cpp @@ -30,7 +30,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(apparatus); + addRecordFunctionBinding(apparatus, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Apparatus"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/armor.cpp b/apps/openmw/mwlua/types/armor.cpp index 7c5b477e10..0368a89b21 100644 --- a/apps/openmw/mwlua/types/armor.cpp +++ b/apps/openmw/mwlua/types/armor.cpp @@ -37,7 +37,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(armor); + addRecordFunctionBinding(armor, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Armor"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/book.cpp b/apps/openmw/mwlua/types/book.cpp index 02602bd6a7..651ba8fd4f 100644 --- a/apps/openmw/mwlua/types/book.cpp +++ b/apps/openmw/mwlua/types/book.cpp @@ -32,7 +32,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(book); + addRecordFunctionBinding(book, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Book"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/clothing.cpp b/apps/openmw/mwlua/types/clothing.cpp index 35634d5fc1..947b997f71 100644 --- a/apps/openmw/mwlua/types/clothing.cpp +++ b/apps/openmw/mwlua/types/clothing.cpp @@ -36,7 +36,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(clothing); + addRecordFunctionBinding(clothing, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Clothing"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/container.cpp b/apps/openmw/mwlua/types/container.cpp index cd6d2e3d24..21ec2f04dc 100644 --- a/apps/openmw/mwlua/types/container.cpp +++ b/apps/openmw/mwlua/types/container.cpp @@ -48,7 +48,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(container); + addRecordFunctionBinding(container, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Container"); record[sol::meta_function::to_string] = [](const ESM::Container& rec) -> std::string { diff --git a/apps/openmw/mwlua/types/creature.cpp b/apps/openmw/mwlua/types/creature.cpp index ecba2b7eba..be6786b4ca 100644 --- a/apps/openmw/mwlua/types/creature.cpp +++ b/apps/openmw/mwlua/types/creature.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(creature); + addRecordFunctionBinding(creature, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Creature"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/door.cpp b/apps/openmw/mwlua/types/door.cpp index db54a77277..7dee2d7af3 100644 --- a/apps/openmw/mwlua/types/door.cpp +++ b/apps/openmw/mwlua/types/door.cpp @@ -43,7 +43,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(door); + addRecordFunctionBinding(door, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Door"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/ingredient.cpp b/apps/openmw/mwlua/types/ingredient.cpp index ed66ec9a9b..69b4a670fb 100644 --- a/apps/openmw/mwlua/types/ingredient.cpp +++ b/apps/openmw/mwlua/types/ingredient.cpp @@ -24,7 +24,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(ingredient); + addRecordFunctionBinding(ingredient, context); sol::usertype record = context.mLua->sol().new_usertype(("ESM3_Ingredient")); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/light.cpp b/apps/openmw/mwlua/types/light.cpp index bc9630289b..347bb61641 100644 --- a/apps/openmw/mwlua/types/light.cpp +++ b/apps/openmw/mwlua/types/light.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(light); + addRecordFunctionBinding(light, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Light"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/lockpick.cpp b/apps/openmw/mwlua/types/lockpick.cpp index c5d9c6983d..786471461a 100644 --- a/apps/openmw/mwlua/types/lockpick.cpp +++ b/apps/openmw/mwlua/types/lockpick.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(lockpick); + addRecordFunctionBinding(lockpick, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Lockpick"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/misc.cpp b/apps/openmw/mwlua/types/misc.cpp index 7b0b5934ec..c565094022 100644 --- a/apps/openmw/mwlua/types/misc.cpp +++ b/apps/openmw/mwlua/types/misc.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(miscellaneous); + addRecordFunctionBinding(miscellaneous, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Miscellaneous"); diff --git a/apps/openmw/mwlua/types/npc.cpp b/apps/openmw/mwlua/types/npc.cpp index 71d611f99c..31dc87095f 100644 --- a/apps/openmw/mwlua/types/npc.cpp +++ b/apps/openmw/mwlua/types/npc.cpp @@ -25,7 +25,7 @@ namespace MWLua { addNpcStatsBindings(npc, context); - addRecordFunctionBinding(npc); + addRecordFunctionBinding(npc, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_NPC"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/potion.cpp b/apps/openmw/mwlua/types/potion.cpp index c14d84024c..3ab15f2691 100644 --- a/apps/openmw/mwlua/types/potion.cpp +++ b/apps/openmw/mwlua/types/potion.cpp @@ -41,7 +41,7 @@ namespace MWLua { void addPotionBindings(sol::table potion, const Context& context) { - addRecordFunctionBinding(potion); + addRecordFunctionBinding(potion, context); // Creates a new potion struct but does not store it in MWWorld::ESMStore. // Global scripts can use world.createRecord to add the potion to the world. diff --git a/apps/openmw/mwlua/types/probe.cpp b/apps/openmw/mwlua/types/probe.cpp index e042e4a0ab..668e58c98c 100644 --- a/apps/openmw/mwlua/types/probe.cpp +++ b/apps/openmw/mwlua/types/probe.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(probe); + addRecordFunctionBinding(probe, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Probe"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/repair.cpp b/apps/openmw/mwlua/types/repair.cpp index f38b7e2992..75d0a17c49 100644 --- a/apps/openmw/mwlua/types/repair.cpp +++ b/apps/openmw/mwlua/types/repair.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(repair); + addRecordFunctionBinding(repair, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Repair"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/static.cpp b/apps/openmw/mwlua/types/static.cpp index ad7ee6b640..76dac4fa00 100644 --- a/apps/openmw/mwlua/types/static.cpp +++ b/apps/openmw/mwlua/types/static.cpp @@ -23,7 +23,7 @@ namespace MWLua { auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(stat); + addRecordFunctionBinding(stat, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Static"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwlua/types/types.hpp b/apps/openmw/mwlua/types/types.hpp index 284bc95d39..01d0766289 100644 --- a/apps/openmw/mwlua/types/types.hpp +++ b/apps/openmw/mwlua/types/types.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "apps/openmw/mwbase/environment.hpp" #include "apps/openmw/mwbase/world.hpp" @@ -13,6 +14,16 @@ #include "../context.hpp" #include "../object.hpp" +namespace sol +{ + // Ensure sol does not try to create the automatic Container or usertype bindings for Store. + // They include write operations and we want the store to be read-only. + template + struct is_automagical> : std::false_type + { + }; +} + namespace MWLua { // `getLiveCellRefType()` is not exactly what we usually mean by "type" because some refids have special meaning. @@ -53,12 +64,33 @@ namespace MWLua void addLightBindings(sol::table light, const Context& context); template - void addRecordFunctionBinding(sol::table table) + void addRecordFunctionBinding(sol::table table, const Context& context) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); table["record"] = sol::overload([](const Object& obj) -> const T* { return obj.ptr().get()->mBase; }, [&store](std::string_view id) -> const T* { return store.find(ESM::RefId::deserializeText(id)); }); + + // Define a custom user type for the store. + // Provide the interface of a read-only array. + using StoreT = MWWorld::Store; + sol::state_view& lua = context.mLua->sol(); + sol::usertype storeT = lua.new_usertype(std::string(T::getRecordType()) + "WorldStore"); + storeT[sol::meta_function::to_string] = [](const StoreT& store) { + return "{" + std::to_string(store.getSize()) + " " + std::string(T::getRecordType()) + " records}"; + }; + storeT[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); }; + storeT[sol::meta_function::index] = [](const StoreT& store, size_t index) -> const T& { + if (index > 0 && index <= store.getSize()) + return store.at(index - 1); // Translate from Lua's 1-based indexing. + else + throw std::runtime_error("Index out of range"); + }; + storeT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + storeT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + + // Provide access to the store. + table["records"] = [&store]() { return &store; }; } } diff --git a/apps/openmw/mwlua/types/weapon.cpp b/apps/openmw/mwlua/types/weapon.cpp index b333f4ef57..aa781c58ea 100644 --- a/apps/openmw/mwlua/types/weapon.cpp +++ b/apps/openmw/mwlua/types/weapon.cpp @@ -40,7 +40,7 @@ namespace MWLua auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); - addRecordFunctionBinding(weapon); + addRecordFunctionBinding(weapon, context); sol::usertype record = context.mLua->sol().new_usertype("ESM3_Weapon"); record[sol::meta_function::to_string] diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 39d022a393..faa04ffba1 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -209,6 +209,11 @@ namespace MWWorld { return mShared.end(); } + template + const T& TypedDynamicStore::at(size_t index) const + { + return *mShared.at(index); + } template size_t TypedDynamicStore::getSize() const diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index dbcf4c5a98..d6e713207e 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -209,6 +209,7 @@ namespace MWWorld iterator begin() const; iterator end() const; + const T& at(size_t index) const; size_t getSize() const override; int getDynamicSize() const override;