1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

refactors MWWorld::Store maps (#3197)

With this PR we clean up `MWWorld::Store` maps according to the approach of PR #3184.
This commit is contained in:
Bo Svensson 2021-10-30 17:27:57 +00:00 committed by GitHub
parent 7d34149adc
commit 1329f22186
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 107 deletions

View File

@ -61,7 +61,7 @@ namespace
}
}
std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions, const MWWorld::Store<ESM::Class>& classes, const std::map<std::string, ESM::NPC>& npcs)
std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions, const MWWorld::Store<ESM::Class>& classes, const std::unordered_map<std::string, ESM::NPC, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>& npcs)
{
// Cache first class from store - we will use it if current class is not found
std::string defaultCls;
@ -114,8 +114,8 @@ namespace
// Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no longer exists however.
// So instead of removing the item altogether, we're only removing the script.
template<class T>
void removeMissingScripts(const MWWorld::Store<ESM::Script>& scripts, std::map<std::string, T>& items)
template<class MapT>
void removeMissingScripts(const MWWorld::Store<ESM::Script>& scripts, MapT& items)
{
for(auto& [id, item] : items)
{
@ -324,7 +324,6 @@ void ESMStore::countRecords()
if (value.mRefID != deletedRefID)
{
std::string& refId = refIDs[value.mRefID];
Misc::StringUtils::lowerCaseInPlace(refId);
++mRefCount[std::move(refId)];
}
};
@ -333,8 +332,7 @@ void ESMStore::countRecords()
int ESMStore::getRefCount(const std::string& id) const
{
const std::string lowerId = Misc::StringUtils::lowerCase(id);
auto it = mRefCount.find(lowerId);
auto it = mRefCount.find(id);
if(it == mRefCount.end())
return 0;
return it->second;
@ -533,9 +531,8 @@ void ESMStore::removeMissingObjects(Store<T>& store)
throw std::runtime_error ("Invalid player record (race or class unavailable");
}
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& originalId) const
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& id) const
{
const std::string id = Misc::StringUtils::lowerCase(originalId);
auto result = mSpellListCache.find(id);
std::shared_ptr<MWMechanics::SpellList> ptr;
if (result != mSpellListCache.end())

View File

@ -75,16 +75,17 @@ namespace MWWorld
// Lookup of all IDs. Makes looking up references faster. Just
// maps the id name to the record type.
std::map<std::string, int> mIds;
std::map<std::string, int> mStaticIds;
using IDMap = std::unordered_map<std::string, int, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>;
IDMap mIds;
IDMap mStaticIds;
std::unordered_map<std::string, int> mRefCount;
IDMap mRefCount;
std::map<int, StoreBase *> mStores;
unsigned int mDynamicCount;
mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache;
mutable std::unordered_map<std::string, std::weak_ptr<MWMechanics::SpellList>, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> mSpellListCache;
/// Validate entries in store after setup
void validate();
@ -115,10 +116,9 @@ namespace MWWorld
}
/// Look up the given ID in 'all'. Returns 0 if not found.
/// \note id must be in lower case.
int find(const std::string &id) const
{
std::map<std::string, int>::const_iterator it = mIds.find(id);
IDMap::const_iterator it = mIds.find(id);
if (it == mIds.end()) {
return 0;
}
@ -126,7 +126,7 @@ namespace MWWorld
}
int findStatic(const std::string &id) const
{
std::map<std::string, int>::const_iterator it = mStaticIds.find(id);
IDMap::const_iterator it = mStaticIds.find(id);
if (it == mStaticIds.end()) {
return 0;
}

View File

@ -38,8 +38,8 @@ namespace MWWorld
bool isDeleted = false;
record.load(esm, isDeleted);
mStatic.insert_or_assign(record.mIndex, record);
auto idx = record.mIndex;
mStatic.insert_or_assign(idx, std::move(record));
}
template<typename T>
int IndexedStore<T>::getSize() const
@ -98,13 +98,11 @@ namespace MWWorld
template<typename T>
const T *Store<T>::search(const std::string &id) const
{
std::string idLower = Misc::StringUtils::lowerCase(id);
typename Dynamic::const_iterator dit = mDynamic.find(idLower);
typename Dynamic::const_iterator dit = mDynamic.find(id);
if (dit != mDynamic.end())
return &dit->second;
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);
typename Static::const_iterator it = mStatic.find(id);
if (it != mStatic.end())
return &(it->second);
@ -113,8 +111,7 @@ namespace MWWorld
template<typename T>
const T *Store<T>::searchStatic(const std::string &id) const
{
std::string idLower = Misc::StringUtils::lowerCase(id);
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);
typename Static::const_iterator it = mStatic.find(id);
if (it != mStatic.end())
return &(it->second);
@ -159,7 +156,7 @@ namespace MWWorld
bool isDeleted = false;
record.load(esm, isDeleted);
Misc::StringUtils::lowerCaseInPlace(record.mId);
Misc::StringUtils::lowerCaseInPlace(record.mId); // TODO: remove this line once we have ported our remaining code base to lowercase on lookup
std::pair<typename Static::iterator, bool> inserted = mStatic.insert_or_assign(record.mId, record);
if (inserted.second)
@ -206,14 +203,13 @@ namespace MWWorld
template<typename T>
T *Store<T>::insert(const T &item, bool overrideOnly)
{
std::string id = Misc::StringUtils::lowerCase(item.mId);
if(overrideOnly)
{
auto it = mStatic.find(id);
auto it = mStatic.find(item.mId);
if(it == mStatic.end())
return nullptr;
}
std::pair<typename Dynamic::iterator, bool> result = mDynamic.insert_or_assign(id, item);
std::pair<typename Dynamic::iterator, bool> result = mDynamic.insert_or_assign(item.mId, item);
T *ptr = &result.first->second;
if (result.second)
mShared.push_back(ptr);
@ -222,8 +218,7 @@ namespace MWWorld
template<typename T>
T *Store<T>::insertStatic(const T &item)
{
std::string id = Misc::StringUtils::lowerCase(item.mId);
std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(id, item);
std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(item.mId, item);
T *ptr = &result.first->second;
if (result.second)
mShared.push_back(ptr);
@ -232,9 +227,7 @@ namespace MWWorld
template<typename T>
bool Store<T>::eraseStatic(const std::string &id)
{
std::string idLower = Misc::StringUtils::lowerCase(id);
typename std::map<std::string, T>::iterator it = mStatic.find(idLower);
typename Static::iterator it = mStatic.find(id);
if (it != mStatic.end()) {
// delete from the static part of mShared
@ -242,7 +235,7 @@ namespace MWWorld
typename std::vector<T *>::iterator end = sharedIter + mStatic.size();
while (sharedIter != mShared.end() && sharedIter != end) {
if((*sharedIter)->mId == idLower) {
if(Misc::StringUtils::ciEqual((*sharedIter)->mId, id)) {
mShared.erase(sharedIter);
break;
}
@ -257,17 +250,13 @@ namespace MWWorld
template<typename T>
bool Store<T>::erase(const std::string &id)
{
std::string key = Misc::StringUtils::lowerCase(id);
typename Dynamic::iterator it = mDynamic.find(key);
if (it == mDynamic.end()) {
if (!mDynamic.erase(id))
return false;
}
mDynamic.erase(it);
// have to reinit the whole shared part
assert(mShared.size() >= mStatic.size());
mShared.erase(mShared.begin() + mStatic.size(), mShared.end());
for (it = mDynamic.begin(); it != mDynamic.end(); ++it) {
for (auto it = mDynamic.begin(); it != mDynamic.end(); ++it) {
mShared.push_back(&it->second);
}
return true;
@ -353,12 +342,8 @@ namespace MWWorld
ESM::LandTexture* tex = const_cast<ESM::LandTexture*>(search(lt.mIndex, i));
if (tex)
{
const std::string texId = Misc::StringUtils::lowerCase(tex->mId);
const std::string ltId = Misc::StringUtils::lowerCase(lt.mId);
if (texId == ltId)
{
if (Misc::StringUtils::ciEqual(tex->mId, lt.mId))
tex->mTexture = lt.mTexture;
}
}
}
@ -367,9 +352,10 @@ namespace MWWorld
ltexl.resize(lt.mIndex+1);
// Store it
ltexl[lt.mIndex] = lt;
auto idx = lt.mIndex;
ltexl[idx] = std::move(lt);
return RecordId(lt.mId, isDeleted);
return RecordId(ltexl[idx].mId, isDeleted);
}
RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm)
{
@ -503,16 +489,12 @@ namespace MWWorld
}
const ESM::Cell *Store<ESM::Cell>::search(const std::string &id) const
{
ESM::Cell cell;
cell.mName = Misc::StringUtils::lowerCase(id);
std::map<std::string, ESM::Cell>::const_iterator it = mInt.find(cell.mName);
DynamicInt::const_iterator it = mInt.find(id);
if (it != mInt.end()) {
return &(it->second);
}
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);
DynamicInt::const_iterator dit = mDynamicInt.find(id);
if (dit != mDynamicInt.end()) {
return &dit->second;
}
@ -521,48 +503,34 @@ namespace MWWorld
}
const ESM::Cell *Store<ESM::Cell>::search(int x, int y) const
{
ESM::Cell cell;
cell.mData.mX = x;
cell.mData.mY = y;
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) {
if (it != mExt.end())
return &(it->second);
}
DynamicExt::const_iterator dit = mDynamicExt.find(key);
if (dit != mDynamicExt.end()) {
if (dit != mDynamicExt.end())
return &dit->second;
}
return nullptr;
}
const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const
{
ESM::Cell cell;
cell.mData.mX = x;
cell.mData.mY = y;
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) {
DynamicExt::const_iterator it = mExt.find(std::make_pair(x,y));
if (it != mExt.end())
return &(it->second);
}
return nullptr;
}
const ESM::Cell *Store<ESM::Cell>::searchOrCreate(int x, int y)
{
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) {
if (it != mExt.end())
return &(it->second);
}
DynamicExt::const_iterator dit = mDynamicExt.find(key);
if (dit != mDynamicExt.end()) {
if (dit != mDynamicExt.end())
return &dit->second;
}
ESM::Cell newCell;
newCell.mData.mX = x;
@ -625,12 +593,11 @@ namespace MWWorld
// Load the (x,y) coordinates of the cell, if it is an exterior cell,
// so we can find the cell we need to merge with
cell.loadNameAndData(esm, isDeleted);
std::string idLower = Misc::StringUtils::lowerCase(cell.mName);
if(cell.mData.mFlags & ESM::Cell::Interior)
{
// Store interior cell by name, try to merge with existing parent data.
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell.mName));
if (oldcell) {
// merge new cell into old cell
// push the new references on the list of references to manage (saveContext = true)
@ -642,7 +609,7 @@ namespace MWWorld
// spawn a new cell
cell.loadCell(esm, true);
mInt[idLower] = cell;
mInt[cell.mName] = cell;
}
}
else
@ -780,27 +747,19 @@ namespace MWWorld
const std::string cellType = (cell.isExterior()) ? "exterior" : "interior";
throw std::runtime_error("Failed to create " + cellType + " cell");
}
ESM::Cell *ptr;
if (cell.isExterior()) {
std::pair<int, int> key(cell.getGridX(), cell.getGridY());
// duplicate insertions are avoided by search(ESM::Cell &)
std::pair<DynamicExt::iterator, bool> result =
mDynamicExt.insert(std::make_pair(key, cell));
ptr = &result.first->second;
mSharedExt.push_back(ptr);
DynamicExt::iterator result = mDynamicExt.emplace(key, cell).first;
mSharedExt.push_back(&result->second);
return &result->second;
} else {
std::string key = Misc::StringUtils::lowerCase(cell.mName);
// duplicate insertions are avoided by search(ESM::Cell &)
std::pair<DynamicInt::iterator, bool> result =
mDynamicInt.insert(std::make_pair(key, cell));
ptr = &result.first->second;
mSharedInt.push_back(ptr);
DynamicInt::iterator result = mDynamicInt.emplace(cell.mName, cell).first;
mSharedInt.push_back(&result->second);
return &result->second;
}
return ptr;
}
bool Store<ESM::Cell>::erase(const ESM::Cell &cell)
{
@ -811,8 +770,7 @@ namespace MWWorld
}
bool Store<ESM::Cell>::erase(const std::string &id)
{
std::string key = Misc::StringUtils::lowerCase(id);
DynamicInt::iterator it = mDynamicInt.find(key);
DynamicInt::iterator it = mDynamicInt.find(id);
if (it == mDynamicInt.end()) {
return false;
@ -1062,12 +1020,11 @@ namespace MWWorld
dialogue.loadId(esm);
std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId);
std::map<std::string, ESM::Dialogue>::iterator found = mStatic.find(idLower);
Static::iterator found = mStatic.find(dialogue.mId);
if (found == mStatic.end())
{
dialogue.loadData(esm, isDeleted);
mStatic.insert(std::make_pair(idLower, dialogue));
mStatic.emplace(dialogue.mId, dialogue);
}
else
{
@ -1081,11 +1038,7 @@ namespace MWWorld
template<>
bool Store<ESM::Dialogue>::eraseStatic(const std::string &id)
{
auto it = mStatic.find(Misc::StringUtils::lowerCase(id));
if (it != mStatic.end())
mStatic.erase(it);
mStatic.erase(id);
return true;
}

View File

@ -5,6 +5,7 @@
#include <vector>
#include <memory>
#include <map>
#include <unordered_map>
#include <set>
#include "recordcmp.hpp"
@ -147,14 +148,13 @@ namespace MWWorld
template <class T>
class Store : public StoreBase
{
std::map<std::string, T> mStatic;
std::vector<T *> mShared; // Preserves the record order as it came from the content files (this
typedef std::unordered_map<std::string, T, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Static;
Static mStatic;
std::vector<T*> mShared; // Preserves the record order as it came from the content files (this
// is relevant for the spell autocalc code and selection order
// for heads/hairs in the character creation)
std::map<std::string, T> mDynamic;
typedef std::map<std::string, T> Dynamic;
typedef std::map<std::string, T> Static;
typedef std::unordered_map<std::string, T, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Dynamic;
Dynamic mDynamic;
friend class ESMStore;
@ -294,7 +294,7 @@ namespace MWWorld
}
};
typedef std::map<std::string, ESM::Cell> DynamicInt;
typedef std::unordered_map<std::string, ESM::Cell, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> DynamicInt;
typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp> DynamicExt;
DynamicInt mInt;
@ -354,7 +354,7 @@ namespace MWWorld
class Store<ESM::Pathgrid> : public StoreBase
{
private:
typedef std::map<std::string, ESM::Pathgrid> Interior;
typedef std::unordered_map<std::string, ESM::Pathgrid, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Interior;
typedef std::map<std::pair<int, int>, ESM::Pathgrid> Exterior;
Interior mInt;