From 4ead80f388cbd7cc0264f69b9d122d8f7dc1b5f3 Mon Sep 17 00:00:00 2001 From: Shauren Date: Fri, 14 Mar 2025 16:57:04 +0100 Subject: [PATCH] Core/Auras: Minor optimization for Unit object creation with msvc (not allocating a list end sentinel node for all of the 500 lists stored in Unit for each aura type) --- src/common/Containers/ListUtils.h | 53 +++++++++++++++++ src/server/game/AI/PlayerAI/PlayerAI.cpp | 2 +- src/server/game/Combat/ThreatManager.cpp | 13 ++--- src/server/game/Entities/Unit/Unit.cpp | 57 ++++++++++++------- src/server/game/Entities/Unit/Unit.h | 7 ++- .../game/Spells/Auras/SpellAuraEffects.cpp | 40 ++++++------- src/server/game/Spells/Auras/SpellAuras.cpp | 3 +- src/server/scripts/Commands/cs_list.cpp | 2 +- src/server/scripts/Spells/spell_generic.cpp | 3 +- src/server/scripts/Spells/spell_mage.cpp | 4 +- 10 files changed, 127 insertions(+), 57 deletions(-) create mode 100644 src/common/Containers/ListUtils.h diff --git a/src/common/Containers/ListUtils.h b/src/common/Containers/ListUtils.h new file mode 100644 index 0000000000..f5de0d42ec --- /dev/null +++ b/src/common/Containers/ListUtils.h @@ -0,0 +1,53 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITYCORE_LIST_UTILS_H +#define TRINITYCORE_LIST_UTILS_H + +#include +#include + +namespace Trinity::Containers::Lists +{ +template> +inline typename std::list::iterator RemoveUnique(std::list& list, T const& value) +{ + auto itr = std::find(list.begin(), list.end(), value); + if (itr != list.end()) + return list.erase(itr); + + return list.end(); +} + +template> +inline typename std::forward_list::iterator RemoveUnique(std::forward_list& list, T const& value) +{ + auto itr = list.before_begin(); + auto toErase = std::next(itr); + while (toErase != list.end()) + { + if (*toErase == value) + return list.erase_after(itr); + + itr = toErase++; + } + + return list.end(); +} +} + +#endif // TRINITYCORE_LIST_UTILS_H diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp index 454537e7b5..54453b495c 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.cpp +++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp @@ -687,7 +687,7 @@ void PlayerAI::DoAutoAttackIfReady() void PlayerAI::CancelAllShapeshifts() { - std::list const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + Unit::AuraEffectList const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); std::set removableShapeshifts; for (AuraEffect* auraEff : shapeshiftAuras) { diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 5f83c58f8e..265dc8cd41 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -503,19 +503,19 @@ void ThreatManager::MatchUnitThreatToHighestThreat(Unit* target) void ThreatManager::TauntUpdate() { - std::list const& tauntEffects = _owner->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT); + Unit::AuraEffectList const& tauntEffects = _owner->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT); - uint32 state = ThreatReference::TAUNT_STATE_TAUNT; - std::unordered_map tauntStates; + uint32 tauntPriority = 0; // lowest is highest + std::unordered_map tauntStates; // Only the last taunt effect applied by something still on our threat list is considered - for (auto it = tauntEffects.begin(), end = tauntEffects.end(); it != end; ++it) - tauntStates[(*it)->GetCasterGUID()] = ThreatReference::TauntState(state++); + for (AuraEffect const* tauntEffect : tauntEffects) + tauntStates[tauntEffect->GetCasterGUID()] = ++tauntPriority; for (auto const& pair : _myThreatListEntries) { auto it = tauntStates.find(pair.first); if (it != tauntStates.end()) - pair.second->UpdateTauntState(it->second); + pair.second->UpdateTauntState(ThreatReference::TauntState(ThreatReference::TAUNT_STATE_TAUNT + tauntStates.size() - it->second)); else pair.second->UpdateTauntState(); } @@ -523,7 +523,6 @@ void ThreatManager::TauntUpdate() // taunt aura update also re-evaluates all suppressed states (retail behavior) EvaluateSuppressed(true); } - void ThreatManager::ResetAllThreat() { for (auto const& pair : _myThreatListEntries) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5bf1feb93d..a11ba0931f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -43,6 +43,7 @@ #include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Item.h" +#include "ListUtils.h" #include "Log.h" #include "LootMgr.h" #include "LootPackets.h" @@ -3098,20 +3099,20 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) * but may be created as a result of aura links. */ - // register single target aura - caster->GetSingleCastAuras().push_back(aura); - - std::queue aurasSharingLimit; + std::vector aurasSharingLimit; // remove other single target auras for (Aura* scAura : caster->GetSingleCastAuras()) - if (scAura != aura && scAura->IsSingleTargetWith(aura)) - aurasSharingLimit.push(scAura); + if (scAura->IsSingleTargetWith(aura)) + aurasSharingLimit.push_back(scAura); + + // register single target aura + caster->GetSingleCastAuras().push_front(aura); uint32 maxOtherAuras = aura->GetSpellInfo()->MaxAffectedTargets - 1; while (aurasSharingLimit.size() > maxOtherAuras) { - aurasSharingLimit.front()->Remove(); - aurasSharingLimit.pop(); + aurasSharingLimit.back()->Remove(); + aurasSharingLimit.pop_back(); } } } @@ -3149,7 +3150,7 @@ AuraApplication* Unit::_CreateAuraApplication(Aura* aura, uint8 effMask) if (aurSpellInfo->HasAnyAuraInterruptFlag()) { - m_interruptableAuras.push_back(aurApp); + m_interruptableAuras.push_front(aurApp); AddInterruptMask(aurSpellInfo->AuraInterruptFlags, aurSpellInfo->AuraInterruptFlags2); } @@ -3243,7 +3244,7 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveFlags removeM if (aura->GetSpellInfo()->HasAnyAuraInterruptFlag()) { - m_interruptableAuras.remove(aurApp); + Trinity::Containers::Lists::RemoveUnique(m_interruptableAuras, aurApp); UpdateInterruptMask(); } @@ -3349,7 +3350,7 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply) { if (apply) { - m_modAuras[aurEff->GetAuraType()].push_back(aurEff); + m_modAuras[aurEff->GetAuraType()].push_front(aurEff); if (Player* player = ToPlayer()) { player->StartAchievementCriteria(AchievementCriteriaStartEvent::GainAuraEffect, aurEff->GetAuraType()); @@ -3357,7 +3358,7 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply) } } else - m_modAuras[aurEff->GetAuraType()].remove(aurEff); + Trinity::Containers::Lists::RemoveUnique(m_modAuras[aurEff->GetAuraType()], aurEff); } // All aura base removes should go through this function! @@ -3371,7 +3372,7 @@ void Unit::RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveFlags removeMode) ++m_auraUpdateIterator; m_ownedAuras.erase(i); - m_removedAuras.push_back(aura); + m_removedAuras.push_front(aura); // Unregister single target aura if (aura->IsSingleTarget()) @@ -3704,7 +3705,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, W newAura->UnregisterSingleTarget(); // bring back single target aura status to the old aura aura->SetIsSingleTarget(true); - caster->GetSingleCastAuras().push_back(aura); + caster->GetSingleCastAuras().push_front(aura); } // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? stolenCharges : aura->GetCharges(), stolenCharges, recalculateMask, aura->GetCritChance(), aura->CanApplyResilience(), &damage[0]); @@ -10207,7 +10208,7 @@ void Unit::TriggerAurasProcOnEvent(Unit* actionTarget, uint32 typeMaskActor, uin for (auto itr = modOwner->GetAppliedAuras().begin(); itr != modOwner->GetAppliedAuras().end(); ++itr) { if (spell->m_appliedMods.count(itr->second->GetBase()) != 0) - modAuras.push_back(itr->second); + modAuras.push_front(itr->second); } modOwner->GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, &modAuras, myProcEventInfo); } @@ -10408,7 +10409,7 @@ void Unit::RestoreDisplayId() if (!transforms.empty()) { // iterate over already applied transform auras - from newest to oldest - for (auto i = transforms.rbegin(); i != transforms.rend(); ++i) + for (auto i = transforms.begin(); i != transforms.end(); ++i) { if (AuraApplication const* aurApp = (*i)->GetBase()->GetApplicationOfTarget(GetGUID())) { @@ -11795,6 +11796,12 @@ void Unit::RemoveCharmedBy(Unit* charmer) void Unit::RestoreFaction() { + if (HasAuraType(SPELL_AURA_MOD_FACTION)) + { + SetFaction(GetAuraEffectsByType(SPELL_AURA_MOD_FACTION).front()->GetMiscValue()); + return; + } + if (GetTypeId() == TYPEID_PLAYER) ToPlayer()->SetFactionForRace(getRace()); else @@ -14302,10 +14309,8 @@ float Unit::GetCollisionHeight() const SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo, TriggerCastFlags& triggerFlag) const { - Unit::AuraEffectList swaps = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); + Unit::AuraEffectList const& swaps = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); Unit::AuraEffectList const& swaps2 = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_TRIGGERED); - if (!swaps2.empty()) - swaps.insert(swaps.end(), swaps2.begin(), swaps2.end()); for (AuraEffect const* auraEffect : swaps) { @@ -14321,6 +14326,20 @@ SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo, TriggerCastF } } + for (AuraEffect const* auraEffect : swaps2) + { + if (uint32(auraEffect->GetMiscValue()) == spellInfo->Id || auraEffect->IsAffectingSpell(spellInfo)) + { + if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount())) + { + if (auraEffect->GetSpellInfo()->HasAttribute(SPELL_ATTR8_IGNORE_SPELLCAST_OVERRIDE_COST)) + triggerFlag |= TRIGGERED_IGNORE_POWER_AND_REAGENT_COST; + + return newInfo; + } + } + } + return spellInfo; } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 969895a31a..68b6878468 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -27,6 +27,7 @@ #include "UnitDefines.h" #include "Util.h" #include +#include #include #include #include @@ -783,9 +784,9 @@ class TC_GAME_API Unit : public WorldObject typedef std::multimap AuraStateAurasMap; typedef std::pair AuraStateAurasMapBounds; - typedef std::list AuraEffectList; - typedef std::list AuraList; - typedef std::list AuraApplicationList; + typedef std::forward_list AuraEffectList; + typedef std::forward_list AuraList; + typedef std::forward_list AuraApplicationList; typedef std::vector> AuraApplicationProcContainer; typedef std::vector FormationFollowerGUIDContainer; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index fd6bc78b57..6b3184a399 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2413,35 +2413,36 @@ void AuraEffect::HandleAuraModDisarm(AuraApplication const* aurApp, uint8 mode, Unit* target = aurApp->GetTarget(); - //Prevent handling aura twice AuraType type = GetAuraType(); - if (apply ? target->GetAuraEffectsByType(type).size() > 1 : target->HasAuraType(type)) - return; - // Prevent handling aura twice - if (apply ? target->GetAuraEffectsByType(type).size() > 1 : target->HasAuraType(type)) - return; + bool(*flagChangeFunc)(Unit* u) = nullptr; - uint32 field, flag, slot; + uint32 slot; WeaponAttackType attType; switch (type) { case SPELL_AURA_MOD_DISARM: - field = UNIT_FIELD_FLAGS; - flag = UNIT_FLAG_DISARMED; + if (apply) + flagChangeFunc = [](Unit* u) { if (u->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED)) { return false; } u->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); return true; }; + else + flagChangeFunc = [](Unit* u) { if (u->HasAuraType(SPELL_AURA_MOD_DISARM)) { return false; } u->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); return true; }; slot = EQUIPMENT_SLOT_MAINHAND; attType = BASE_ATTACK; break; case SPELL_AURA_MOD_DISARM_OFFHAND: - field = UNIT_FIELD_FLAGS_2; - flag = UNIT_FLAG2_DISARM_OFFHAND; + if (apply) + flagChangeFunc = [](Unit* u) { if (u->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND)) { return false; } u->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND); return true; }; + else + flagChangeFunc = [](Unit* u) { if (u->HasAuraType(SPELL_AURA_MOD_DISARM_OFFHAND)) { return false; } u->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND); return true; }; slot = EQUIPMENT_SLOT_OFFHAND; attType = OFF_ATTACK; break; case SPELL_AURA_MOD_DISARM_RANGED: - field = UNIT_FIELD_FLAGS_2; - flag = UNIT_FLAG2_DISARM_RANGED; - slot = EQUIPMENT_SLOT_RANGED; + if (apply) + flagChangeFunc = [](Unit* u) { if (u->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED)) { return false; } u->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED); return true; }; + else + flagChangeFunc = [](Unit* u) { if (u->HasAuraType(SPELL_AURA_MOD_DISARM_RANGED)) { return false; } u->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED); return true; }; + slot = EQUIPMENT_SLOT_MAINHAND; attType = RANGED_ATTACK; break; default: @@ -2449,10 +2450,9 @@ void AuraEffect::HandleAuraModDisarm(AuraApplication const* aurApp, uint8 mode, } // set/remove flag before weapon bonuses so it's properly reflected in CanUseAttackType - if (apply) - target->SetFlag(field, flag); - else - target->RemoveFlag(field, flag); + if (flagChangeFunc) + if (!flagChangeFunc(target)) //Prevent handling aura twice + return; // Handle damage modification, shapeshifted druids are not affected if (target->GetTypeId() == TYPEID_PLAYER && !target->IsInFeralForm()) @@ -5029,16 +5029,12 @@ void AuraEffect::HandleAuraModFaction(AuraApplication const* aurApp, uint8 mode, if (apply) { target->SetFaction(GetMiscValue()); - target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION); - if (target->IsPlayer()) target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); } else { target->RestoreFaction(); - target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION); - if (target->IsPlayer()) target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index e29d74e969..4fe3d81382 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -22,6 +22,7 @@ #include "EnumFlag.h" #include "GridNotifiersImpl.h" #include "Item.h" +#include "ListUtils.h" #include "Log.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -1169,7 +1170,7 @@ void Aura::UnregisterSingleTarget() ASSERT(m_isSingleTarget); Unit* caster = GetCaster(); ASSERT(caster); - caster->GetSingleCastAuras().remove(this); + Trinity::Containers::Lists::RemoveUnique(caster->GetSingleCastAuras(), this); SetIsSingleTarget(false); } diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 6fc49f2ce1..1d2aab7e28 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -508,7 +508,7 @@ public: if (auraList.empty()) continue; - handler->PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, auraList.size(), i); + handler->PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, std::to_string(std::distance(auraList.begin(), auraList.end())).c_str(), i); for (Unit::AuraEffectList::const_iterator itr = auraList.begin(); itr != auraList.end(); ++itr) handler->PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(), (*itr)->GetAmount()); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 162d110eba..b9154fb186 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -2951,7 +2951,8 @@ class spell_gen_two_forms : public SpellScript } // Player cannot transform to human form if he is forced to be worgen for some reason (Darkflight) - if (GetCaster()->GetAuraEffectsByType(SPELL_AURA_WORGEN_ALTERED_FORM).size() > 1) + Unit::AuraEffectList const& alteredFormAuras = GetCaster()->GetAuraEffectsByType(SPELL_AURA_WORGEN_ALTERED_FORM); + if (std::distance(alteredFormAuras.begin(), alteredFormAuras.end()) > 1) { SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_CANT_TRANSFORM); return SPELL_FAILED_CUSTOM_ERROR; diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index c343197b60..4fdff2678d 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -1623,7 +1623,7 @@ private: if (!target) return false; - std::list dotAuraEffects = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); + Unit::AuraEffectList const& dotAuraEffects = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); if (dotAuraEffects.empty()) return false; @@ -1665,7 +1665,7 @@ class spell_mage_impact_triggered : public SpellScript if (!target || !caster || target != launchTarget) return; - std::list dotAuraEffects = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); + Unit::AuraEffectList const& dotAuraEffects = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); if (dotAuraEffects.empty()) return;