mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:59:54 +00:00
e892c62b10
Topic info records need to have specific order defined via mNext and mPrev fields (next and previous records). When loading multiple files a record may be inserted into middle of the topic but neighborhood records may not be aware of it. Having the order it's possible to move the records within one topic. Sort the record once after loading all content files but preserve the order for all other operations. Use std::map to group info ids by topic to make sure the topics order is stable. Keep order within a topic for info ids on loading new records. Use this order later for sorting the records.
114 lines
3.1 KiB
C++
114 lines
3.1 KiB
C++
#ifndef OPENMW_COMPONENTS_ESM3_INFOORDER_H
|
|
#define OPENMW_COMPONENTS_ESM3_INFOORDER_H
|
|
|
|
#include "components/esm/refid.hpp"
|
|
|
|
#include <iterator>
|
|
#include <list>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace ESM
|
|
{
|
|
template <class T>
|
|
class InfoOrder
|
|
{
|
|
public:
|
|
const std::list<T>& getOrderedInfo() const { return mOrderedInfo; }
|
|
|
|
template <class V>
|
|
void insertInfo(V&& value, bool deleted)
|
|
{
|
|
static_assert(std::is_same_v<std::decay_t<V>, T>);
|
|
|
|
auto it = mInfoPositions.find(value.mId);
|
|
|
|
if (it != mInfoPositions.end() && it->second.mPosition->mPrev == value.mPrev)
|
|
{
|
|
*it->second.mPosition = std::forward<V>(value);
|
|
it->second.mDeleted = deleted;
|
|
return;
|
|
}
|
|
|
|
if (it == mInfoPositions.end())
|
|
it = mInfoPositions.emplace(value.mId, Item{ .mPosition = mOrderedInfo.end(), .mDeleted = deleted })
|
|
.first;
|
|
|
|
Item& item = it->second;
|
|
|
|
const auto insertOrSplice = [&](typename std::list<T>::const_iterator before) {
|
|
if (item.mPosition == mOrderedInfo.end())
|
|
item.mPosition = mOrderedInfo.insert(before, std::forward<V>(value));
|
|
else
|
|
mOrderedInfo.splice(before, mOrderedInfo, item.mPosition);
|
|
};
|
|
|
|
if (value.mPrev.empty())
|
|
{
|
|
insertOrSplice(mOrderedInfo.begin());
|
|
return;
|
|
}
|
|
|
|
const auto prevIt = mInfoPositions.find(value.mPrev);
|
|
if (prevIt != mInfoPositions.end())
|
|
{
|
|
insertOrSplice(std::next(prevIt->second.mPosition));
|
|
return;
|
|
}
|
|
|
|
const auto nextIt = mInfoPositions.find(value.mNext);
|
|
if (nextIt != mInfoPositions.end())
|
|
{
|
|
insertOrSplice(nextIt->second.mPosition);
|
|
return;
|
|
}
|
|
|
|
insertOrSplice(mOrderedInfo.end());
|
|
}
|
|
|
|
void removeInfo(const RefId& infoRefId)
|
|
{
|
|
const auto it = mInfoPositions.find(infoRefId);
|
|
|
|
if (it == mInfoPositions.end())
|
|
return;
|
|
|
|
mOrderedInfo.erase(it->second.mPosition);
|
|
mInfoPositions.erase(it);
|
|
}
|
|
|
|
void removeDeleted()
|
|
{
|
|
for (auto it = mInfoPositions.begin(); it != mInfoPositions.end();)
|
|
{
|
|
if (!it->second.mDeleted)
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
mOrderedInfo.erase(it->second.mPosition);
|
|
it = mInfoPositions.erase(it);
|
|
}
|
|
}
|
|
|
|
void extractOrderedInfo(std::list<T>& info)
|
|
{
|
|
info = mOrderedInfo;
|
|
mInfoPositions.clear();
|
|
}
|
|
|
|
private:
|
|
struct Item
|
|
{
|
|
typename std::list<T>::iterator mPosition;
|
|
bool mDeleted = false;
|
|
};
|
|
|
|
std::list<T> mOrderedInfo;
|
|
std::unordered_map<RefId, Item> mInfoPositions;
|
|
};
|
|
}
|
|
|
|
#endif
|