aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2018-02-05 01:33:24 +0100
committerShauren <shauren.trinity@gmail.com>2021-06-19 23:33:24 +0200
commit809c38994a47b972754fcd5379e8730bdab31e33 (patch)
treef10c07093cd14b03f102ae02b368d48e596d346b /src
parentd34292534c197887cd33c15ff98ca8c96dc4fb99 (diff)
Core/Threat: Custom ThreatListIterator that transparently iterates over the backing map (instead of using heap iterators) when working with the unsorted threat list. This greatly reduces the range of actions that can cause iterator invalidation.
Also some minor adjustments to .debug threat, which no longer hides certain invalid states from view. (cherry picked from commit 4cb13af4faff0c1f9dbda2ab6a86882b4e4201a6)
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Combat/ThreatManager.h26
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp79
2 files changed, 66 insertions, 39 deletions
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
index 14ee60aea77..09717ff66fb 100644
--- a/src/server/game/Combat/ThreatManager.h
+++ b/src/server/game/Combat/ThreatManager.h
@@ -69,8 +69,9 @@ class SpellInfo;
* The current (= last selected) victim can be accessed using GetCurrentVictim. SelectVictim selects a (potentially new) victim. *
* Beyond that, ThreatManager has a variety of helpers and notifiers, which are documented inline below. *
* *
- * SPECIAL NOTE: Please be aware that any heap iterator may be invalidated if you modify a ThreatReference. The heap holds const pointers for a reason. *
- * If you need to modify multiple ThreatReference objects, then use GetModifiableThreatList(), which is safe to modify! *
+ * SPECIAL NOTE: Please be aware that any iterator may be invalidated if you modify a ThreatReference. The heap holds const pointers for a reason, but *
+ * that doesn't mean you're scot free. A variety of actions (casting spells, teleporting units, and so forth) can cause changes to *
+ * the threat list. Use with care - or default to GetModifiableThreatList(), which inherently copies entries. *
\********************************************************************************************************************************************************/
class ThreatReference;
@@ -85,6 +86,7 @@ class TC_GAME_API ThreatManager
{
public:
typedef boost::heap::fibonacci_heap<ThreatReference const*, boost::heap::compare<CompareThreatLessThan>> threat_list_heap;
+ class ThreatListIterator;
static const uint32 CLIENT_THREAT_UPDATE_INTERVAL = 1000u;
static bool CanHaveThreatList(Unit const* who);
@@ -121,11 +123,13 @@ class TC_GAME_API ThreatManager
float GetThreat(Unit const* who, bool includeOffline = false) const;
size_t GetThreatListSize() const { return _sortedThreatList.size(); }
// fastest of the three threat list getters - gets the threat list in "arbitrary" order
- Trinity::IteratorPair<threat_list_heap::const_iterator> GetUnsortedThreatList() const { return { _sortedThreatList.begin(), _sortedThreatList.end() }; }
+ // iterators will invalidate on adding/removing entries from the threat list; slightly less finicky than GetSorted.
+ Trinity::IteratorPair<ThreatListIterator> GetUnsortedThreatList() const { return { _myThreatListEntries.begin(), _myThreatListEntries.end() }; }
// slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
+ // this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this!
// note: current tank is NOT guaranteed to be the first entry in this list - check GetCurrentVictim separately if you want that!
Trinity::IteratorPair<threat_list_heap::ordered_iterator> GetSortedThreatList() const { return { _sortedThreatList.ordered_begin(), _sortedThreatList.ordered_end() }; }
- // slowest of the three threat list getters (by far), but lets you modify the threat references
+ // slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted
std::vector<ThreatReference*> GetModifiableThreatList() const;
// does any unit have a threat list entry with victim == this.owner?
@@ -217,6 +221,20 @@ class TC_GAME_API ThreatManager
ThreatManager(ThreatManager const&) = delete;
ThreatManager& operator=(ThreatManager const&) = delete;
+ class ThreatListIterator
+ {
+ private:
+ decltype(_myThreatListEntries)::const_iterator _it;
+
+ public:
+ ThreatReference const* operator*() const { return _it->second; }
+ ThreatReference const* operator->() const { return _it->second; }
+ ThreatListIterator& operator++() { ++_it; return *this; }
+ bool operator==(ThreatListIterator const& o) const { return _it == o._it; }
+ bool operator!=(ThreatListIterator const& o) const { return _it != o._it; }
+ ThreatListIterator(decltype(_it) it) : _it(it) {}
+ };
+
friend class ThreatReference;
friend struct CompareThreatLessThan;
friend class debug_commandscript;
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index e1ca92ce670..67490c05f7d 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -555,8 +555,8 @@ public:
if (!target)
return false;
- handler->PSendSysMessage("Loot recipient for creature %s (%s, DB GUID %s) is %s",
- target->GetName().c_str(), target->GetGUID().ToString().c_str(), std::to_string(target->GetSpawnId()).c_str(),
+ handler->PSendSysMessage("Loot recipient for creature %s (%s, SpawnID " UI64FMTD ") is %s",
+ target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetSpawnId(),
target->hasLootRecipient() ? (target->GetLootRecipient() ? target->GetLootRecipient()->GetName().c_str() : "offline") : "no loot recipient");
return true;
}
@@ -874,50 +874,59 @@ public:
for (auto const& pair : threatenedByMe)
{
Unit* unit = pair.second->GetOwner();
- handler->PSendSysMessage(" %u. %s (%s, SpawnID %u) - threat %f", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, pair.second->GetThreat());
+ handler->PSendSysMessage(" %u. %s (%s, SpawnID " UI64FMTD ") - threat %f", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, pair.second->GetThreat());
}
handler->SendSysMessage("End of threatened-by-me list.");
}
- if (!mgr.CanHaveThreatList())
- handler->PSendSysMessage("%s (%s) cannot have a threat list.", target->GetName().c_str(), target->GetGUID().ToString().c_str());
- else if (mgr.IsEngaged())
+ if (mgr.CanHaveThreatList())
{
- count = 0;
- handler->PSendSysMessage("Threat list of %s (%s, SpawnID %u)", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
- for (ThreatReference const* ref : mgr.GetSortedThreatList())
+ if (!mgr.IsThreatListEmpty(true))
{
- Unit* unit = ref->GetVictim();
- char const* onlineStr;
- switch (ref->GetOnlineState())
- {
- case ThreatReference::ONLINE_STATE_SUPPRESSED:
- onlineStr = " [SUPPRESSED]";
- break;
- case ThreatReference::ONLINE_STATE_OFFLINE:
- onlineStr = " [OFFLINE]";
- break;
- default:
- onlineStr = "";
- }
- char const* tauntStr;
- switch (ref->GetTauntState())
+ if (mgr.IsEngaged())
+ handler->PSendSysMessage("Threat list of %s (%s, SpawnID " UI64FMTD "):", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
+ else
+ handler->PSendSysMessage("%s (%s, SpawnID " UI64FMTD ") is not engaged, but still has a threat list? Well, here it is:", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
+
+ count = 0;
+ for (ThreatReference const* ref : mgr.GetSortedThreatList())
{
- case ThreatReference::TAUNT_STATE_TAUNT:
- tauntStr = " [TAUNT]";
- break;
- case ThreatReference::TAUNT_STATE_DETAUNT:
- tauntStr = " [DETAUNT]";
- break;
- default:
- tauntStr = "";
+ Unit* unit = ref->GetVictim();
+ char const* onlineStr;
+ switch (ref->GetOnlineState())
+ {
+ case ThreatReference::ONLINE_STATE_SUPPRESSED:
+ onlineStr = " [SUPPRESSED]";
+ break;
+ case ThreatReference::ONLINE_STATE_OFFLINE:
+ onlineStr = " [OFFLINE]";
+ break;
+ default:
+ onlineStr = "";
+ }
+ char const* tauntStr;
+ switch (ref->GetTauntState())
+ {
+ case ThreatReference::TAUNT_STATE_TAUNT:
+ tauntStr = " [TAUNT]";
+ break;
+ case ThreatReference::TAUNT_STATE_DETAUNT:
+ tauntStr = " [DETAUNT]";
+ break;
+ default:
+ tauntStr = "";
+ }
+ handler->PSendSysMessage(" %u. %s (%s) - threat %f%s%s", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), ref->GetThreat(), tauntStr, onlineStr);
}
- handler->PSendSysMessage(" %u. %s (%s) - threat %f%s%s", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), ref->GetThreat(), tauntStr, onlineStr);
+ handler->SendSysMessage("End of threat list.");
}
- handler->SendSysMessage("End of threat list.");
+ else if (!mgr.IsEngaged())
+ handler->PSendSysMessage("%s (%s, SpawnID " UI64FMTD ") is not currently engaged.", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
+ else
+ handler->PSendSysMessage("%s (%s, SpawnID " UI64FMTD ") seems to be engaged, but does not have a threat list??", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
}
else
- handler->PSendSysMessage("%s (%s, SpawnID %u) is not currently engaged.", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0);
+ handler->PSendSysMessage("%s (%s) cannot have a threat list.", target->GetName().c_str(), target->GetGUID().ToString().c_str());
return true;
}