CBonusProxy.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * CBonusProxy.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "BonusList.h"
  12. #include "CBonusProxy.h"
  13. #include "IBonusBearer.h"
  14. VCMI_LIB_NAMESPACE_BEGIN
  15. ///CBonusProxy
  16. CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector):
  17. bonusListCachedLast(0),
  18. target(Target),
  19. selector(std::move(Selector)),
  20. currentBonusListIndex(0)
  21. {
  22. }
  23. CBonusProxy::CBonusProxy(const CBonusProxy & other):
  24. bonusListCachedLast(other.bonusListCachedLast),
  25. target(other.target),
  26. selector(other.selector),
  27. currentBonusListIndex(other.currentBonusListIndex)
  28. {
  29. bonusList[currentBonusListIndex] = other.bonusList[currentBonusListIndex];
  30. }
  31. CBonusProxy::CBonusProxy(CBonusProxy && other) noexcept:
  32. bonusListCachedLast(0),
  33. target(other.target),
  34. currentBonusListIndex(0)
  35. {
  36. std::swap(bonusListCachedLast, other.bonusListCachedLast);
  37. std::swap(selector, other.selector);
  38. std::swap(bonusList, other.bonusList);
  39. std::swap(currentBonusListIndex, other.currentBonusListIndex);
  40. }
  41. CBonusProxy & CBonusProxy::operator=(const CBonusProxy & other)
  42. {
  43. boost::lock_guard<boost::mutex> lock(swapGuard);
  44. selector = other.selector;
  45. swapBonusList(other.bonusList[other.currentBonusListIndex]);
  46. bonusListCachedLast = other.bonusListCachedLast;
  47. return *this;
  48. }
  49. CBonusProxy & CBonusProxy::operator=(CBonusProxy && other) noexcept
  50. {
  51. std::swap(bonusListCachedLast, other.bonusListCachedLast);
  52. std::swap(selector, other.selector);
  53. std::swap(bonusList, other.bonusList);
  54. std::swap(currentBonusListIndex, other.currentBonusListIndex);
  55. return *this;
  56. }
  57. void CBonusProxy::swapBonusList(TConstBonusListPtr other) const
  58. {
  59. // The idea here is to avoid changing active bonusList while it can be read by a different thread.
  60. // Because such use of shared ptr is not thread safe
  61. // So to avoid this we change the second offline instance and swap active index
  62. auto newCurrent = 1 - currentBonusListIndex;
  63. bonusList[newCurrent] = std::move(other);
  64. currentBonusListIndex = newCurrent;
  65. }
  66. TConstBonusListPtr CBonusProxy::getBonusList() const
  67. {
  68. auto needUpdateBonusList = [&]() -> bool
  69. {
  70. return target->getTreeVersion() != bonusListCachedLast || !bonusList[currentBonusListIndex];
  71. };
  72. // avoid locking if everything is up-to-date
  73. if(needUpdateBonusList())
  74. {
  75. boost::lock_guard<boost::mutex>lock(swapGuard);
  76. if(needUpdateBonusList())
  77. {
  78. //TODO: support limiters
  79. swapBonusList(target->getAllBonuses(selector, Selector::all));
  80. bonusListCachedLast = target->getTreeVersion();
  81. }
  82. }
  83. return bonusList[currentBonusListIndex];
  84. }
  85. const BonusList * CBonusProxy::operator->() const
  86. {
  87. return getBonusList().get();
  88. }
  89. CTotalsProxy::CTotalsProxy(const IBonusBearer * Target, CSelector Selector, int InitialValue):
  90. CBonusProxy(Target, std::move(Selector)),
  91. initialValue(InitialValue),
  92. meleeCachedLast(0),
  93. meleeValue(0),
  94. rangedCachedLast(0),
  95. rangedValue(0)
  96. {
  97. }
  98. CTotalsProxy::CTotalsProxy(const CTotalsProxy & other)
  99. : CBonusProxy(other),
  100. initialValue(other.initialValue),
  101. meleeCachedLast(other.meleeCachedLast),
  102. meleeValue(other.meleeValue),
  103. rangedCachedLast(other.rangedCachedLast),
  104. rangedValue(other.rangedValue)
  105. {
  106. }
  107. int CTotalsProxy::getValue() const
  108. {
  109. const auto treeVersion = target->getTreeVersion();
  110. if(treeVersion != valueCachedLast)
  111. {
  112. auto bonuses = getBonusList();
  113. value = initialValue + bonuses->totalValue();
  114. valueCachedLast = treeVersion;
  115. }
  116. return value;
  117. }
  118. int CTotalsProxy::getValueAndList(TConstBonusListPtr & outBonusList) const
  119. {
  120. const auto treeVersion = target->getTreeVersion();
  121. outBonusList = getBonusList();
  122. if(treeVersion != valueCachedLast)
  123. {
  124. value = initialValue + outBonusList->totalValue();
  125. valueCachedLast = treeVersion;
  126. }
  127. return value;
  128. }
  129. int CTotalsProxy::getMeleeValue() const
  130. {
  131. static const auto limit = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_MELEE_FIGHT));
  132. const auto treeVersion = target->getTreeVersion();
  133. if(treeVersion != meleeCachedLast)
  134. {
  135. auto bonuses = target->getBonuses(selector, limit);
  136. meleeValue = initialValue + bonuses->totalValue();
  137. meleeCachedLast = treeVersion;
  138. }
  139. return meleeValue;
  140. }
  141. int CTotalsProxy::getRangedValue() const
  142. {
  143. static const auto limit = Selector::effectRange()(BonusLimitEffect::NO_LIMIT).Or(Selector::effectRange()(BonusLimitEffect::ONLY_DISTANCE_FIGHT));
  144. const auto treeVersion = target->getTreeVersion();
  145. if(treeVersion != rangedCachedLast)
  146. {
  147. auto bonuses = target->getBonuses(selector, limit);
  148. rangedValue = initialValue + bonuses->totalValue();
  149. rangedCachedLast = treeVersion;
  150. }
  151. return rangedValue;
  152. }
  153. ///CCheckProxy
  154. CCheckProxy::CCheckProxy(const IBonusBearer * Target, BonusType bonusType):
  155. target(Target),
  156. selector(Selector::type()(bonusType)),
  157. cachingStr("type_" + std::to_string(static_cast<int>(bonusType))),
  158. cachedLast(0),
  159. hasBonus(false)
  160. {
  161. }
  162. CCheckProxy::CCheckProxy(const IBonusBearer * Target, CSelector Selector, const std::string & cachingStr):
  163. target(Target),
  164. selector(std::move(Selector)),
  165. cachedLast(0),
  166. cachingStr(cachingStr),
  167. hasBonus(false)
  168. {
  169. }
  170. //This constructor should be placed here to avoid side effects
  171. CCheckProxy::CCheckProxy(const CCheckProxy & other) = default;
  172. bool CCheckProxy::getHasBonus() const
  173. {
  174. const auto treeVersion = target->getTreeVersion();
  175. if(treeVersion != cachedLast)
  176. {
  177. hasBonus = target->hasBonus(selector, cachingStr);
  178. cachedLast = treeVersion;
  179. }
  180. return hasBonus;
  181. }
  182. VCMI_LIB_NAMESPACE_END