From ca8869042bd307632803381f687223e34bce55c1 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 15 Jun 2024 00:27:25 +0200 Subject: [PATCH 1/5] Move LiveCellRef where possible --- apps/openmw/mwworld/cellstore.cpp | 8 ++++---- apps/openmw/mwworld/containerstore.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 6f3d23593b..c22f873fe9 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -319,7 +319,7 @@ namespace // new reference MWWorld::LiveCellRef ref(record); ref.load(state); - collection.mList.push_back(ref); + collection.mList.push_back(std::move(ref)); MWWorld::LiveCellRefBase* base = &collection.mList.back(); MWBase::Environment::get().getWorldModel()->registerPtr(MWWorld::Ptr(base, cellstore)); @@ -426,9 +426,9 @@ namespace MWWorld liveCellRef.mData.setDeletedByContentFile(true); if (iter != mList.end()) - *iter = liveCellRef; + *iter = std::move(liveCellRef); else - mList.push_back(liveCellRef); + mList.push_back(std::move(liveCellRef)); } else { @@ -455,7 +455,7 @@ namespace MWWorld LiveCellRef liveCellRef(ref, ptr); if (!isEnabled(ref, esmStore)) liveCellRef.mData.disable(); - list.push_back(liveCellRef); + list.push_back(std::move(liveCellRef)); } template diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index f48f73f48a..5e020ac886 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -103,7 +103,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState( LiveCellRef ref(record); ref.load(state); - collection.mList.push_back(ref); + collection.mList.push_back(std::move(ref)); auto it = ContainerStoreIterator(this, --collection.mList.end()); MWBase::Environment::get().getWorldModel()->registerPtr(*it); From 4565152b3d8538965821d2343a5419a8c745deb4 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 15 Jun 2024 00:31:43 +0200 Subject: [PATCH 2/5] Define LiveCellRefBase functions inside MWWorld namespace block --- apps/openmw/mwworld/livecellref.cpp | 148 ++++++++++++++-------------- apps/openmw/mwworld/livecellref.hpp | 2 +- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index 61b838bbf0..5623083de7 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -15,104 +15,104 @@ #include "ptr.hpp" #include "worldmodel.hpp" -MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM::CellRef& cref) - : mClass(&Class::get(type)) - , mRef(cref) - , mData(cref) +namespace MWWorld { -} + LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM::CellRef& cref) + : mClass(&Class::get(type)) + , mRef(cref) + , mData(cref) + { + } -MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::Reference& cref) - : mClass(&Class::get(type)) - , mRef(cref) - , mData(cref) -{ -} + LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::Reference& cref) + : mClass(&Class::get(type)) + , mRef(cref) + , mData(cref) + { + } -MWWorld::LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref) - : mClass(&Class::get(type)) - , mRef(cref) - , mData(cref) -{ -} + LiveCellRefBase::LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref) + : mClass(&Class::get(type)) + , mRef(cref) + , mData(cref) + { + } -MWWorld::LiveCellRefBase::~LiveCellRefBase() -{ - MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(*this); -} + LiveCellRefBase::~LiveCellRefBase() + { + MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(*this); + } -void MWWorld::LiveCellRefBase::loadImp(const ESM::ObjectState& state) -{ - mRef = MWWorld::CellRef(state.mRef); - mData = RefData(state, mData.isDeletedByContentFile()); + void LiveCellRefBase::loadImp(const ESM::ObjectState& state) + { + mRef = CellRef(state.mRef); + mData = RefData(state, mData.isDeletedByContentFile()); - Ptr ptr(this); + Ptr ptr(this); - if (state.mHasLocals) - { - const ESM::RefId& scriptId = mClass->getScript(ptr); - // Make sure we still have a script. It could have been coming from a content file that is no longer active. - if (!scriptId.empty()) + if (state.mHasLocals) { - if (const ESM::Script* script - = MWBase::Environment::get().getESMStore()->get().search(scriptId)) + const ESM::RefId& scriptId = mClass->getScript(ptr); + // Make sure we still have a script. It could have been coming from a content file that is no longer active. + if (!scriptId.empty()) { - try - { - mData.setLocals(*script); - mData.getLocals().read(state.mLocals, scriptId); - } - catch (const std::exception& exception) + if (const ESM::Script* script + = MWBase::Environment::get().getESMStore()->get().search(scriptId)) { - Log(Debug::Error) << "Error: failed to load state for local script " << scriptId - << " because an exception has been thrown: " << exception.what(); + try + { + mData.setLocals(*script); + mData.getLocals().read(state.mLocals, scriptId); + } + catch (const std::exception& exception) + { + Log(Debug::Error) << "Error: failed to load state for local script " << scriptId + << " because an exception has been thrown: " << exception.what(); + } } } } - } - mClass->readAdditionalState(ptr, state); + mClass->readAdditionalState(ptr, state); - if (!mRef.getSoul().empty() - && !MWBase::Environment::get().getESMStore()->get().search(mRef.getSoul())) - { - Log(Debug::Warning) << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem"; - mRef.setSoul(ESM::RefId()); - } + if (!mRef.getSoul().empty() + && !MWBase::Environment::get().getESMStore()->get().search(mRef.getSoul())) + { + Log(Debug::Warning) << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem"; + mRef.setSoul(ESM::RefId()); + } - MWBase::Environment::get().getLuaManager()->loadLocalScripts(ptr, state.mLuaScripts); -} + MWBase::Environment::get().getLuaManager()->loadLocalScripts(ptr, state.mLuaScripts); + } -void MWWorld::LiveCellRefBase::saveImp(ESM::ObjectState& state) const -{ - mRef.writeState(state); + void LiveCellRefBase::saveImp(ESM::ObjectState& state) const + { + mRef.writeState(state); - ConstPtr ptr(this); + ConstPtr ptr(this); - mData.write(state, mClass->getScript(ptr)); - MWBase::Environment::get().getLuaManager()->saveLocalScripts( - Ptr(const_cast(this)), state.mLuaScripts); + mData.write(state, mClass->getScript(ptr)); + MWBase::Environment::get().getLuaManager()->saveLocalScripts( + Ptr(const_cast(this)), state.mLuaScripts); - mClass->writeAdditionalState(ptr, state); -} + mClass->writeAdditionalState(ptr, state); + } -bool MWWorld::LiveCellRefBase::checkStateImp(const ESM::ObjectState& state) -{ - return true; -} + bool LiveCellRefBase::checkStateImp(const ESM::ObjectState& state) + { + return true; + } -unsigned int MWWorld::LiveCellRefBase::getType() const -{ - return mClass->getType(); -} + unsigned int LiveCellRefBase::getType() const + { + return mClass->getType(); + } -bool MWWorld::LiveCellRefBase::isDeleted() const -{ - return mData.isDeletedByContentFile() || mRef.getCount(false) == 0; -} + bool LiveCellRefBase::isDeleted() const + { + return mData.isDeletedByContentFile() || mRef.getCount(false) == 0; + } -namespace MWWorld -{ std::string makeDynamicCastErrorMessage(const LiveCellRefBase* value, std::string_view recordType) { std::stringstream message; diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index c95dd589b2..5694e33642 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -29,7 +29,7 @@ namespace MWWorld /** Information about this instance, such as 3D location and rotation * and individual type-dependent data. */ - MWWorld::CellRef mRef; + CellRef mRef; /** runtime-data */ RefData mData; From d998faec1b5c14dbd3f2652a551c631d635f045c Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 15 Jun 2024 00:30:14 +0200 Subject: [PATCH 3/5] Deregister only registered LiveCellRefBase --- apps/openmw/mwworld/livecellref.cpp | 20 +++++++++++++++++++- apps/openmw/mwworld/livecellref.hpp | 12 ++++++++++++ apps/openmw/mwworld/worldmodel.cpp | 15 +++++++++++++++ apps/openmw/mwworld/worldmodel.hpp | 4 ++-- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index 5623083de7..aaa74f5ef3 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -38,9 +38,27 @@ namespace MWWorld { } + LiveCellRefBase::LiveCellRefBase(LiveCellRefBase&& other) noexcept + : mClass(other.mClass) + , mRef(std::move(other.mRef)) + , mData(std::move(other.mData)) + , mWorldModel(std::exchange(other.mWorldModel, nullptr)) + { + } + LiveCellRefBase::~LiveCellRefBase() { - MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(*this); + if (mWorldModel != nullptr) + mWorldModel->deregisterLiveCellRef(*this); + } + + LiveCellRefBase& LiveCellRefBase::operator=(LiveCellRefBase&& other) noexcept + { + mClass = other.mClass; + mRef = std::move(other.mRef); + mData = std::move(other.mData); + mWorldModel = std::exchange(other.mWorldModel, nullptr); + return *this; } void LiveCellRefBase::loadImp(const ESM::ObjectState& state) diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 5694e33642..f3b48c0ff1 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -17,6 +17,7 @@ namespace MWWorld class Ptr; class ESMStore; class Class; + class WorldModel; template struct LiveCellRef; @@ -34,12 +35,23 @@ namespace MWWorld /** runtime-data */ RefData mData; + WorldModel* mWorldModel = nullptr; + LiveCellRefBase(unsigned int type, const ESM::CellRef& cref = ESM::CellRef()); LiveCellRefBase(unsigned int type, const ESM4::Reference& cref); LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref); + + LiveCellRefBase(const LiveCellRefBase& other) = default; + + LiveCellRefBase(LiveCellRefBase&& other) noexcept; + /* Need this for the class to be recognized as polymorphic */ virtual ~LiveCellRefBase(); + LiveCellRefBase& operator=(const LiveCellRefBase& other) = default; + + LiveCellRefBase& operator=(LiveCellRefBase&& other) noexcept; + virtual void load(const ESM::ObjectState& state) = 0; ///< Load state into a LiveCellRef, that has already been initialised with base and class. /// diff --git a/apps/openmw/mwworld/worldmodel.cpp b/apps/openmw/mwworld/worldmodel.cpp index 3a1c486f0e..39ed27e96b 100644 --- a/apps/openmw/mwworld/worldmodel.cpp +++ b/apps/openmw/mwworld/worldmodel.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -339,6 +340,20 @@ namespace MWWorld throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name)); return *result; } + + void WorldModel::registerPtr(const Ptr& ptr) + { + if (ptr.mRef == nullptr) + throw std::logic_error("Ptr with nullptr mRef is not allowed to be registered"); + mPtrRegistry.insert(ptr); + ptr.mRef->mWorldModel = this; + } + + void WorldModel::deregisterLiveCellRef(LiveCellRefBase& ref) noexcept + { + mPtrRegistry.remove(ref); + ref.mWorldModel = nullptr; + } } MWWorld::Ptr MWWorld::WorldModel::getPtrByRefId(const ESM::RefId& name) diff --git a/apps/openmw/mwworld/worldmodel.hpp b/apps/openmw/mwworld/worldmodel.hpp index 4c39d866de..e4b161d16e 100644 --- a/apps/openmw/mwworld/worldmodel.hpp +++ b/apps/openmw/mwworld/worldmodel.hpp @@ -77,9 +77,9 @@ namespace MWWorld std::size_t getPtrRegistryRevision() const { return mPtrRegistry.getRevision(); } - void registerPtr(const Ptr& ptr) { mPtrRegistry.insert(ptr); } + void registerPtr(const Ptr& ptr); - void deregisterLiveCellRef(const LiveCellRefBase& ref) noexcept { mPtrRegistry.remove(ref); } + void deregisterLiveCellRef(LiveCellRefBase& ref) noexcept; void assignSaveFileRefNum(ESM::CellRef& ref) { mPtrRegistry.assign(ref); } From 1cdbbef7ee90834816fe106192d3afdf1c8e52e1 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 15 Jun 2024 00:40:05 +0200 Subject: [PATCH 4/5] Use blank CellRef as default Default constructed CellRef has some fields uninitialized. --- apps/openmw/mwrender/characterpreview.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/livecellref.hpp | 8 +------- apps/openmw/mwworld/player.cpp | 19 +++++++++++++------ components/esm3/cellref.cpp | 6 ++++++ components/esm3/cellref.hpp | 2 ++ 7 files changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index a4c0181d35..123eadfdec 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -533,7 +533,7 @@ namespace MWRender : CharacterPreview( parent, resourceSystem, MWMechanics::getPlayer(), 512, 512, osg::Vec3f(0, 125, 8), osg::Vec3f(0, 0, 8)) , mBase(*mCharacter.get()->mBase) - , mRef(&mBase) + , mRef(ESM::makeBlankCellRef(), &mBase) , mPitchRadians(osg::DegreesToRadians(6.f)) { mCharacter = MWWorld::Ptr(&mRef, nullptr); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index c22f873fe9..4cd189bb20 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -317,7 +317,7 @@ namespace } // new reference - MWWorld::LiveCellRef ref(record); + MWWorld::LiveCellRef ref(ESM::makeBlankCellRef(), record); ref.load(state); collection.mList.push_back(std::move(ref)); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 5e020ac886..5a7c5b5333 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -101,7 +101,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::getState( if (!record) return ContainerStoreIterator(this); - LiveCellRef ref(record); + LiveCellRef ref(ESM::makeBlankCellRef(), record); ref.load(state); collection.mList.push_back(std::move(ref)); auto it = ContainerStoreIterator(this, --collection.mList.end()); diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index f3b48c0ff1..026d3edefd 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -37,7 +37,7 @@ namespace MWWorld WorldModel* mWorldModel = nullptr; - LiveCellRefBase(unsigned int type, const ESM::CellRef& cref = ESM::CellRef()); + LiveCellRefBase(unsigned int type, const ESM::CellRef& cref); LiveCellRefBase(unsigned int type, const ESM4::Reference& cref); LiveCellRefBase(unsigned int type, const ESM4::ActorCharacter& cref); @@ -144,12 +144,6 @@ namespace MWWorld { } - LiveCellRef(const X* b = nullptr) - : LiveCellRefBase(X::sRecordId) - , mBase(b) - { - } - // The object that this instance is based on. const X* mBase; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index f5d38e8686..b776f27f06 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -36,8 +36,20 @@ namespace MWWorld { + namespace + { + ESM::CellRef makePlayerCellRef() + { + ESM::CellRef result; + result.blank(); + result.mRefID = ESM::RefId::stringRefId("Player"); + return result; + } + } + Player::Player(const ESM::NPC* player) - : mCellStore(nullptr) + : mPlayer(makePlayerCellRef(), player) + , mCellStore(nullptr) , mLastKnownExteriorPosition(0, 0, 0) , mMarkedPosition(ESM::Position()) , mMarkedCell(nullptr) @@ -46,11 +58,6 @@ namespace MWWorld , mPaidCrimeId(-1) , mJumping(false) { - ESM::CellRef cellRef; - cellRef.blank(); - cellRef.mRefID = ESM::RefId::stringRefId("Player"); - mPlayer = LiveCellRef(cellRef, player); - ESM::Position playerPos = mPlayer.mData.getPosition(); playerPos.pos[0] = playerPos.pos[1] = playerPos.pos[2] = 0; mPlayer.mData.setPosition(playerPos); diff --git a/components/esm3/cellref.cpp b/components/esm3/cellref.cpp index 97ccfb730a..927f517495 100644 --- a/components/esm3/cellref.cpp +++ b/components/esm3/cellref.cpp @@ -287,4 +287,10 @@ namespace ESM loadDataImpl(esm, isDeleted, cellRef); } + CellRef makeBlankCellRef() + { + CellRef result; + result.blank(); + return result; + } } diff --git a/components/esm3/cellref.hpp b/components/esm3/cellref.hpp index 8b23cc5c34..683bde200d 100644 --- a/components/esm3/cellref.hpp +++ b/components/esm3/cellref.hpp @@ -105,6 +105,8 @@ namespace ESM }; void skipLoadCellRef(ESMReader& esm, bool wideRefNum = false); + + CellRef makeBlankCellRef(); } #endif From e01861140e049e49b3b4a9d36dddd20f5fe90712 Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 15 Jun 2024 01:10:19 +0200 Subject: [PATCH 5/5] Add tests for MWWorld::Ptr --- apps/openmw_tests/CMakeLists.txt | 1 + apps/openmw_tests/main.cpp | 17 ++++++ apps/openmw_tests/mwworld/testptr.cpp | 82 +++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 apps/openmw_tests/mwworld/testptr.cpp diff --git a/apps/openmw_tests/CMakeLists.txt b/apps/openmw_tests/CMakeLists.txt index 0c6b0d84df..9b57113110 100644 --- a/apps/openmw_tests/CMakeLists.txt +++ b/apps/openmw_tests/CMakeLists.txt @@ -9,6 +9,7 @@ file(GLOB UNITTEST_SRC_FILES mwworld/test_store.cpp mwworld/testduration.cpp mwworld/testtimestamp.cpp + mwworld/testptr.cpp mwdialogue/test_keywordsearch.cpp diff --git a/apps/openmw_tests/main.cpp b/apps/openmw_tests/main.cpp index fd7d4900c8..6b7298596a 100644 --- a/apps/openmw_tests/main.cpp +++ b/apps/openmw_tests/main.cpp @@ -1,11 +1,28 @@ #include +#include +#include +#include #include +#include + int main(int argc, char* argv[]) { Log::sMinDebugLevel = Debug::getDebugLevel(); + const std::filesystem::path settingsDefaultPath = std::filesystem::path{ OPENMW_PROJECT_SOURCE_DIR } / "files" + / Misc::StringUtils::stringToU8String("settings-default.cfg"); + + Settings::SettingsFileParser parser; + parser.loadSettingsFile(settingsDefaultPath, Settings::Manager::mDefaultSettings); + + Settings::StaticValues::initDefaults(); + + Settings::Manager::mUserSettings = Settings::Manager::mDefaultSettings; + + Settings::StaticValues::init(); + testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/apps/openmw_tests/mwworld/testptr.cpp b/apps/openmw_tests/mwworld/testptr.cpp new file mode 100644 index 0000000000..7bc0bebcec --- /dev/null +++ b/apps/openmw_tests/mwworld/testptr.cpp @@ -0,0 +1,82 @@ +#include "apps/openmw/mwclass/npc.hpp" +#include "apps/openmw/mwworld/esmstore.hpp" +#include "apps/openmw/mwworld/livecellref.hpp" +#include "apps/openmw/mwworld/ptr.hpp" +#include "apps/openmw/mwworld/worldmodel.hpp" + +#include +#include + +#include + +namespace MWWorld +{ + namespace + { + using namespace testing; + + TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtrWithNullRef) + { + Ptr ptr; + EXPECT_EQ(ptr.toString(), "null object"); + } + + TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtrWithDeletedRef) + { + MWClass::Npc::registerSelf(); + ESM::NPC npc; + npc.blank(); + npc.mId = ESM::RefId::stringRefId("Player"); + ESMStore store; + store.insert(npc); + ESM::CellRef cellRef; + cellRef.blank(); + cellRef.mRefID = npc.mId; + cellRef.mRefNum = ESM::RefNum{ .mIndex = 0x2a, .mContentFile = 0xd }; + LiveCellRef liveCellRef(cellRef, &npc); + liveCellRef.mData.setDeletedByContentFile(true); + Ptr ptr(&liveCellRef); + EXPECT_EQ(ptr.toString(), "deleted object0xd00002a (NPC, \"player\")"); + } + + TEST(MWWorldPtrTest, toStringShouldReturnHumanReadableTextRepresentationOfPtr) + { + MWClass::Npc::registerSelf(); + ESM::NPC npc; + npc.blank(); + npc.mId = ESM::RefId::stringRefId("Player"); + ESMStore store; + store.insert(npc); + ESM::CellRef cellRef; + cellRef.blank(); + cellRef.mRefID = npc.mId; + cellRef.mRefNum = ESM::RefNum{ .mIndex = 0x2a, .mContentFile = 0xd }; + LiveCellRef liveCellRef(cellRef, &npc); + Ptr ptr(&liveCellRef); + EXPECT_EQ(ptr.toString(), "object0xd00002a (NPC, \"player\")"); + } + + TEST(MWWorldPtrTest, underlyingLiveCellRefShouldBeDeregisteredOnDestruction) + { + MWClass::Npc::registerSelf(); + ESM::NPC npc; + npc.blank(); + npc.mId = ESM::RefId::stringRefId("Player"); + ESMStore store; + store.insert(npc); + ESM::ReadersCache readersCache; + WorldModel worldModel(store, readersCache); + ESM::CellRef cellRef; + cellRef.blank(); + cellRef.mRefID = npc.mId; + cellRef.mRefNum = ESM::FormId{ .mIndex = 0x2a, .mContentFile = 0xd }; + { + LiveCellRef liveCellRef(cellRef, &npc); + Ptr ptr(&liveCellRef); + worldModel.registerPtr(ptr); + ASSERT_EQ(worldModel.getPtr(cellRef.mRefNum), ptr); + } + EXPECT_EQ(worldModel.getPtr(cellRef.mRefNum), Ptr()); + } + } +}