CBonusProxy.cpp 5.1 KB

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