mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-08 15:58:04 +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)
|
#define AuraEffectSplitFn(F, I) EffectSplitFunction(&F, I)
|
||||||
|
|
||||||
// executed when aura checks if it can proc
|
// 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);
|
// example: DoCheckProc += AuraCheckProcFn(class::function);
|
||||||
// where function is: bool function (ProcEventInfo& eventInfo);
|
// where function is: bool function (ProcEventInfo& eventInfo);
|
||||||
HookList<CheckProcHandler> DoCheckProc;
|
HookList<CheckProcHandler> DoCheckProc;
|
||||||
#define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
|
#define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
|
||||||
|
|
||||||
// executed when aura effect checks if it can proc the aura
|
// 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);
|
// example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
|
||||||
// where function is: bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
// where function is: bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
||||||
HookList<CheckEffectProcHandler> DoCheckEffectProc;
|
HookList<CheckEffectProcHandler> DoCheckEffectProc;
|
||||||
|
|||||||
@@ -127,9 +127,19 @@ class spell_botanica_shift_form_aura : public AuraScript
|
|||||||
{
|
{
|
||||||
_swapTime = GameTime::GetGameTime().count() + 6;
|
_swapTime = GameTime::GetGameTime().count() + 6;
|
||||||
_lastSchool = spellInfo->GetSchoolMask();
|
_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;
|
_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))))
|
if (!(eventInfo.GetSpellInfo()->GetAllEffectsMechanicMask() & ((1ULL << MECHANIC_INTERRUPT) | (1ULL << MECHANIC_SILENCE))))
|
||||||
return false;
|
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
|
// Xinef: immuned effect should just eat charge
|
||||||
if (eventInfo.GetHitMask() & PROC_EX_IMMUNE)
|
if (eventInfo.GetHitMask() & PROC_EX_IMMUNE)
|
||||||
{
|
{
|
||||||
eventInfo.GetActionTarget()->RemoveAurasDueToSpell(54748);
|
target->m_Events.AddEventAtOffset([target]() { target->RemoveAurasDueToSpell(54748); }, 1ms);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Aura* aura = eventInfo.GetActionTarget()->GetAura(54748))
|
if (Aura* aura = target->GetAura(54748))
|
||||||
{
|
{
|
||||||
if (aura->GetDuration() < aura->GetMaxDuration())
|
if (aura->GetDuration() < aura->GetMaxDuration())
|
||||||
eventInfo.GetActionTarget()->RemoveAurasDueToSpell(54748);
|
target->m_Events.AddEventAtOffset([target]() { target->RemoveAurasDueToSpell(54748); }, 1ms);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,7 +1050,14 @@ class spell_mage_combustion : public AuraScript
|
|||||||
// Do not take charges, add a stack of crit buff
|
// Do not take charges, add a stack of crit buff
|
||||||
if (!(eventInfo.GetHitMask() & PROC_HIT_CRITICAL))
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user