mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-07-28 18:47:40 +00:00
fix(Scripts/Spells): defer aura mutations out of proc-check handlers (#26644)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -823,12 +823,14 @@ public:
|
||||
#define AuraEffectSplitFn(F, I) EffectSplitFunction(&F, I)
|
||||
|
||||
// executed when aura checks if it can proc
|
||||
// do not use this hook for triggering spellcasts/removing auras etc - runs mid proc-iteration and may be unsafe
|
||||
// example: DoCheckProc += AuraCheckProcFn(class::function);
|
||||
// where function is: bool function (ProcEventInfo& eventInfo);
|
||||
HookList<CheckProcHandler> DoCheckProc;
|
||||
#define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
|
||||
|
||||
// executed when aura effect checks if it can proc the aura
|
||||
// do not use this hook for triggering spellcasts/removing auras etc - runs mid proc-iteration and may be unsafe
|
||||
// example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
|
||||
// where function is: bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
||||
HookList<CheckEffectProcHandler> DoCheckEffectProc;
|
||||
|
||||
@@ -127,9 +127,19 @@ class spell_botanica_shift_form_aura : public AuraScript
|
||||
{
|
||||
_swapTime = GameTime::GetGameTime().count() + 6;
|
||||
_lastSchool = spellInfo->GetSchoolMask();
|
||||
GetUnitOwner()->RemoveAurasDueToSpell(_lastForm);
|
||||
|
||||
// Runs from Aura::GetProcEffectMask while the proc engine iterates the owner's
|
||||
// applied-aura map. Swapping forms mutates that map (RemoveAura + CastSpell),
|
||||
// which invalidates the live iterator now that aura containers are flat_multimaps.
|
||||
// Defer the swap to the owner's event queue so it runs outside the proc walk.
|
||||
Unit* owner = GetUnitOwner();
|
||||
uint32 const oldForm = _lastForm;
|
||||
_lastForm = form;
|
||||
GetUnitOwner()->CastSpell(GetUnitOwner(), _lastForm, true);
|
||||
owner->m_Events.AddEventAtOffset([owner, oldForm, form]()
|
||||
{
|
||||
owner->RemoveAurasDueToSpell(oldForm);
|
||||
owner->CastSpell(owner, form, true);
|
||||
}, 1ms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,16 +123,21 @@ class spell_mage_burning_determination : public AuraScript
|
||||
if (!(eventInfo.GetSpellInfo()->GetAllEffectsMechanicMask() & ((1ULL << MECHANIC_INTERRUPT) | (1ULL << MECHANIC_SILENCE))))
|
||||
return false;
|
||||
|
||||
Unit* target = eventInfo.GetActionTarget();
|
||||
|
||||
// This hook runs while the proc engine iterates the target's aura map; removing an
|
||||
// aura inline would invalidate the live iterator (aura containers are flat_multimaps).
|
||||
// Defer removals to the target's event queue so they run outside the proc walk.
|
||||
// Xinef: immuned effect should just eat charge
|
||||
if (eventInfo.GetHitMask() & PROC_EX_IMMUNE)
|
||||
{
|
||||
eventInfo.GetActionTarget()->RemoveAurasDueToSpell(54748);
|
||||
target->m_Events.AddEventAtOffset([target]() { target->RemoveAurasDueToSpell(54748); }, 1ms);
|
||||
return false;
|
||||
}
|
||||
if (Aura* aura = eventInfo.GetActionTarget()->GetAura(54748))
|
||||
if (Aura* aura = target->GetAura(54748))
|
||||
{
|
||||
if (aura->GetDuration() < aura->GetMaxDuration())
|
||||
eventInfo.GetActionTarget()->RemoveAurasDueToSpell(54748);
|
||||
target->m_Events.AddEventAtOffset([target]() { target->RemoveAurasDueToSpell(54748); }, 1ms);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1050,14 @@ class spell_mage_combustion : public AuraScript
|
||||
// Do not take charges, add a stack of crit buff
|
||||
if (!(eventInfo.GetHitMask() & PROC_HIT_CRITICAL))
|
||||
{
|
||||
eventInfo.GetActor()->CastSpell(static_cast<Unit*>(nullptr), SPELL_MAGE_COMBUSTION_PROC, true);
|
||||
// Applying the stack mutates the actor's aura map while the proc engine iterates
|
||||
// it (this hook runs from Aura::GetProcEffectMask); defer it so the insert can't
|
||||
// invalidate the live iterator (aura containers are flat_multimaps).
|
||||
Unit* actor = eventInfo.GetActor();
|
||||
actor->m_Events.AddEventAtOffset([actor]()
|
||||
{
|
||||
actor->CastSpell(static_cast<Unit*>(nullptr), SPELL_MAGE_COMBUSTION_PROC, true);
|
||||
}, 1ms);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user