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());
|
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)
|
void Creature::SetRespawnTime(uint32 respawn)
|
||||||
{
|
{
|
||||||
m_respawnTime = respawn ? GameTime::GetGameTime().count() + respawn : 0;
|
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
|
#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
|
class Creature : public Unit, public GridObject<Creature>, public MovableMapObject, public UpdatableMapObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -388,6 +392,10 @@ public:
|
|||||||
void UpdateLeashExtensionTime();
|
void UpdateLeashExtensionTime();
|
||||||
uint8 GetLeashTimer() const;
|
uint8 GetLeashTimer() const;
|
||||||
|
|
||||||
|
CreatureTextRepeatIds const& GetTextRepeatGroup(uint8 textGroup);
|
||||||
|
void SetTextRepeatId(uint8 textGroup, uint8 id);
|
||||||
|
void ClearTextRepeatGroup(uint8 textGroup);
|
||||||
|
|
||||||
bool IsFreeToMove();
|
bool IsFreeToMove();
|
||||||
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
||||||
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
|
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
|
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
|
||||||
|
|
||||||
|
CreatureTextRepeatGroup m_textRepeat;
|
||||||
|
|
||||||
bool _isMissingSwimmingFlagOutOfCombat;
|
bool _isMissingSwimmingFlagOutOfCombat;
|
||||||
|
|
||||||
uint32 m_assistanceTimer;
|
uint32 m_assistanceTimer;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ void CreatureTextMgr::LoadCreatureTexts()
|
|||||||
uint32 oldMSTime = getMSTime();
|
uint32 oldMSTime = getMSTime();
|
||||||
|
|
||||||
mTextMap.clear(); // for reload case
|
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);
|
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT);
|
||||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
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
|
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
|
CreatureTextGroup tempGroup;//will use this to talk after sorting repeatGroup
|
||||||
|
|
||||||
for (CreatureTextGroup::const_iterator giter = textGroupContainer.begin(); giter != textGroupContainer.end(); ++giter)
|
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())
|
if (tempGroup.empty())
|
||||||
{
|
{
|
||||||
CreatureTextRepeatMap::iterator mapItr = mTextRepeatMap.find(source->GetGUID());
|
source->ClearTextRepeatGroup(textGroup);
|
||||||
if (mapItr != mTextRepeatMap.end())
|
|
||||||
{
|
|
||||||
CreatureTextRepeatGroup::iterator groupItr = mapItr->second.find(textGroup);
|
|
||||||
groupItr->second.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
tempGroup = textGroupContainer;
|
tempGroup = textGroupContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 count = 0;
|
auto iter = Acore::Containers::SelectRandomWeightedContainerElement(tempGroup, [](CreatureTextEntry const& t) -> double
|
||||||
float lastChance = -1;
|
|
||||||
bool isEqualChanced = true;
|
|
||||||
|
|
||||||
float totalChance = 0;
|
|
||||||
|
|
||||||
for (CreatureTextGroup::const_iterator iter = tempGroup.begin(); iter != tempGroup.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
if (lastChance >= 0 && lastChance != iter->probability)
|
return t.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;
|
|
||||||
|
|
||||||
ChatMsg finalType = (msgType == CHAT_MSG_ADDON) ? iter->type : msgType;
|
ChatMsg finalType = (msgType == CHAT_MSG_ADDON) ? iter->type : msgType;
|
||||||
Language finalLang = (language == LANG_ADDON) ? iter->lang : language;
|
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);
|
CreatureTextBuilder builder(finalSource, finalSource->getGender(), finalType, iter->group, iter->id, finalLang, target);
|
||||||
SendChatPacket(finalSource, builder, finalType, target, range, teamId, gmOnly);
|
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;
|
return iter->duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,34 +371,6 @@ void CreatureTextMgr::SendEmote(Unit* source, uint32 emote)
|
|||||||
source->HandleEmoteCommand(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)
|
bool CreatureTextMgr::TextExist(uint32 sourceEntry, uint8 textGroup)
|
||||||
{
|
{
|
||||||
if (!sourceEntry)
|
if (!sourceEntry)
|
||||||
|
|||||||
@@ -77,11 +77,6 @@ typedef std::unordered_map<uint32, CreatureTextHolder> CreatureTextMap; // a
|
|||||||
|
|
||||||
typedef std::map<CreatureTextId, CreatureTextLocale> LocaleCreatureTextMap;
|
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
|
class CreatureTextMgr
|
||||||
{
|
{
|
||||||
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;
|
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:
|
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;
|
void SendNonChatPacket(WorldObject* source, WorldPacket const* data, ChatMsg msgType, WorldObject const* target, CreatureTextRange range, TeamId teamId, bool gmOnly) const;
|
||||||
float GetRangeForChatType(ChatMsg msgType) const;
|
float GetRangeForChatType(ChatMsg msgType) const;
|
||||||
|
|
||||||
CreatureTextMap mTextMap;
|
CreatureTextMap mTextMap;
|
||||||
CreatureTextRepeatMap mTextRepeatMap;
|
|
||||||
LocaleCreatureTextMap mLocaleTextMap;
|
LocaleCreatureTextMap mLocaleTextMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user