From 0688f55171628a91080e0b23f95678ec600a5b61 Mon Sep 17 00:00:00 2001 From: myrix Date: Sat, 18 Dec 2021 20:07:41 +0300 Subject: [PATCH 1/5] optimized keyword parsing --- apps/openmw/mwdialogue/hypertextparser.cpp | 26 +++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index caafa5f324..389488cbe1 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -47,19 +47,25 @@ namespace MWDialogue void tokenizeKeywords(const std::string & text, std::vector & tokens) { - const MWWorld::Store & dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); + static bool keywordSearchInitialized = false; + static KeywordSearch keywordSearch; - std::vector keywordList; - keywordList.reserve(dialogs.getSize()); - for (const auto& it : dialogs) - keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); - sort(keywordList.begin(), keywordList.end()); + if (!keywordSearchInitialized) + { + const MWWorld::Store & dialogs = + MWBase::Environment::get().getWorld()->getStore().get(); - KeywordSearch keywordSearch; + std::vector keywordList; + keywordList.reserve(dialogs.getSize()); + for (const auto& it : dialogs) + keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); + sort(keywordList.begin(), keywordList.end()); - for (const auto& it : keywordList) - keywordSearch.seed(it, 0 /*unused*/); + for (const auto& it : keywordList) + keywordSearch.seed(it, 0 /*unused*/); + + keywordSearchInitialized = true; + } std::vector::Match> matches; keywordSearch.highlightKeywords(text.begin(), text.end(), matches); From 3a9cfbfa5340b80dd8949a0334ddea72a58eb39a Mon Sep 17 00:00:00 2001 From: myrix Date: Sun, 19 Dec 2021 14:00:49 +0300 Subject: [PATCH 2/5] HyperTextParser as a class with proper keyword search caching --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 + apps/openmw/mwdialogue/hypertextparser.cpp | 134 +++++++++--------- apps/openmw/mwdialogue/hypertextparser.hpp | 15 +- apps/openmw/mwworld/store.cpp | 26 +++- apps/openmw/mwworld/store.hpp | 4 + 6 files changed, 109 insertions(+), 77 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 9800f1b39c..0698624f32 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -43,7 +43,6 @@ #include "../mwmechanics/actorutil.hpp" #include "filter.hpp" -#include "hypertextparser.hpp" namespace MWDialogue { @@ -80,7 +79,7 @@ namespace MWDialogue { std::vector topicIdList; - std::vector hypertext = HyperTextParser::parseHyperText(text); + std::vector hypertext = mHyperTextParser.parseHyperText(text); for (std::vector::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 57eb74d0a6..1fc80db666 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -16,6 +16,8 @@ #include "../mwscript/compilercontext.hpp" +#include "hypertextparser.hpp" + namespace ESM { struct Dialogue; @@ -57,6 +59,8 @@ namespace MWDialogue int mCurrentDisposition; int mPermanentDispositionChange; + HyperTextParser mHyperTextParser; + std::vector parseTopicIdsFromText (const std::string& text); void addTopicsFromText (const std::string& text); diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index 389488cbe1..eae36d26d1 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -6,95 +6,89 @@ #include "../mwworld/store.hpp" #include "../mwworld/esmstore.hpp" -#include "keywordsearch.hpp" - #include "hypertextparser.hpp" namespace MWDialogue { - namespace HyperTextParser + std::vector HyperTextParser::parseHyperText(const std::string & text) { - std::vector parseHyperText(const std::string & text) + std::vector result; + size_t pos_end = std::string::npos, iteration_pos = 0; + for(;;) { - std::vector result; - size_t pos_end = std::string::npos, iteration_pos = 0; - for(;;) + size_t pos_begin = text.find('@', iteration_pos); + if (pos_begin != std::string::npos) + pos_end = text.find('#', pos_begin); + + if (pos_begin != std::string::npos && pos_end != std::string::npos) { - size_t pos_begin = text.find('@', iteration_pos); - if (pos_begin != std::string::npos) - pos_end = text.find('#', pos_begin); + if (pos_begin != iteration_pos) + tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); - if (pos_begin != std::string::npos && pos_end != std::string::npos) - { - if (pos_begin != iteration_pos) - tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); + std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); + result.emplace_back(link, Token::ExplicitLink); - std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); - result.emplace_back(link, Token::ExplicitLink); - - iteration_pos = pos_end + 1; - } - else - { - if (iteration_pos != text.size()) - tokenizeKeywords(text.substr(iteration_pos), result); - break; - } + iteration_pos = pos_end + 1; } - - return result; - } - - void tokenizeKeywords(const std::string & text, std::vector & tokens) - { - static bool keywordSearchInitialized = false; - static KeywordSearch keywordSearch; - - if (!keywordSearchInitialized) + else { - const MWWorld::Store & dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::vector keywordList; - keywordList.reserve(dialogs.getSize()); - for (const auto& it : dialogs) - keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); - sort(keywordList.begin(), keywordList.end()); - - for (const auto& it : keywordList) - keywordSearch.seed(it, 0 /*unused*/); - - keywordSearchInitialized = true; - } - - std::vector::Match> matches; - keywordSearch.highlightKeywords(text.begin(), text.end(), matches); - - for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) - { - tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword); + if (iteration_pos != text.size()) + tokenizeKeywords(text.substr(iteration_pos), result); + break; } } - size_t removePseudoAsterisks(std::string & phrase) + return result; + } + + void HyperTextParser::tokenizeKeywords(const std::string & text, std::vector & tokens) + { + const MWWorld::Store & dialogs = + MWBase::Environment::get().getWorld()->getStore().get(); + + if (dialogs.getModPoint() != mKeywordModPoint) { - size_t pseudoAsterisksCount = 0; + mKeywordSearch.clear(); - if( !phrase.empty() ) - { - std::string::reverse_iterator rit = phrase.rbegin(); + std::vector keywordList; + keywordList.reserve(dialogs.getSize()); + for (const auto& it : dialogs) + keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); + sort(keywordList.begin(), keywordList.end()); - const char specialPseudoAsteriskCharacter = 127; - while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) - { - pseudoAsterisksCount++; - ++rit; - } - } + for (const auto& it : keywordList) + mKeywordSearch.seed(it, 0 /*unused*/); - phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); + mKeywordModPoint = dialogs.getModPoint(); + } - return pseudoAsterisksCount; + std::vector::Match> matches; + mKeywordSearch.highlightKeywords(text.begin(), text.end(), matches); + + for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) + { + tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword); } } + + size_t HyperTextParser::removePseudoAsterisks(std::string & phrase) + { + size_t pseudoAsterisksCount = 0; + + if( !phrase.empty() ) + { + std::string::reverse_iterator rit = phrase.rbegin(); + + const char specialPseudoAsteriskCharacter = 127; + while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) + { + pseudoAsterisksCount++; + ++rit; + } + } + + phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); + + return pseudoAsterisksCount; + } } diff --git a/apps/openmw/mwdialogue/hypertextparser.hpp b/apps/openmw/mwdialogue/hypertextparser.hpp index 4ae0474c42..1e903d5618 100644 --- a/apps/openmw/mwdialogue/hypertextparser.hpp +++ b/apps/openmw/mwdialogue/hypertextparser.hpp @@ -4,10 +4,17 @@ #include #include +#include "keywordsearch.hpp" + namespace MWDialogue { - namespace HyperTextParser + class HyperTextParser { + uint64_t mKeywordModPoint; + KeywordSearch mKeywordSearch; + + public: + struct Token { enum Type @@ -24,12 +31,14 @@ namespace MWDialogue Type mType; }; + HyperTextParser() : mKeywordModPoint(0) {} + // In translations (at least Russian) the links are marked with @#, so // it should be a function to parse it std::vector parseHyperText(const std::string & text); void tokenizeKeywords(const std::string & text, std::vector & tokens); - size_t removePseudoAsterisks(std::string & phrase); - } + static size_t removePseudoAsterisks(std::string & phrase); + }; } #endif diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 4d720a11a7..5c696e5556 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -77,12 +77,13 @@ namespace MWWorld template Store::Store() + : mModPoint(1) { } template Store::Store(const Store& orig) - : mStatic(orig.mStatic) + : mStatic(orig.mStatic), mModPoint(orig.mModPoint + 1) { } @@ -93,6 +94,8 @@ namespace MWWorld assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); + + mModPoint++; } template @@ -162,6 +165,8 @@ namespace MWWorld if (inserted.second) mShared.push_back(&inserted.first->second); + mModPoint++; + return RecordId(record.mId, isDeleted); } template @@ -213,6 +218,9 @@ namespace MWWorld T *ptr = &result.first->second; if (result.second) mShared.push_back(ptr); + + mModPoint++; + return ptr; } template @@ -222,6 +230,9 @@ namespace MWWorld T *ptr = &result.first->second; if (result.second) mShared.push_back(ptr); + + mModPoint++; + return ptr; } template @@ -242,6 +253,8 @@ namespace MWWorld ++sharedIter; } mStatic.erase(it); + + mModPoint++; } return true; @@ -259,6 +272,9 @@ namespace MWWorld for (auto it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } + + mModPoint++; + return true; } template @@ -997,6 +1013,8 @@ namespace MWWorld // TODO: verify and document this inconsistent behaviour // TODO: if we require this behaviour, maybe we should move it to the place that requires it std::sort(mShared.begin(), mShared.end(), [](const ESM::Dialogue* l, const ESM::Dialogue* r) -> bool { return l->mId < r->mId; }); + + mModPoint++; } template <> @@ -1018,6 +1036,8 @@ namespace MWWorld found->second.loadData(esm, isDeleted); dialogue.mId = found->second.mId; } + + mModPoint++; return RecordId(dialogue.mId, isDeleted); } @@ -1025,7 +1045,9 @@ namespace MWWorld template<> bool Store::eraseStatic(const std::string &id) { - mStatic.erase(id); + if (mStatic.erase(id)) + mModPoint++; + return true; } diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 22f36da690..b4ac1056a8 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -159,6 +159,8 @@ namespace MWWorld typedef std::unordered_map Dynamic; Dynamic mDynamic; + uint64_t mModPoint; + friend class ESMStore; public: @@ -203,6 +205,8 @@ namespace MWWorld RecordId load(ESM::ESMReader &esm) override; void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override; RecordId read(ESM::ESMReader& reader, bool overrideOnly = false) override; + + uint64_t getModPoint() const { return mModPoint; } }; template <> From 27cc7a5172265b861e0b5b050f95df549534a0bc Mon Sep 17 00:00:00 2001 From: myrix Date: Fri, 24 Dec 2021 00:54:00 +0300 Subject: [PATCH 3/5] caching dialog keyword search in Store --- apps/openmw/mwbase/world.hpp | 6 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 - apps/openmw/mwdialogue/hypertextparser.cpp | 119 ++++++++---------- apps/openmw/mwdialogue/hypertextparser.hpp | 15 +-- apps/openmw/mwdialogue/keywordsearch.hpp | 10 +- apps/openmw/mwworld/esmstore.hpp | 3 + apps/openmw/mwworld/store.cpp | 87 +++++++++---- apps/openmw/mwworld/store.hpp | 42 ++++++- apps/openmw/mwworld/worldimp.hpp | 3 + 10 files changed, 177 insertions(+), 115 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d1747a2e39..d0ff851274 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -87,6 +87,11 @@ namespace MWWorld typedef std::vector > PtrMovementList; } +namespace MWDialogue +{ + template class KeywordSearch; +} + namespace MWBase { /// \brief Interface for the World (implemented in MWWorld) @@ -150,6 +155,7 @@ namespace MWBase virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0; virtual const MWWorld::ESMStore& getStore() const = 0; + virtual const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 0698624f32..9800f1b39c 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -43,6 +43,7 @@ #include "../mwmechanics/actorutil.hpp" #include "filter.hpp" +#include "hypertextparser.hpp" namespace MWDialogue { @@ -79,7 +80,7 @@ namespace MWDialogue { std::vector topicIdList; - std::vector hypertext = mHyperTextParser.parseHyperText(text); + std::vector hypertext = HyperTextParser::parseHyperText(text); for (std::vector::iterator tok = hypertext.begin(); tok != hypertext.end(); ++tok) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 1fc80db666..57eb74d0a6 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -16,8 +16,6 @@ #include "../mwscript/compilercontext.hpp" -#include "hypertextparser.hpp" - namespace ESM { struct Dialogue; @@ -59,8 +57,6 @@ namespace MWDialogue int mCurrentDisposition; int mPermanentDispositionChange; - HyperTextParser mHyperTextParser; - std::vector parseTopicIdsFromText (const std::string& text); void addTopicsFromText (const std::string& text); diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index eae36d26d1..c1fb6c9c43 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -6,89 +6,78 @@ #include "../mwworld/store.hpp" #include "../mwworld/esmstore.hpp" +#include "keywordsearch.hpp" + #include "hypertextparser.hpp" namespace MWDialogue { - std::vector HyperTextParser::parseHyperText(const std::string & text) + namespace HyperTextParser { - std::vector result; - size_t pos_end = std::string::npos, iteration_pos = 0; - for(;;) + std::vector parseHyperText(const std::string & text) { - size_t pos_begin = text.find('@', iteration_pos); - if (pos_begin != std::string::npos) - pos_end = text.find('#', pos_begin); - - if (pos_begin != std::string::npos && pos_end != std::string::npos) + std::vector result; + size_t pos_end = std::string::npos, iteration_pos = 0; + for(;;) { - if (pos_begin != iteration_pos) - tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); + size_t pos_begin = text.find('@', iteration_pos); + if (pos_begin != std::string::npos) + pos_end = text.find('#', pos_begin); - std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); - result.emplace_back(link, Token::ExplicitLink); + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + if (pos_begin != iteration_pos) + tokenizeKeywords(text.substr(iteration_pos, pos_begin - iteration_pos), result); - iteration_pos = pos_end + 1; + std::string link = text.substr(pos_begin + 1, pos_end - pos_begin - 1); + result.emplace_back(link, Token::ExplicitLink); + + iteration_pos = pos_end + 1; + } + else + { + if (iteration_pos != text.size()) + tokenizeKeywords(text.substr(iteration_pos), result); + break; + } } - else + + return result; + } + + void tokenizeKeywords(const std::string & text, std::vector & tokens) + { + const auto& keywordSearch = + MWBase::Environment::get().getWorld()->getDialogIdKeywordSearch(); + + std::vector::Match> matches; + keywordSearch.highlightKeywords(text.begin(), text.end(), matches); + + for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) { - if (iteration_pos != text.size()) - tokenizeKeywords(text.substr(iteration_pos), result); - break; + tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword); } } - return result; - } - - void HyperTextParser::tokenizeKeywords(const std::string & text, std::vector & tokens) - { - const MWWorld::Store & dialogs = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (dialogs.getModPoint() != mKeywordModPoint) + size_t removePseudoAsterisks(std::string & phrase) { - mKeywordSearch.clear(); + size_t pseudoAsterisksCount = 0; - std::vector keywordList; - keywordList.reserve(dialogs.getSize()); - for (const auto& it : dialogs) - keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); - sort(keywordList.begin(), keywordList.end()); - - for (const auto& it : keywordList) - mKeywordSearch.seed(it, 0 /*unused*/); - - mKeywordModPoint = dialogs.getModPoint(); - } - - std::vector::Match> matches; - mKeywordSearch.highlightKeywords(text.begin(), text.end(), matches); - - for (std::vector::Match>::const_iterator it = matches.begin(); it != matches.end(); ++it) - { - tokens.emplace_back(std::string(it->mBeg, it->mEnd), Token::ImplicitKeyword); - } - } - - size_t HyperTextParser::removePseudoAsterisks(std::string & phrase) - { - size_t pseudoAsterisksCount = 0; - - if( !phrase.empty() ) - { - std::string::reverse_iterator rit = phrase.rbegin(); - - const char specialPseudoAsteriskCharacter = 127; - while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) + if( !phrase.empty() ) { - pseudoAsterisksCount++; - ++rit; + std::string::reverse_iterator rit = phrase.rbegin(); + + const char specialPseudoAsteriskCharacter = 127; + while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) + { + pseudoAsterisksCount++; + ++rit; + } } + + phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); + + return pseudoAsterisksCount; } - - phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); - - return pseudoAsterisksCount; } } diff --git a/apps/openmw/mwdialogue/hypertextparser.hpp b/apps/openmw/mwdialogue/hypertextparser.hpp index 1e903d5618..4ae0474c42 100644 --- a/apps/openmw/mwdialogue/hypertextparser.hpp +++ b/apps/openmw/mwdialogue/hypertextparser.hpp @@ -4,17 +4,10 @@ #include #include -#include "keywordsearch.hpp" - namespace MWDialogue { - class HyperTextParser + namespace HyperTextParser { - uint64_t mKeywordModPoint; - KeywordSearch mKeywordSearch; - - public: - struct Token { enum Type @@ -31,14 +24,12 @@ namespace MWDialogue Type mType; }; - HyperTextParser() : mKeywordModPoint(0) {} - // In translations (at least Russian) the links are marked with @#, so // it should be a function to parse it std::vector parseHyperText(const std::string & text); void tokenizeKeywords(const std::string & text, std::vector & tokens); - static size_t removePseudoAsterisks(std::string & phrase); - }; + size_t removePseudoAsterisks(std::string & phrase); + } } #endif diff --git a/apps/openmw/mwdialogue/keywordsearch.hpp b/apps/openmw/mwdialogue/keywordsearch.hpp index 39599457ef..3f932084fe 100644 --- a/apps/openmw/mwdialogue/keywordsearch.hpp +++ b/apps/openmw/mwdialogue/keywordsearch.hpp @@ -74,13 +74,13 @@ public: return left.mBeg < right.mBeg; } - void highlightKeywords (Point beg, Point end, std::vector& out) + void highlightKeywords (Point beg, Point end, std::vector& out) const { std::vector matches; for (Point i = beg; i != end; ++i) { // check first character - typename Entry::childen_t::iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i)); + typename Entry::childen_t::const_iterator candidate = mRoot.mChildren.find (Misc::StringUtils::toLower (*i)); // no match, on to next character if (candidate == mRoot.mChildren.end ()) @@ -91,11 +91,11 @@ public: // some keywords might be longer variations of other keywords, so we definitely need a list of candidates // the first element in the pair is length of the match, i.e. depth from the first character on - std::vector< typename std::pair > candidates; + std::vector< typename std::pair > candidates; while ((j + 1) != end) { - typename Entry::childen_t::iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j)); + typename Entry::childen_t::const_iterator next = candidate->second.mChildren.find (Misc::StringUtils::toLower (*++j)); if (next == candidate->second.mChildren.end ()) { @@ -116,7 +116,7 @@ public: // shorter candidates will be added to the vector first. however, we want to check against longer candidates first std::reverse(candidates.begin(), candidates.end()); - for (typename std::vector< std::pair >::iterator it = candidates.begin(); + for (typename std::vector< std::pair >::iterator it = candidates.begin(); it != candidates.end(); ++it) { candidate = it->second; diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 8582a1daca..855dd062e9 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -284,6 +284,9 @@ namespace MWWorld /// Actors with the same ID share spells, abilities, etc. /// @return The shared spell list to use for this actor and whether or not it has already been initialized. std::pair, bool> getSpellList(const std::string& id) const; + + const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() { + return mDialogs.getDialogIdKeywordSearch(); } }; template <> diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 5c696e5556..a06bf843cb 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -77,13 +77,12 @@ namespace MWWorld template Store::Store() - : mModPoint(1) { } template Store::Store(const Store& orig) - : mStatic(orig.mStatic), mModPoint(orig.mModPoint + 1) + : mStatic(orig.mStatic) { } @@ -94,8 +93,6 @@ namespace MWWorld assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); - - mModPoint++; } template @@ -165,8 +162,6 @@ namespace MWWorld if (inserted.second) mShared.push_back(&inserted.first->second); - mModPoint++; - return RecordId(record.mId, isDeleted); } template @@ -218,9 +213,6 @@ namespace MWWorld T *ptr = &result.first->second; if (result.second) mShared.push_back(ptr); - - mModPoint++; - return ptr; } template @@ -230,9 +222,6 @@ namespace MWWorld T *ptr = &result.first->second; if (result.second) mShared.push_back(ptr); - - mModPoint++; - return ptr; } template @@ -253,8 +242,6 @@ namespace MWWorld ++sharedIter; } mStatic.erase(it); - - mModPoint++; } return true; @@ -272,9 +259,6 @@ namespace MWWorld for (auto it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } - - mModPoint++; - return true; } template @@ -997,8 +981,11 @@ namespace MWWorld // Dialogue //========================================================================= + Store::Store() + : mKeywordSearchModFlag(true) + { + } - template<> void Store::setUp() { // DialInfos marked as deleted are kept during the loading phase, so that the linked list @@ -1014,10 +1001,45 @@ namespace MWWorld // TODO: if we require this behaviour, maybe we should move it to the place that requires it std::sort(mShared.begin(), mShared.end(), [](const ESM::Dialogue* l, const ESM::Dialogue* r) -> bool { return l->mId < r->mId; }); - mModPoint++; + mKeywordSearchModFlag = true; + } + + const ESM::Dialogue *Store::search(const std::string &id) const + { + typename Static::const_iterator it = mStatic.find(id); + if (it != mStatic.end()) + return &(it->second); + + return nullptr; + } + + const ESM::Dialogue *Store::find(const std::string &id) const + { + const ESM::Dialogue *ptr = search(id); + if (ptr == nullptr) + { + std::stringstream msg; + msg << ESM::Dialogue::getRecordType() << " '" << id << "' not found"; + throw std::runtime_error(msg.str()); + } + return ptr; + } + + typename Store::iterator Store::begin() const + { + return mShared.begin(); + } + + typename Store::iterator Store::end() const + { + return mShared.end(); + } + + size_t Store::getSize() const + { + return mShared.size(); } - template <> inline RecordId Store::load(ESM::ESMReader &esm) { // The original letter case of a dialogue ID is saved, because it's printed ESM::Dialogue dialogue; @@ -1037,20 +1059,39 @@ namespace MWWorld dialogue.mId = found->second.mId; } - mModPoint++; + mKeywordSearchModFlag = true; return RecordId(dialogue.mId, isDeleted); } - template<> bool Store::eraseStatic(const std::string &id) { if (mStatic.erase(id)) - mModPoint++; + mKeywordSearchModFlag = true; return true; } + const MWDialogue::KeywordSearch& Store::getDialogIdKeywordSearch() + { + if (mKeywordSearchModFlag) + { + mKeywordSearch.clear(); + + std::vector keywordList; + keywordList.reserve(getSize()); + for (const auto& it : *this) + keywordList.push_back(Misc::StringUtils::lowerCase(it.mId)); + sort(keywordList.begin(), keywordList.end()); + + for (const auto& it : keywordList) + mKeywordSearch.seed(it, 0 /*unused*/); + + mKeywordSearchModFlag = false; + } + + return mKeywordSearch; + } } template class MWWorld::Store; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index b4ac1056a8..58ac1e6780 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -11,6 +11,8 @@ #include #include +#include "../mwdialogue/keywordsearch.hpp" + namespace ESM { struct Land; @@ -154,13 +156,10 @@ namespace MWWorld /// @par mShared usually 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) - /// @warning ESM::Dialogue Store currently implements a sorted order for unknown reasons. std::vector mShared; typedef std::unordered_map Dynamic; Dynamic mDynamic; - uint64_t mModPoint; - friend class ESMStore; public: @@ -205,8 +204,6 @@ namespace MWWorld RecordId load(ESM::ESMReader &esm) override; void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override; RecordId read(ESM::ESMReader& reader, bool overrideOnly = false) override; - - uint64_t getModPoint() const { return mModPoint; } }; template <> @@ -444,6 +441,41 @@ namespace MWWorld iterator end() const; }; + template <> + class Store : public StoreBase + { + typedef std::unordered_map Static; + Static mStatic; + /// @par mShared usually 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) + /// @warning ESM::Dialogue Store currently implements a sorted order for unknown reasons. + std::vector mShared; + + bool mKeywordSearchModFlag; + MWDialogue::KeywordSearch mKeywordSearch; + + public: + Store(); + + typedef SharedIterator iterator; + + void setUp() override; + + const ESM::Dialogue *search(const std::string &id) const; + const ESM::Dialogue *find(const std::string &id) const; + + iterator begin() const; + iterator end() const; + + size_t getSize() const override; + + bool eraseStatic(const std::string &id) override; + + RecordId load(ESM::ESMReader &esm) override; + + const MWDialogue::KeywordSearch& getDialogIdKeywordSearch(); + }; } //end namespace diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 84d97ce37c..bea7710a53 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -236,6 +236,9 @@ namespace MWWorld const MWWorld::ESMStore& getStore() const override; + const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() override { + return mStore.getDialogIdKeywordSearch(); } + std::vector& getEsmReader() override; LocalScripts& getLocalScripts() override; From 9c7835e27fe812becf82627d2bf6bb0132045317 Mon Sep 17 00:00:00 2001 From: myrix Date: Fri, 24 Dec 2021 02:04:26 +0300 Subject: [PATCH 4/5] explicit instantiation error fix --- apps/openmw/mwworld/store.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index a06bf843cb..1dc3ebf622 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1107,7 +1107,7 @@ template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; From 88fc038cebcc25106ef24026c0b29346e0298f3c Mon Sep 17 00:00:00 2001 From: myrix Date: Fri, 24 Dec 2021 12:04:26 +0300 Subject: [PATCH 5/5] with mutable, without World and ESMStore interface change --- apps/openmw/mwbase/world.hpp | 6 ------ apps/openmw/mwdialogue/hypertextparser.cpp | 2 +- apps/openmw/mwworld/esmstore.hpp | 3 --- apps/openmw/mwworld/store.cpp | 2 +- apps/openmw/mwworld/store.hpp | 6 +++--- apps/openmw/mwworld/worldimp.hpp | 3 --- 6 files changed, 5 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d0ff851274..d1747a2e39 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -87,11 +87,6 @@ namespace MWWorld typedef std::vector > PtrMovementList; } -namespace MWDialogue -{ - template class KeywordSearch; -} - namespace MWBase { /// \brief Interface for the World (implemented in MWWorld) @@ -155,7 +150,6 @@ namespace MWBase virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0; virtual const MWWorld::ESMStore& getStore() const = 0; - virtual const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() = 0; virtual std::vector& getEsmReader() = 0; diff --git a/apps/openmw/mwdialogue/hypertextparser.cpp b/apps/openmw/mwdialogue/hypertextparser.cpp index c1fb6c9c43..89a42bf2ea 100644 --- a/apps/openmw/mwdialogue/hypertextparser.cpp +++ b/apps/openmw/mwdialogue/hypertextparser.cpp @@ -48,7 +48,7 @@ namespace MWDialogue void tokenizeKeywords(const std::string & text, std::vector & tokens) { const auto& keywordSearch = - MWBase::Environment::get().getWorld()->getDialogIdKeywordSearch(); + MWBase::Environment::get().getWorld()->getStore().get().getDialogIdKeywordSearch(); std::vector::Match> matches; keywordSearch.highlightKeywords(text.begin(), text.end(), matches); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 855dd062e9..8582a1daca 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -284,9 +284,6 @@ namespace MWWorld /// Actors with the same ID share spells, abilities, etc. /// @return The shared spell list to use for this actor and whether or not it has already been initialized. std::pair, bool> getSpellList(const std::string& id) const; - - const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() { - return mDialogs.getDialogIdKeywordSearch(); } }; template <> diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 1dc3ebf622..c767bd669a 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1072,7 +1072,7 @@ namespace MWWorld return true; } - const MWDialogue::KeywordSearch& Store::getDialogIdKeywordSearch() + const MWDialogue::KeywordSearch& Store::getDialogIdKeywordSearch() const { if (mKeywordSearchModFlag) { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 58ac1e6780..1ec51ad5fd 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -452,8 +452,8 @@ namespace MWWorld /// @warning ESM::Dialogue Store currently implements a sorted order for unknown reasons. std::vector mShared; - bool mKeywordSearchModFlag; - MWDialogue::KeywordSearch mKeywordSearch; + mutable bool mKeywordSearchModFlag; + mutable MWDialogue::KeywordSearch mKeywordSearch; public: Store(); @@ -474,7 +474,7 @@ namespace MWWorld RecordId load(ESM::ESMReader &esm) override; - const MWDialogue::KeywordSearch& getDialogIdKeywordSearch(); + const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() const; }; } //end namespace diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index bea7710a53..84d97ce37c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -236,9 +236,6 @@ namespace MWWorld const MWWorld::ESMStore& getStore() const override; - const MWDialogue::KeywordSearch& getDialogIdKeywordSearch() override { - return mStore.getDialogIdKeywordSearch(); } - std::vector& getEsmReader() override; LocalScripts& getLocalScripts() override;