mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-23 03:47:34 -04:00
Merge branch 'intern' into 'master'
Draft: Cache Lua GameObjects to prevent duplicate table keys #8677 Closes #8677 See merge request OpenMW/openmw!4902
This commit is contained in:
commit
302ac70d87
@ -63,7 +63,7 @@ add_openmw_dir (mwlua
|
||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings coremwscriptbindings
|
||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
||||
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker landbindings magicbindings factionbindings
|
||||
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings weatherbindings regionbindings
|
||||
classbindings intern itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings weatherbindings regionbindings
|
||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
|
||||
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
|
||||
|
84
apps/openmw/mwlua/intern.cpp
Normal file
84
apps/openmw/mwlua/intern.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
inline constexpr bool is_gameObject = std::is_same_v<LObject, T> || std::is_same_v<GObject, T>;
|
||||
template <typename T>
|
||||
inline constexpr bool is_cell = std::is_same_v<LCell, T> || std::is_same_v<GCell, T>;
|
||||
|
||||
constexpr std::string_view gameObjects = "LUA_GAMEOBJECTS_REGISTRY";
|
||||
constexpr std::string_view cells = "LUA_CELLS_REGISTRY";
|
||||
|
||||
inline constexpr uint64_t toUInt64(const ObjectId& id)
|
||||
{
|
||||
if (id.hasContentFile())
|
||||
return id.mIndex | (static_cast<uint64_t>(id.mContentFile) << 24);
|
||||
else
|
||||
return id.mIndex | (static_cast<uint64_t>(-id.mContentFile - 1) << 32);
|
||||
}
|
||||
|
||||
inline sol::table getRegistry(std::string_view key, sol::state_view lua)
|
||||
{
|
||||
if (sol::object reg = lua.registry()[key]; reg.valid()) // LUA_REGISTRYINDEX
|
||||
return reg.as<sol::table>();
|
||||
|
||||
sol::table t(lua, sol::create);
|
||||
t[sol::metatable_key] = lua.create_table_with("__mode", "v"); // weak table
|
||||
|
||||
lua.registry()[key] = t;
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename userT, typename ArgT>
|
||||
sol::object makeOrGetImpl(ArgT arg, sol::state_view lua)
|
||||
{
|
||||
static_assert(std::is_constructible_v<userT, ArgT>);
|
||||
|
||||
sol::table registry;
|
||||
sol::object key;
|
||||
|
||||
if constexpr (is_gameObject<userT>)
|
||||
{
|
||||
registry = getRegistry(gameObjects, lua);
|
||||
if constexpr (std::is_same_v<ArgT, MWWorld::Ptr>)
|
||||
key = sol::make_object(lua, toUInt64(getId(arg)));
|
||||
else
|
||||
key = sol::make_object(lua, toUInt64(arg));
|
||||
}
|
||||
else if constexpr (is_cell<userT>)
|
||||
{
|
||||
registry = getRegistry(cells, lua);
|
||||
key = sol::make_object(lua, sol::make_light(arg));
|
||||
}
|
||||
// else compile error
|
||||
|
||||
sol::object existing = registry[key];
|
||||
if (existing.valid())
|
||||
return existing;
|
||||
|
||||
sol::object obj = sol::make_object(lua, userT(arg));
|
||||
registry[key] = obj; // stored in weak table so garbage collector should work on this
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename userT, typename ArgT>
|
||||
sol::object makeOrGet(ArgT arg, sol::state_view lua)
|
||||
{
|
||||
return makeOrGetImpl<userT>(arg, lua);
|
||||
}
|
||||
|
||||
// Game Objects
|
||||
template sol::object MWLua::makeOrGet<LObject, MWWorld::Ptr>(MWWorld::Ptr, sol::state_view);
|
||||
template sol::object MWLua::makeOrGet<LObject, ObjectId>(ObjectId, sol::state_view);
|
||||
template sol::object MWLua::makeOrGet<GObject, MWWorld::Ptr>(MWWorld::Ptr, sol::state_view);
|
||||
template sol::object MWLua::makeOrGet<GObject, ObjectId>(ObjectId, sol::state_view);
|
||||
|
||||
// Cells
|
||||
template sol::object MWLua::makeOrGet<LCell, MWWorld::CellStore*>(MWWorld::CellStore*, sol::state_view);
|
||||
template sol::object MWLua::makeOrGet<GCell, MWWorld::CellStore*>(MWWorld::CellStore*, sol::state_view);
|
||||
}
|
@ -58,7 +58,8 @@ namespace MWLua
|
||||
= lua.new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject, Object>());
|
||||
selfAPI[sol::meta_function::to_string]
|
||||
= [](SelfObject& self) { return "openmw.self[" + self.toString() + "]"; };
|
||||
selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
|
||||
selfAPI["object"] = sol::readonly_property(
|
||||
[lua](SelfObject& self) -> sol::object { return makeOrGet<LObject>(self.ptr(), lua); });
|
||||
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||
selfAPI["isActive"] = [](SelfObject& self) -> bool { return self.mIsActive; };
|
||||
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
|
||||
|
@ -791,9 +791,8 @@ namespace MWLua
|
||||
{
|
||||
sol::object selected = sol::nil;
|
||||
if (!selectedPtr.isEmpty())
|
||||
mLua.protectedCall([&](LuaUtil::LuaView& view) {
|
||||
selected = sol::make_object(view.sol(), LObject(getId(selectedPtr)));
|
||||
});
|
||||
mLua.protectedCall(
|
||||
[&](LuaUtil::LuaView& view) { selected = makeOrGet<LObject>(selectedPtr, view.sol()); });
|
||||
if (playerScripts->consoleCommand(consoleMode, command, selected))
|
||||
processed = true;
|
||||
}
|
||||
|
@ -130,18 +130,18 @@ namespace MWLua
|
||||
mwscript["isRunning"] = sol::readonly_property([](const MWScriptRef& s) { return s.isRunning(); });
|
||||
mwscript["recordId"] = sol::readonly_property([](const MWScriptRef& s) { return s.mId.serializeText(); });
|
||||
mwscript["variables"] = sol::readonly_property([](const MWScriptRef& s) { return MWScriptVariables{ s }; });
|
||||
mwscript["object"] = sol::readonly_property([](const MWScriptRef& s) -> sol::optional<GObject> {
|
||||
mwscript["object"] = sol::readonly_property([lua](const MWScriptRef& s) -> sol::object {
|
||||
if (s.mObj)
|
||||
return s.mObj;
|
||||
return makeOrGet<GObject>(s.mObj->ptr(), lua);
|
||||
const MWScript::GlobalScriptDesc* script
|
||||
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScriptIfPresent(s.mId);
|
||||
if (!script)
|
||||
throw std::runtime_error("Invalid MWScriptRef");
|
||||
const MWWorld::Ptr* ptr = script->getPtrIfPresent();
|
||||
if (ptr && !ptr->isEmpty())
|
||||
return GObject(*ptr);
|
||||
return makeOrGet<GObject>(*ptr, lua);
|
||||
else
|
||||
return sol::nullopt;
|
||||
return sol::nil;
|
||||
});
|
||||
mwscript["player"] = sol::readonly_property(
|
||||
[](const MWScriptRef&) { return GObject(MWBase::Environment::get().getWorld()->getPlayerPtr()); });
|
||||
|
@ -35,6 +35,9 @@ namespace MWLua
|
||||
}
|
||||
};
|
||||
|
||||
template <typename userT, typename ArgT>
|
||||
sol::object makeOrGet(ArgT arg, sol::state_view lua);
|
||||
|
||||
// Used only in local scripts
|
||||
struct LCell
|
||||
{
|
||||
|
@ -170,11 +170,11 @@ namespace MWLua
|
||||
listT[sol::meta_function::to_string]
|
||||
= [](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; };
|
||||
listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); };
|
||||
listT[sol::meta_function::index] = [](const ListT& list, size_t index) -> sol::optional<ObjectT> {
|
||||
listT[sol::meta_function::index] = [lua](const ListT& list, size_t index) -> sol::object {
|
||||
if (index > 0 && index <= list.mIds->size())
|
||||
return ObjectT((*list.mIds)[LuaUtil::fromLuaIndex(index)]);
|
||||
return makeOrGet<ObjectT>((*list.mIds)[LuaUtil::fromLuaIndex(index)], lua);
|
||||
else
|
||||
return sol::nullopt;
|
||||
return sol::nil;
|
||||
};
|
||||
listT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||
listT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||
@ -278,6 +278,7 @@ namespace MWLua
|
||||
template <class ObjectT>
|
||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
sol::state_view lua = context.sol();
|
||||
objectT["id"] = sol::readonly_property([](const ObjectT& o) -> std::string { return o.id().toString(); });
|
||||
objectT["contentFile"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<std::string> {
|
||||
int contentFileIndex = o.id().mContentFile;
|
||||
@ -296,13 +297,13 @@ namespace MWLua
|
||||
else
|
||||
return ESM::RefId::stringRefId(globalVariable).serializeText();
|
||||
});
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
|
||||
objectT["cell"] = sol::readonly_property([lua](const ObjectT& o) -> sol::object {
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
MWWorld::WorldModel* wm = MWBase::Environment::get().getWorldModel();
|
||||
if (ptr.isInCell() && ptr.getCell() != &wm->getDraftCell())
|
||||
return Cell<ObjectT>{ ptr.getCell() };
|
||||
return makeOrGet<Cell<ObjectT>>(ptr.getCell(), lua);
|
||||
else
|
||||
return sol::nullopt;
|
||||
return sol::nil;
|
||||
});
|
||||
objectT["parentContainer"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<ObjectT> {
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
@ -555,6 +556,7 @@ namespace MWLua
|
||||
void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
|
||||
{
|
||||
using InventoryT = Inventory<ObjectT>;
|
||||
sol::state_view lua = context.sol();
|
||||
sol::usertype<InventoryT> inventoryT = context.sol().new_usertype<InventoryT>(prefix + "Inventory");
|
||||
|
||||
inventoryT[sol::meta_function::to_string]
|
||||
@ -651,7 +653,7 @@ namespace MWLua
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
return store.isResolved();
|
||||
};
|
||||
inventoryT["find"] = [](const InventoryT& inventory, std::string_view recordId) -> sol::optional<ObjectT> {
|
||||
inventoryT["find"] = [lua](const InventoryT& inventory, std::string_view recordId) -> sol::object {
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore(ptr);
|
||||
auto itemId = ESM::RefId::deserializeText(recordId);
|
||||
@ -660,10 +662,10 @@ namespace MWLua
|
||||
if (item.getCellRef().getRefId() == itemId)
|
||||
{
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(item);
|
||||
return ObjectT(getId(item));
|
||||
return makeOrGet<ObjectT>(getId(item), lua);
|
||||
}
|
||||
}
|
||||
return sol::nullopt;
|
||||
return sol::nil;
|
||||
};
|
||||
inventoryT["findAll"] = [](const InventoryT& inventory, std::string_view recordId) {
|
||||
const MWWorld::Ptr& ptr = inventory.mObj.ptr();
|
||||
|
@ -96,9 +96,9 @@ namespace MWLua
|
||||
return sol::nil;
|
||||
MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell());
|
||||
if (dynamic_cast<const GObject*>(&o))
|
||||
return sol::make_object(thisState, GCell{ &cell });
|
||||
return makeOrGet<GCell>(&cell, thisState);
|
||||
else
|
||||
return sol::make_object(thisState, LCell{ &cell });
|
||||
return makeOrGet<LCell>(&cell, thisState);
|
||||
};
|
||||
|
||||
addRecordFunctionBinding<ESM::Door>(door, context);
|
||||
@ -132,9 +132,9 @@ namespace MWLua
|
||||
return sol::nil;
|
||||
MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getCell(cellRef.getDestCell());
|
||||
if (dynamic_cast<const GObject*>(&o))
|
||||
return sol::make_object(lua, GCell{ &cell });
|
||||
return makeOrGet<GCell>(&cell, lua);
|
||||
else
|
||||
return sol::make_object(lua, LCell{ &cell });
|
||||
return makeOrGet<LCell>(&cell, lua);
|
||||
};
|
||||
|
||||
addRecordFunctionBinding<ESM4::Door>(door, context, "ESM4Door");
|
||||
|
@ -77,9 +77,9 @@ namespace MWLua
|
||||
ObjectId id = loadRefNum(binaryData);
|
||||
adjustRefNum(id);
|
||||
if (mLocalSerializer)
|
||||
sol::stack::push<LObject>(lua, LObject(id));
|
||||
sol::stack::push(lua, makeOrGet<LObject>(id, lua));
|
||||
else
|
||||
sol::stack::push<GObject>(lua, GObject(id));
|
||||
sol::stack::push(lua, makeOrGet<GObject>(id, lua));
|
||||
return true;
|
||||
}
|
||||
if (typeName == sObjListTypeName)
|
||||
|
@ -81,14 +81,16 @@ namespace MWLua
|
||||
|
||||
static void addCellGetters(sol::table& api, const Context& context)
|
||||
{
|
||||
api["getCellByName"] = [](std::string_view name) {
|
||||
return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(name, /*forceLoad=*/false) };
|
||||
sol::state_view lua = context.sol();
|
||||
api["getCellByName"] = [lua](std::string_view name) {
|
||||
return makeOrGet<GCell>(
|
||||
&MWBase::Environment::get().getWorldModel()->getCell(name, /*forceLoad=*/false), lua);
|
||||
};
|
||||
api["getCellById"] = [](std::string_view stringId) {
|
||||
return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(
|
||||
ESM::RefId::deserializeText(stringId), /*forceLoad=*/false) };
|
||||
api["getCellById"] = [lua](std::string_view stringId) {
|
||||
ESM::RefId id = ESM::RefId::deserializeText(stringId);
|
||||
return makeOrGet<GCell>(&MWBase::Environment::get().getWorldModel()->getCell(id, /*forceLoad=*/false), lua);
|
||||
};
|
||||
api["getExteriorCell"] = [](int x, int y, sol::object cellOrName) {
|
||||
api["getExteriorCell"] = [lua](int x, int y, sol::object cellOrName) {
|
||||
ESM::RefId worldspace;
|
||||
if (cellOrName.is<GCell>())
|
||||
worldspace = cellOrName.as<GCell>().mStore->getCell()->getWorldSpace();
|
||||
@ -100,8 +102,9 @@ namespace MWLua
|
||||
->getWorldSpace();
|
||||
else
|
||||
worldspace = ESM::Cell::sDefaultWorldspaceId;
|
||||
return GCell{ &MWBase::Environment::get().getWorldModel()->getExterior(
|
||||
ESM::ExteriorCellLocation(x, y, worldspace), /*forceLoad=*/false) };
|
||||
auto location = ESM::ExteriorCellLocation(x, y, worldspace);
|
||||
return makeOrGet<GCell>(
|
||||
&MWBase::Environment::get().getWorldModel()->getExterior(location, /*forceLoad=*/false), lua);
|
||||
};
|
||||
|
||||
const MWWorld::Store<ESM::Cell>* cells3Store = &MWBase::Environment::get().getESMStore()->get<ESM::Cell>();
|
||||
@ -111,22 +114,22 @@ namespace MWLua
|
||||
cells[sol::meta_function::length]
|
||||
= [cells3Store, cells4Store](const CellsStore&) { return cells3Store->getSize() + cells4Store->getSize(); };
|
||||
cells[sol::meta_function::index]
|
||||
= [cells3Store, cells4Store](const CellsStore&, size_t index) -> sol::optional<GCell> {
|
||||
= [cells3Store, cells4Store, lua](const CellsStore&, size_t index) -> sol::object {
|
||||
if (index > cells3Store->getSize() + cells3Store->getSize() || index == 0)
|
||||
return sol::nullopt;
|
||||
return sol::nil;
|
||||
|
||||
index--; // Translate from Lua's 1-based indexing.
|
||||
if (index < cells3Store->getSize())
|
||||
{
|
||||
const ESM::Cell* cellRecord = cells3Store->at(index);
|
||||
return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(
|
||||
cellRecord->mId, /*forceLoad=*/false) };
|
||||
return makeOrGet<GCell>(
|
||||
&MWBase::Environment::get().getWorldModel()->getCell(cellRecord->mId, /*forceLoad=*/false), lua);
|
||||
}
|
||||
else
|
||||
{
|
||||
const ESM4::Cell* cellRecord = cells4Store->at(index - cells3Store->getSize());
|
||||
return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(
|
||||
cellRecord->mId, /*forceLoad=*/false) };
|
||||
return makeOrGet<GCell>(
|
||||
&MWBase::Environment::get().getWorldModel()->getCell(cellRecord->mId, /*forceLoad=*/false), lua);
|
||||
}
|
||||
};
|
||||
cells[sol::meta_function::pairs] = view["ipairsForArray"].template get<sol::function>();
|
||||
|
Loading…
x
Reference in New Issue
Block a user