mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 09:17:18 +00:00
fix(Core/CreatureText): race conditions in CreatureTextMgr repeat group (#23153)
Co-authored-by: Shauren <shauren.trinity@gmail.com> Co-authored-by: Giacomo Pozzoni <giacomopoz@gmail.com>
This commit is contained in:
@@ -3713,6 +3713,33 @@ bool Creature::CanGeneratePickPocketLoot() const
|
||||
return (lootPickPocketRestoreTime == 0 || lootPickPocketRestoreTime < GameTime::GetGameTime().count());
|
||||
}
|
||||
|
||||
void Creature::SetTextRepeatId(uint8 textGroup, uint8 id)
|
||||
{
|
||||
CreatureTextRepeatIds& repeats = m_textRepeat[textGroup];
|
||||
if (std::find(repeats.begin(), repeats.end(), id) == repeats.end())
|
||||
repeats.push_back(id);
|
||||
else
|
||||
LOG_ERROR("sql.sql", "CreatureTextMgr: TextGroup {} for Creature({}) {}, id {} already added", uint32(textGroup), GetName(), GetGUID().ToString(), uint32(id));
|
||||
}
|
||||
|
||||
CreatureTextRepeatIds const& Creature::GetTextRepeatGroup(uint8 textGroup)
|
||||
{
|
||||
static CreatureTextRepeatIds const emptyIds;
|
||||
|
||||
CreatureTextRepeatGroup::const_iterator groupItr = m_textRepeat.find(textGroup);
|
||||
if (groupItr != m_textRepeat.end())
|
||||
return groupItr->second;
|
||||
|
||||
return emptyIds;
|
||||
}
|
||||
|
||||
void Creature::ClearTextRepeatGroup(uint8 textGroup)
|
||||
{
|
||||
CreatureTextRepeatGroup::iterator groupItr = m_textRepeat.find(textGroup);
|
||||
if (groupItr != m_textRepeat.end())
|
||||
groupItr->second.clear();
|
||||
}
|
||||
|
||||
void Creature::SetRespawnTime(uint32 respawn)
|
||||
{
|
||||
m_respawnTime = respawn ? GameTime::GetGameTime().count() + respawn : 0;
|
||||
|
||||
@@ -39,6 +39,10 @@ class CreatureGroup;
|
||||
|
||||
#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY
|
||||
|
||||
//used for handling non-repeatable random texts
|
||||
typedef std::vector<uint8> CreatureTextRepeatIds;
|
||||
typedef std::unordered_map<uint8, CreatureTextRepeatIds> CreatureTextRepeatGroup;
|
||||
|
||||
class Creature : public Unit, public GridObject<Creature>, public MovableMapObject, public UpdatableMapObject
|
||||
{
|
||||
public:
|
||||
@@ -388,6 +392,10 @@ public:
|
||||
void UpdateLeashExtensionTime();
|
||||
uint8 GetLeashTimer() const;
|
||||
|
||||
CreatureTextRepeatIds const& GetTextRepeatGroup(uint8 textGroup);
|
||||
void SetTextRepeatId(uint8 textGroup, uint8 id);
|
||||
void ClearTextRepeatGroup(uint8 textGroup);
|
||||
|
||||
bool IsFreeToMove();
|
||||
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
||||
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
|
||||
@@ -519,6 +527,8 @@ private:
|
||||
|
||||
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
|
||||
|
||||
CreatureTextRepeatGroup m_textRepeat;
|
||||
|
||||
bool _isMissingSwimmingFlagOutOfCombat;
|
||||
|
||||
uint32 m_assistanceTimer;
|
||||
|
||||
@@ -83,7 +83,7 @@ void CreatureTextMgr::LoadCreatureTexts()
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
mTextMap.clear(); // for reload case
|
||||
mTextRepeatMap.clear(); //reset all currently used temp texts
|
||||
//all currently used temp texts are NOT reset
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT);
|
||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
||||
@@ -218,7 +218,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
|
||||
}
|
||||
|
||||
CreatureTextGroup const& textGroupContainer = itr->second; //has all texts in the group
|
||||
CreatureTextRepeatIds repeatGroup = GetRepeatGroup(source, textGroup);//has all textIDs from the group that were already said
|
||||
CreatureTextRepeatIds repeatGroup = source->GetTextRepeatGroup(textGroup);//has all textIDs from the group that were already said
|
||||
CreatureTextGroup tempGroup;//will use this to talk after sorting repeatGroup
|
||||
|
||||
for (CreatureTextGroup::const_iterator giter = textGroupContainer.begin(); giter != textGroupContainer.end(); ++giter)
|
||||
@@ -227,52 +227,14 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
|
||||
|
||||
if (tempGroup.empty())
|
||||
{
|
||||
CreatureTextRepeatMap::iterator mapItr = mTextRepeatMap.find(source->GetGUID());
|
||||
if (mapItr != mTextRepeatMap.end())
|
||||
{
|
||||
CreatureTextRepeatGroup::iterator groupItr = mapItr->second.find(textGroup);
|
||||
groupItr->second.clear();
|
||||
}
|
||||
|
||||
source->ClearTextRepeatGroup(textGroup);
|
||||
tempGroup = textGroupContainer;
|
||||
}
|
||||
|
||||
uint8 count = 0;
|
||||
float lastChance = -1;
|
||||
bool isEqualChanced = true;
|
||||
|
||||
float totalChance = 0;
|
||||
|
||||
for (CreatureTextGroup::const_iterator iter = tempGroup.begin(); iter != tempGroup.end(); ++iter)
|
||||
auto iter = Acore::Containers::SelectRandomWeightedContainerElement(tempGroup, [](CreatureTextEntry const& t) -> double
|
||||
{
|
||||
if (lastChance >= 0 && lastChance != iter->probability)
|
||||
isEqualChanced = false;
|
||||
|
||||
lastChance = iter->probability;
|
||||
totalChance += iter->probability;
|
||||
++count;
|
||||
}
|
||||
|
||||
int32 offset = -1;
|
||||
if (!isEqualChanced)
|
||||
{
|
||||
for (CreatureTextGroup::const_iterator iter = tempGroup.begin(); iter != tempGroup.end(); ++iter)
|
||||
{
|
||||
uint32 chance = uint32(iter->probability);
|
||||
uint32 r = urand(0, 100);
|
||||
++offset;
|
||||
if (r <= chance)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 pos = 0;
|
||||
if (isEqualChanced || offset < 0)
|
||||
pos = urand(0, count - 1);
|
||||
else if (offset >= 0)
|
||||
pos = offset;
|
||||
|
||||
CreatureTextGroup::const_iterator iter = tempGroup.begin() + pos;
|
||||
return t.probability;
|
||||
});
|
||||
|
||||
ChatMsg finalType = (msgType == CHAT_MSG_ADDON) ? iter->type : msgType;
|
||||
Language finalLang = (language == LANG_ADDON) ? iter->lang : language;
|
||||
@@ -301,9 +263,8 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
|
||||
CreatureTextBuilder builder(finalSource, finalSource->getGender(), finalType, iter->group, iter->id, finalLang, target);
|
||||
SendChatPacket(finalSource, builder, finalType, target, range, teamId, gmOnly);
|
||||
}
|
||||
if (isEqualChanced || (!isEqualChanced && totalChance == 100.0f))
|
||||
SetRepeatId(source, textGroup, iter->id);
|
||||
|
||||
source->SetTextRepeatId(textGroup, iter->id);
|
||||
return iter->duration;
|
||||
}
|
||||
|
||||
@@ -410,34 +371,6 @@ void CreatureTextMgr::SendEmote(Unit* source, uint32 emote)
|
||||
source->HandleEmoteCommand(emote);
|
||||
}
|
||||
|
||||
void CreatureTextMgr::SetRepeatId(Creature* source, uint8 textGroup, uint8 id)
|
||||
{
|
||||
if (!source)
|
||||
return;
|
||||
|
||||
CreatureTextRepeatIds& repeats = mTextRepeatMap[source->GetGUID()][textGroup];
|
||||
if (std::find(repeats.begin(), repeats.end(), id) == repeats.end())
|
||||
repeats.push_back(id);
|
||||
else
|
||||
LOG_ERROR("sql.sql", "CreatureTextMgr: TextGroup {} for Creature {} ({}), id {} already added",
|
||||
uint32(textGroup), source->GetName(), source->GetGUID().ToString(), uint32(id));
|
||||
}
|
||||
|
||||
CreatureTextRepeatIds CreatureTextMgr::GetRepeatGroup(Creature* source, uint8 textGroup)
|
||||
{
|
||||
ASSERT(source);//should never happen
|
||||
CreatureTextRepeatIds ids;
|
||||
|
||||
CreatureTextRepeatMap::const_iterator mapItr = mTextRepeatMap.find(source->GetGUID());
|
||||
if (mapItr != mTextRepeatMap.end())
|
||||
{
|
||||
CreatureTextRepeatGroup::const_iterator groupItr = (*mapItr).second.find(textGroup);
|
||||
if (groupItr != mapItr->second.end())
|
||||
ids = groupItr->second;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
bool CreatureTextMgr::TextExist(uint32 sourceEntry, uint8 textGroup)
|
||||
{
|
||||
if (!sourceEntry)
|
||||
|
||||
@@ -77,11 +77,6 @@ typedef std::unordered_map<uint32, CreatureTextHolder> CreatureTextMap; // a
|
||||
|
||||
typedef std::map<CreatureTextId, CreatureTextLocale> LocaleCreatureTextMap;
|
||||
|
||||
//used for handling non-repeatable random texts
|
||||
typedef std::vector<uint8> CreatureTextRepeatIds;
|
||||
typedef std::unordered_map<uint8, CreatureTextRepeatIds> CreatureTextRepeatGroup;
|
||||
typedef std::unordered_map<ObjectGuid, CreatureTextRepeatGroup> CreatureTextRepeatMap;//guid based
|
||||
|
||||
class CreatureTextMgr
|
||||
{
|
||||
CreatureTextMgr() { }
|
||||
@@ -105,14 +100,10 @@ public:
|
||||
template<class Builder> void SendChatPacket(WorldObject* source, Builder const& builder, ChatMsg msgType, WorldObject const* target = nullptr, CreatureTextRange range = TEXT_RANGE_NORMAL, TeamId teamId = TEAM_NEUTRAL, bool gmOnly = false) const;
|
||||
|
||||
private:
|
||||
CreatureTextRepeatIds GetRepeatGroup(Creature* source, uint8 textGroup);
|
||||
void SetRepeatId(Creature* source, uint8 textGroup, uint8 id);
|
||||
|
||||
void SendNonChatPacket(WorldObject* source, WorldPacket const* data, ChatMsg msgType, WorldObject const* target, CreatureTextRange range, TeamId teamId, bool gmOnly) const;
|
||||
float GetRangeForChatType(ChatMsg msgType) const;
|
||||
|
||||
CreatureTextMap mTextMap;
|
||||
CreatureTextRepeatMap mTextRepeatMap;
|
||||
LocaleCreatureTextMap mLocaleTextMap;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user