From 62c43451ef181e6a3a2d9c79f8c45b8fbdf90a1e Mon Sep 17 00:00:00 2001 From: beegee1 Date: Sat, 16 Jul 2011 13:57:25 +0000 Subject: [PATCH] * Updated bonus caching -> faster in particular cases, operations by limiter objects aren't cached --- client/CBattleInterface.cpp | 2 +- lib/HeroBonus.cpp | 135 ++++++++++++++++++++++-------------- lib/HeroBonus.h | 13 ++-- lib/NetPacksLib.cpp | 2 +- 4 files changed, 92 insertions(+), 60 deletions(-) diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index e26870a6f..7f68b5c73 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -1941,7 +1941,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent) { mouseHoveredStack = -1; int myNumber = -1; //number of hovered tile - for(int g=0; g #define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); BOOST_FOREACH(CBonusSystemNode *pname, lparents) #define FOREACH_RED_CHILD(pname) TNodes lchildren; getRedChildren(lchildren); BOOST_FOREACH(CBonusSystemNode *pname, lchildren) @@ -58,43 +59,45 @@ int BonusList::totalValue() const int indepMin = 0; bool hasIndepMin = false; - BOOST_FOREACH(Bonus *i, bonuses) + for (size_t i = 0; i < bonuses.size(); i++) { - switch(i->valType) + Bonus *b = bonuses[i]; + + switch(b->valType) { case Bonus::BASE_NUMBER: - base += i->val; + base += b->val; break; case Bonus::PERCENT_TO_ALL: - percentToAll += i->val; + percentToAll += b->val; break; case Bonus::PERCENT_TO_BASE: - percentToBase += i->val; + percentToBase += b->val; break; case Bonus::ADDITIVE_VALUE: - additive += i->val; + additive += b->val; break; case Bonus::INDEPENDENT_MAX: if (!hasIndepMax) { - indepMax = i->val; + indepMax = b->val; hasIndepMax = true; } else { - amax(indepMax, i->val); + amax(indepMax, b->val); } break; case Bonus::INDEPENDENT_MIN: if (!hasIndepMin) { - indepMin = i->val; + indepMin = b->val; hasIndepMin = true; } else { - amin(indepMin, i->val); + amin(indepMin, b->val); } break; @@ -137,8 +140,11 @@ Bonus * BonusList::getFirst(const CSelector &select) void BonusList::getModifiersWDescr(TModDescr &out) const { - BOOST_FOREACH(Bonus *i, bonuses) - out.push_back(std::make_pair(i->val, i->Description())); + for (size_t i = 0; i < bonuses.size(); i++) + { + Bonus *b = bonuses[i]; + out.push_back(std::make_pair(b->val, b->Description())); + } } void BonusList::getBonuses(boost::shared_ptr out, const CSelector &selector) const @@ -191,11 +197,11 @@ void BonusList::push_back(Bonus* const &x) CBonusSystemNode::incrementTreeChangedNum(); } -std::vector::iterator BonusList::erase(std::vector::iterator position) +std::vector::iterator BonusList::erase(const int position) { if (belongsToTree) CBonusSystemNode::incrementTreeChangedNum(); - return bonuses.erase(position); + return bonuses.erase(bonuses.begin() + position); } void BonusList::clear() @@ -241,17 +247,20 @@ int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const { + std::stringstream cachingStr; + cachingStr << "type_" << type << "s_" << subtype; + CSelector s = Selector::type(type); if(subtype != -1) s = s && Selector::subtype(subtype); - return valOfBonuses(s); + return valOfBonuses(s, cachingStr.str()); } -int IBonusBearer::valOfBonuses(const CSelector &selector) const +int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr) const { CSelector limit = 0; - boost::shared_ptr hlp = getAllBonuses(selector, limit, NULL); + boost::shared_ptr hlp = getAllBonuses(selector, limit, NULL, cachingStr); return hlp->totalValue(); } bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachingStr /*= ""*/) const @@ -261,36 +270,37 @@ bool IBonusBearer::hasBonus(const CSelector &selector, const std::string &cachin bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) const { + std::stringstream cachingStr; + cachingStr << "type_" << type << "s_" << subtype; + CSelector s = Selector::type(type); - std::string cachingStr = ""; if(subtype != -1) s = s && Selector::subtype(subtype); - else - { - cachingStr = "type_"; - cachingStr += ((char) type); - } - return hasBonus(s, cachingStr); + return hasBonus(s, cachingStr.str()); } void IBonusBearer::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const { - getModifiersWDescr(out, subtype != -1 ? Selector::typeSubtype(type, subtype) : Selector::type(type)); + std::stringstream cachingStr; + cachingStr << "type_" << type << "s_" << subtype; + getModifiersWDescr(out, subtype != -1 ? Selector::typeSubtype(type, subtype) : Selector::type(type), cachingStr.str()); } -void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector) const +void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector, const std::string &cachingStr /* =""*/) const { - getBonuses(selector)->getModifiersWDescr(out); + getBonuses(selector, cachingStr)->getModifiersWDescr(out); } int IBonusBearer::getBonusesCount(int from, int id) const { - return getBonusesCount(Selector::source(from, id)); + std::stringstream cachingStr; + cachingStr << "source_" << from << "id_" << id; + return getBonusesCount(Selector::source(from, id), cachingStr.str()); } -int IBonusBearer::getBonusesCount(const CSelector &selector) const +int IBonusBearer::getBonusesCount(const CSelector &selector, const std::string &cachingStr /* =""*/) const { - return getBonuses(selector)->size(); + return getBonuses(selector, cachingStr)->size(); } const boost::shared_ptr IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr /*= ""*/) const @@ -305,7 +315,9 @@ const boost::shared_ptr IBonusBearer::getBonuses(const CSelector &sel bool IBonusBearer::hasBonusFrom(ui8 source, ui32 sourceID) const { - return hasBonus(Selector::source(source,sourceID)); + std::stringstream cachingStr; + cachingStr << "source_" << source << "id_" << sourceID; + return hasBonus(Selector::source(source,sourceID), cachingStr.str()); } int IBonusBearer::MoraleVal() const @@ -313,8 +325,8 @@ int IBonusBearer::MoraleVal() const if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) || hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON)) return 0; - - int ret = valOfBonuses(Selector::type(Bonus::MORALE)); + + int ret = valOfBonuses(Bonus::MORALE); if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur amax(ret, +1); @@ -326,8 +338,8 @@ int IBonusBearer::LuckVal() const { if(hasBonusOfType(Bonus::NO_LUCK)) return 0; - - int ret = valOfBonuses(Selector::type(Bonus::LUCK)); + + int ret = valOfBonuses(Bonus::LUCK); if(hasBonusOfType(Bonus::SELF_LUCK)) //eg. halfling amax(ret, +1); @@ -339,7 +351,7 @@ si32 IBonusBearer::Attack() const { si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); - if(int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker + if (int frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker { ret += frenzyPower * Defense(false); } @@ -368,11 +380,15 @@ ui16 IBonusBearer::MaxHealth() const ui32 IBonusBearer::getMinDamage() const { - return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1)); + std::stringstream cachingStr; + cachingStr << "type_" << Bonus::CREATURE_DAMAGE << "s_0Otype_" << Bonus::CREATURE_DAMAGE << "s_1"; + return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1), cachingStr.str()); } ui32 IBonusBearer::getMaxDamage() const { - return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2)); + std::stringstream cachingStr; + cachingStr << "type_" << Bonus::CREATURE_DAMAGE << "s_0Otype_" << Bonus::CREATURE_DAMAGE << "s_2"; + return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0) || Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2), cachingStr.str()); } si32 IBonusBearer::manaLimit() const @@ -396,19 +412,21 @@ int IBonusBearer::getPrimSkillLevel(int id) const si32 IBonusBearer::magicResistance() const { - return valOfBonuses(Selector::type(Bonus::MAGIC_RESISTANCE)); + return valOfBonuses(Bonus::MAGIC_RESISTANCE); } bool IBonusBearer::isLiving() const //TODO: theoreticaly there exists "LIVING" bonus in stack experience documentation { - return(!hasBonus(Selector::type(Bonus::UNDEAD) || Selector::type(Bonus::NON_LIVING))); + std::stringstream cachingStr; + cachingStr << "type_" << Bonus::UNDEAD << "s_-1Otype_" << Bonus::NON_LIVING; + return(!hasBonus(Selector::type(Bonus::UNDEAD) || Selector::type(Bonus::NON_LIVING), cachingStr.str())); } const boost::shared_ptr IBonusBearer::getSpellBonuses() const { - std::string cachingStr = "source_"; - cachingStr += (char) Bonus::SPELL_EFFECT; - return getBonuses(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr); + std::stringstream cachingStr; + cachingStr << "source_" << Bonus::SPELL_EFFECT; + return getBonuses(Selector::sourceType(Bonus::SPELL_EFFECT), cachingStr.str()); } Bonus * CBonusSystemNode::getBonus(const CSelector &selector) @@ -458,9 +476,6 @@ void CBonusSystemNode::getAllBonusesRec(boost::shared_ptr out, const p->getAllBonusesRec(out, selector, limit, root ? root : this, caching); bonuses.getBonuses(out, selector, limit, caching); - - if(!root) - out->limit(*this); } const boost::shared_ptr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= NULL*/, const std::string &cachingStr /*= ""*/) const @@ -468,9 +483,12 @@ const boost::shared_ptr CBonusSystemNode::getAllBonuses(const CSelect boost::shared_ptr ret(new BonusList()); if (CBonusSystemNode::cachingEnabled) { + // Exclusive access for one thread static boost::mutex m; boost::mutex::scoped_lock lock(m); + // If the bonus system tree changes(state of a single node or the relations to each other) then + // cache all bonus objects. Selector objects doesn't matter. if (cachedLast != treeChanged) { getAllBonusesRec(ret, selector, limit, this, true); @@ -480,31 +498,44 @@ const boost::shared_ptr CBonusSystemNode::getAllBonuses(const CSelect cachedRequests.clear(); cachedLast = treeChanged; } - + + // If a bonus system request comes with a caching string then look up in the map if there are any + // pre-calculated bonus results. Limiters can't be cached so they have to be calculated. if (cachingStr != "") { std::map >::iterator it(cachedRequests.find(cachingStr)); if (cachedRequests.size() > 0 && it != cachedRequests.end()) { ret = it->second; + if (!root) + ret->limit(*this); + return ret; } } - - cachedBonuses.getBonuses(ret, selector, limit, false); - if (!root) - ret->limit(*this); + // Get the bonus results + cachedBonuses.getBonuses(ret, selector, limit, false); + + // Sets the results with the given caching string into the map if (cachingStr != "") cachedRequests[cachingStr] = ret; - + + // Calculate limiters + if (!root) + ret->limit(*this); return ret; } else { + // Get bonus results without caching enabled. getAllBonusesRec(ret, selector, limit, root, false); ret->eliminateDuplicates(); + + if(!root) + ret->limit(*this); + return ret; } } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 15b891a99..4829d4ad9 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -338,7 +338,7 @@ public: // wrapper functions of the STL vector container std::vector::size_type size() const { return bonuses.size(); } void push_back(Bonus* const &x); - std::vector::iterator erase (std::vector::iterator position); + std::vector::iterator erase (const int position); void clear(); void resize(std::vector::size_type sz, Bonus* c = NULL ); void insert(std::vector::iterator position, std::vector::size_type n, Bonus* const &x); @@ -348,8 +348,8 @@ public: Bonus *const &front() { return bonuses.front(); } Bonus *const &back() const { return bonuses.back(); } Bonus *const &front() const { return bonuses.front(); } - std::vector::iterator begin() { return bonuses.begin(); } - std::vector::iterator end() { return bonuses.end(); } + + // There should be no non-const access to provide solid,robust bonus caching std::vector::const_iterator begin() const { return bonuses.begin(); } std::vector::const_iterator end() const { return bonuses.end(); } std::vector::size_type operator-=(Bonus* const &i); @@ -398,6 +398,7 @@ public: }; // Extensions for BOOST_FOREACH to enable iterating of BonusList objects +// Don't touch/call this functions inline std::vector::iterator range_begin(BonusList & x) { return x.bonuses.begin(); @@ -465,9 +466,9 @@ public: // * root is node on which call was made (NULL will be replaced with this) //interface virtual const boost::shared_ptr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = NULL, const std::string &cachingStr = "") const = 0; - void getModifiersWDescr(TModDescr &out, const CSelector &selector) const; //out: pairs - int getBonusesCount(const CSelector &selector) const; - int valOfBonuses(const CSelector &selector) const; + void getModifiersWDescr(TModDescr &out, const CSelector &selector, const std::string &cachingStr = "") const; //out: pairs + int getBonusesCount(const CSelector &selector, const std::string &cachingStr = "") const; + int valOfBonuses(const CSelector &selector, const std::string &cachingStr = "") const; bool hasBonus(const CSelector &selector, const std::string &cachingStr = "") const; const boost::shared_ptr getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = "") const; const boost::shared_ptr getBonuses(const CSelector &selector, const std::string &cachingStr = "") const; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 8bcce6424..8b8d2e146 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -231,7 +231,7 @@ DLL_EXPORT void RemoveBonus::applyGs( CGameState *gs ) if(b->source == source && b->sid == id) { bonus = *b; //backup bonus (to show to interfaces later) - bonuses.erase(bonuses.begin() + i); + bonuses.erase(i); break; } }