Core/Unit: Fix crashes in Unit::CalcAbsorbResist and Unit::DealDamage caused by unsafe iteration over Unit::AuraEffectList - copy the list instead of using oryginal one.

--HG--
branch : trunk
This commit is contained in:
QAston
2010-12-25 16:12:31 +01:00
parent 99ff32be28
commit bce9b081ec
2 changed files with 66 additions and 98 deletions
+66 -85
View File
@@ -553,10 +553,15 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
// interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto ? spellProto->Id : 0);
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vCopyDamageCopy(pVictim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT));
// copy damage to casters of this aura
AuraEffectList const& vCopyDamage = pVictim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT);
for (AuraEffectList::const_iterator i = vCopyDamage.begin(); i != vCopyDamage.end(); ++i)
for (AuraEffectList::iterator i = vCopyDamageCopy.begin(); i != vCopyDamageCopy.end(); ++i)
{
// Check if aura was removed during iteration - we don't need to work on such auras
if (!((*i)->GetBase()->IsAppliedOnTarget(pVictim->GetGUID())))
continue;
// check damage school mask
if (((*i)->GetMiscValue() & damageSchoolMask) == 0)
continue;
@@ -1615,9 +1620,6 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
// Death Prevention Aura
SpellEntry const* preventDeathSpell = NULL;
int32 preventDeathAmount = 0;
// Need remove expired auras after
bool existExpired = false;
TriggeredSpellInfoVct triggeredSpells;
// Incanter's Absorption, for converting to spell power
int32 incanterAbsorption = 0;
@@ -1644,16 +1646,22 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
auraAbsorbMod = (*itr)->GetAmount();
}
AuraEffectList vSchoolAbsorb;
pVictim->FillOrderedAbsorbAuras(vSchoolAbsorb);
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSchoolAbsorbCopy(pVictim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB));
vSchoolAbsorbCopy.sort(Trinity::AbsorbAuraOrderPred());
// absorb without mana cost
for (AuraEffectList::const_iterator itr = vSchoolAbsorb.begin(); (itr != vSchoolAbsorb.end()) && (RemainingDamage > 0); ++itr)
for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (RemainingDamage > 0); ++itr)
{
if (!((*itr)->GetMiscValue() & schoolMask))
AuraEffect * absorbAurEff = (*itr);
// Check if aura was removed during iteration - we don't need to work on such auras
if (!(absorbAurEff->GetBase()->IsAppliedOnTarget(pVictim->GetGUID())))
continue;
if (!(absorbAurEff->GetMiscValue() & schoolMask))
continue;
SpellEntry const * spellProto = (*itr)->GetSpellProto();
SpellEntry const * spellProto = absorbAurEff->GetSpellProto();
// Frost Warding
// Chaos Bolt ignore the absorption but still proc Frost Warding mana return
@@ -1664,7 +1672,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
if (roll_chance_i(chance))
{
triggeredSpells.push_back(TriggeredSpellInfo(57776, pVictim, pVictim, RemainingDamage, (*itr)));
pVictim->CastCustomSpell(pVictim, 57776, &RemainingDamage, NULL, NULL, true, NULL, absorbAurEff);
RemainingDamage = RemainingDamage * auraAbsorbMod / 100;
continue;
}
@@ -1674,13 +1682,14 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
continue;
// Max Amount can be absorbed by this aura
int32 currentAbsorb = (*itr)->GetAmount();
// Found empty aura (impossible but..)
int32 currentAbsorb = absorbAurEff->GetAmount();
// Found empty aura
if (currentAbsorb <= 0)
{
existExpired = true;
absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
continue;
}
currentAbsorb = (100 - auraAbsorbMod) * currentAbsorb / 100;
// Handle custom absorb auras
@@ -1716,9 +1725,8 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
// Reflective Shield (Lady Malande boss)
if (spellProto->Id == 41475)
{
triggeredSpells.push_back(
TriggeredSpellInfo(33619, pVictim, this, std::min(RemainingDamage, currentAbsorb) / 2, *itr)
);
int32 bp = std::min(RemainingDamage, currentAbsorb) / 2;
pVictim->CastCustomSpell(this, 33619, &bp, NULL, NULL, true, NULL, *itr);
break;
}
if ((spellProto->Id == 39228) || // Argussian Compass
@@ -1748,10 +1756,9 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
if (RemainingDamage < currentAbsorb)
currentAbsorb = RemainingDamage;
(*itr)->SetAmount(0); // guarantee removal
existExpired = true; // maybe hacky but not crashy
RemainingDamage -= currentAbsorb;
absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL); // guarantee removal
continue;
}
// Moonkin Form passive
@@ -1770,8 +1777,8 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
if (spellProto->SpellIconID == 2109)
{
if (!preventDeathSpell && pVictim->ToPlayer())
if (!pVictim->ToPlayer()->HasSpellCooldown(31231) && roll_chance_i((*itr)->GetAmount()))
preventDeathSpell = (*itr)->GetSpellProto();
if (!pVictim->ToPlayer()->HasSpellCooldown(31231) && roll_chance_i(absorbAurEff->GetAmount()))
preventDeathSpell = absorbAurEff->GetSpellProto();
continue;
}
break;
@@ -1781,8 +1788,8 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
// Guardian Spirit
if (spellProto->SpellIconID == 2873)
{
preventDeathSpell = (*itr)->GetSpellProto();
preventDeathAmount = (*itr)->GetAmount();
preventDeathSpell = absorbAurEff->GetSpellProto();
preventDeathAmount = absorbAurEff->GetAmount();
continue;
}
// Power Word: Shield
@@ -1791,7 +1798,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
if (pVictim == this)
break;
Unit * caster = (*itr)->GetCaster();
Unit * caster = absorbAurEff->GetCaster();
if (!caster)
break;
@@ -1801,10 +1808,11 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
{
case 5065: // Rank 1
case 5064: // Rank 2
triggeredSpells.push_back(
TriggeredSpellInfo(33619, pVictim, this, CalculatePctN(std::min(RemainingDamage, currentAbsorb), aurEff->GetAmount()), *itr)
);
{
int32 bp = CalculatePctN(std::min(RemainingDamage, currentAbsorb), aurEff->GetAmount());
pVictim->CastCustomSpell(this, 33619, &bp, NULL, NULL, true, NULL, *itr);
break;
}
default:
sLog->outError("Unit::CalcAbsorbResist: unknown Reflective Shield spell %d", aurEff->GetId());
break;
@@ -1836,8 +1844,8 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
? 1.0f
: float(defenseSkillValue) / float(reqDefForMaxHeal);
int32 healAmount = int32(pVictim->CountPctFromMaxHealth(uint32((*itr)->GetAmount() * pctFromDefense)));
pVictim->CastCustomSpell(pVictim, 66235, &healAmount, NULL, NULL, true);
int32 healAmount = int32(pVictim->CountPctFromMaxHealth(uint32(absorbAurEff->GetAmount() * pctFromDefense)));
pVictim->CastCustomSpell(pVictim, 66235, &healAmount, NULL, NULL, true, NULL, *itr);
pVictim->ToPlayer()->AddSpellCooldown(66235, 0, time(NULL) + 120);
}
else if (remainingHealth < int32(allowedHealth))
@@ -1869,7 +1877,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
switch (spellProto->Id)
{
case 51271: // Unbreakable Armor
if (Unit * caster = (*itr)->GetCaster())
if (Unit * caster = absorbAurEff->GetCaster())
{
uint32 absorbed = uint32(currentAbsorb * caster->GetArmor() * 0.01f);
@@ -1890,7 +1898,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
uint32 rank = sSpellMgr->GetSpellRank(spellProto->Id);
SpellEntry const * talentProto = sSpellStore.LookupEntry(sSpellMgr->GetSpellWithRank(49189, rank));
int32 minHp = int32(pVictim->CountPctFromMaxHealth(SpellMgr::CalculateSpellEffectAmount(talentProto, EFFECT_0, (*itr)->GetCaster())));
int32 minHp = int32(pVictim->CountPctFromMaxHealth(SpellMgr::CalculateSpellEffectAmount(talentProto, EFFECT_0, absorbAurEff->GetCaster())));
// Damage that would take you below [effect0] health or taken while you are at [effect0]
if (remainingHp < minHp)
{
@@ -1905,14 +1913,15 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
// This, if I'm not mistaken, shows that we get back ~2% of the absorbed damage as runic power.
int32 absorbed = CalculatePctN(RemainingDamage, currentAbsorb);
RemainingDamage -= absorbed;
triggeredSpells.push_back(TriggeredSpellInfo(49088, pVictim, pVictim, absorbed * 2 / 10, *itr));
int32 bp = absorbed * 2 / 10;
pVictim->CastCustomSpell(pVictim, 49088, &bp, NULL, NULL, true, NULL, *itr);
continue;
}
case 50462: // Anti-Magic Shell (on single party/raid member)
AddPctN(RemainingDamage, -currentAbsorb);
continue;
case 50461: // Anti-Magic Zone
if (Unit * caster = (*itr)->GetCaster())
if (Unit * caster = absorbAurEff->GetCaster())
{
int32 absorbed = CalculatePctN(RemainingDamage, currentAbsorb);
int32 canabsorb = caster->GetHealth();
@@ -1944,45 +1953,16 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
incanterAbsorption += currentAbsorb;
// Reduce shield amount
(*itr)->SetAmount((*itr)->GetAmount() - currentAbsorb);
absorbAurEff->SetAmount(absorbAurEff->GetAmount() - currentAbsorb);
// Need remove it later
if ((*itr)->GetAmount() <= 0)
existExpired = true;
if (absorbAurEff->GetAmount() <= 0)
absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
}
for (TriggeredSpellInfoVct::const_iterator itr = triggeredSpells.begin(); itr != triggeredSpells.end(); ++itr)
if (itr->spell)
itr->source->CastCustomSpell(itr->spell, SPELLVALUE_BASE_POINT0, itr->amount, itr->target, true, NULL, itr->auraEff);
else if (itr->amount > 0)
{
uint32 damage = uint32(itr->amount);
itr->source->DealDamageMods(itr->target, damage, NULL);
itr->source->DealDamage(itr->target, damage, NULL, damagetype, schoolMask, 0, false);
}
// Remove all expired absorb auras
if (existExpired)
{
AuraEffectList const & vSchoolAbsorbOri = pVictim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB);
for (AuraEffectList::const_iterator itr = vSchoolAbsorbOri.begin(); itr != vSchoolAbsorbOri.end();)
{
AuraEffect * auraEff = (*itr);
++itr;
if (auraEff->GetAmount() <= 0)
{
uint32 removedAuras = pVictim->m_removedAurasCount;
auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
if (removedAuras + 1 < pVictim->m_removedAurasCount)
itr = vSchoolAbsorbOri.begin();
}
}
}
if (auraAbsorbMod < 100) // Do nothing if 100% absorb ignore
{
existExpired = false;
bool existExpired = false;
// absorb by mana cost
AuraEffectList const & vManaShield = pVictim->GetAuraEffectsByType(SPELL_AURA_MANA_SHIELD);
@@ -2040,15 +2020,18 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
}
}
// only split damage if not damaging yourself
if (auraAbsorbMod < 100) // Do nothing if 100% absorb ignore
if (pVictim != this)
// Do nothing if 100% absorb ignore
if (auraAbsorbMod < 100)
if (pVictim != this) // only split damage if not damaging yourself
{
AuraEffectList const & vSplitDamageFlat = pVictim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_FLAT);
for (AuraEffectList::const_iterator itr = vSplitDamageFlat.begin(), next; (itr != vSplitDamageFlat.end()) && (RemainingDamage >= 0); itr = next)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSplitDamageFlatCopy(pVictim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_FLAT));
for (AuraEffectList::iterator itr = vSplitDamageFlatCopy.begin(); (itr != vSplitDamageFlatCopy.end()) && (RemainingDamage >= 0); ++itr)
{
next = itr; ++next;
// Check if aura was removed during iteration - we don't need to work on such auras
if (!((*itr)->GetBase()->IsAppliedOnTarget(pVictim->GetGUID())))
continue;
// check damage school mask
if (!((*itr)->GetMiscValue() & schoolMask))
continue;
@@ -2077,11 +2060,14 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*itr)->GetSpellProto(), false);
}
AuraEffectList const & vSplitDamagePct = pVictim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT);
for (AuraEffectList::const_iterator itr = vSplitDamagePct.begin(), next; (itr != vSplitDamagePct.end()) && (RemainingDamage >= 0); itr = next)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSplitDamagePctCopy(pVictim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT));
for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(), next; (itr != vSplitDamagePctCopy.end()) && (RemainingDamage >= 0); ++itr)
{
next = itr; ++next;
// Check if aura was removed during iteration - we don't need to work on such auras
if (!((*itr)->GetBase()->IsAppliedOnTarget(pVictim->GetGUID())))
continue;
// check damage school mask
if (!((*itr)->GetMiscValue() & schoolMask))
continue;
@@ -2166,12 +2152,6 @@ void Unit::CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEff
}
}
void Unit::FillOrderedAbsorbAuras(AuraEffectList & out) const
{
out = GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB);
out.sort(Trinity::AbsorbAuraOrderPred());
}
void Unit::CalcHealAbsorb(Unit *pVictim, const SpellEntry *healSpell, uint32 &healAmount, uint32 &absorb)
{
if (!healAmount)
@@ -15302,6 +15282,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
{
if ((*itr)->GetSpellProto()->SpellIconID == 1654)
{
AuraEffect * aurEff = *itr;
// save value before aura remove
uint32 ressSpellId = pVictim->GetUInt32Value(PLAYER_SELF_RES_SPELL);
if (!ressSpellId)
@@ -15312,7 +15293,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL,ressSpellId);
// FORM_SPIRITOFREDEMPTION and related auras
pVictim->CastSpell(pVictim,27827,true,NULL,*itr);
pVictim->CastSpell(pVictim,27827,true,NULL,aurEff);
SpiritOfRedemption = true;
break;
}
-13
View File
@@ -865,18 +865,6 @@ struct SpellPeriodicAuraLogInfo
bool critical;
};
struct TriggeredSpellInfo
{
TriggeredSpellInfo(uint32 _spell, Unit *_source, Unit *_target, int32 _amount = 0, AuraEffect *_auraEff = NULL)
: spell(_spell), amount(_amount), source(_source), target(_target), auraEff(_auraEff) {}
uint32 spell;
int32 amount;
Unit *source, *target;
AuraEffect *auraEff;
};
typedef std::vector<TriggeredSpellInfo> TriggeredSpellInfoVct;
uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition);
#define MAX_DECLINED_NAME_CASES 5
@@ -1873,7 +1861,6 @@ class Unit : public WorldObject
static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellEntry const *spellInfo = NULL, uint8 effIndex = MAX_SPELL_EFFECTS);
uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType=MAX_ATTACK);
void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, SpellEntry const *spellInfo = NULL);
void FillOrderedAbsorbAuras(AuraEffectList & out) const;
void CalcHealAbsorb(Unit *pVictim, const SpellEntry *spellProto, uint32 &healAmount, uint32 &absorb);
void UpdateSpeed(UnitMoveType mtype, bool forced);