Fix the bug "ESMStore::setUp() is called twice" that causes duplicated objects in ESM4 cells

macos_ci
Petr Mikheev 12 months ago
parent 8e3e351015
commit 420916327d

@ -248,6 +248,7 @@ namespace MWWorld
{
for (const auto& store : mDynamicStores)
store->clearDynamic();
mStoreImp->mIds = mStoreImp->mStaticIds;
movePlayerRecord();
}
@ -426,32 +427,36 @@ namespace MWWorld
void ESMStore::setUp()
{
mStoreImp->mIds.clear();
std::map<ESM::RecNameInts, DynamicStore*>::iterator storeIt = mStoreImp->mRecNameToStore.begin();
for (; storeIt != mStoreImp->mRecNameToStore.end(); ++storeIt)
{
storeIt->second->setUp();
if (mIsSetUpDone)
throw std::logic_error("ESMStore::setUp() is called twice");
mIsSetUpDone = true;
if (isCacheableRecord(storeIt->first))
{
std::vector<ESM::RefId> identifiers;
storeIt->second->listIdentifier(identifiers);
for (auto& record : identifiers)
mStoreImp->mIds[record] = storeIt->first;
}
}
if (mStoreImp->mStaticIds.empty())
for (const auto& [k, v] : mStoreImp->mIds)
mStoreImp->mStaticIds.emplace(k, v);
for (const auto& [_, store] : mStoreImp->mRecNameToStore)
store->setUp();
getWritable<ESM::Skill>().setUp(get<ESM::GameSetting>());
getWritable<ESM::MagicEffect>().setUp();
getWritable<ESM::Attribute>().setUp();
getWritable<ESM4::Land>().updateLandPositions(get<ESM4::Cell>());
getWritable<ESM4::Reference>().preprocessReferences(get<ESM4::Cell>());
rebuildIdsIndex();
mStoreImp->mStaticIds = mStoreImp->mIds;
}
void ESMStore::rebuildIdsIndex()
{
mStoreImp->mIds.clear();
for (const auto& [recordType, store] : mStoreImp->mRecNameToStore)
{
if (isCacheableRecord(recordType))
{
std::vector<ESM::RefId> identifiers;
store->listIdentifier(identifiers);
for (auto& record : identifiers)
mStoreImp->mIds[record] = recordType;
}
}
}
void ESMStore::validateRecords(ESM::ReadersCache& readers)
@ -704,8 +709,6 @@ namespace MWWorld
void ESMStore::checkPlayer()
{
setUp();
const ESM::NPC* player = get<ESM::NPC>().find(ESM::RefId::stringRefId("Player"));
if (!get<ESM::Race>().find(player->mRace) || !get<ESM::Class>().find(player->mClass))

@ -165,6 +165,8 @@ namespace MWWorld
std::filesystem::path>; // path to an omwscripts file
std::vector<LuaContent> mLuaContent;
bool mIsSetUpDone = false;
public:
void addOMWScripts(std::filesystem::path filePath) { mLuaContent.push_back(std::move(filePath)); }
ESM::LuaScriptsCfg getLuaScriptsCfg() const;
@ -185,6 +187,7 @@ namespace MWWorld
~ESMStore();
void clearDynamic();
void rebuildIdsIndex();
void movePlayerRecord();

@ -472,7 +472,7 @@ namespace MWWorld
{
// The land is static for given game session, there is no need to refresh it every load.
if (mBuilt)
return;
throw std::logic_error("Store<ESM::Land>::setUp() is called twice");
mBuilt = true;
}

@ -2284,6 +2284,7 @@ namespace MWWorld
void World::saveLoaded()
{
mStore.rebuildIdsIndex();
mStore.validateDynamic();
mCurrentDate->setup(mGlobalVariables);
}

@ -325,29 +325,39 @@ TYPED_TEST_P(StoreTest, delete_test)
ESM::ESMReader reader;
ESM::Dialogue* dialogue = nullptr;
MWWorld::ESMStore esmStore;
// master file inserts a record
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 1);
// now a plugin deletes it
reader.open(getEsmFile(record, true, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
{
MWWorld::ESMStore esmStore;
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue); // master file inserts a record
esmStore.setUp();
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 0);
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 1);
}
{
MWWorld::ESMStore esmStore;
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue); // master file inserts a record
reader.open(getEsmFile(record, true, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue); // now a plugin deletes it
esmStore.setUp();
// now another plugin inserts it again
// expected behaviour is the record to reappear rather than staying deleted
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 0);
}
{
MWWorld::ESMStore esmStore;
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue); // master file inserts a record
reader.open(getEsmFile(record, true, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue); // now a plugin deletes it
// now another plugin inserts it again
// expected behaviour is the record to reappear rather than staying deleted
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 1);
EXPECT_EQ(esmStore.get<RecordType>().getSize(), 1);
}
}
}
@ -410,13 +420,13 @@ TYPED_TEST_P(StoreTest, overwrite_test)
// master file inserts a record
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
// now a plugin overwrites it with changed data
record.mId = recordIdUpper; // change id to uppercase, to test case smashing while we're at it
record.mModel = "the_new_model";
reader.open(getEsmFile(record, false, formatVersion), "filename");
esmStore.load(reader, &dummyListener, dialogue);
esmStore.setUp();
// verify that changes were actually applied

Loading…
Cancel
Save