fix(loot+firelands): stop cyclic loot-ref stack overflow & make heroic Ragnaros killable
LootMgr: LootTemplate::Process gains a thread_local recursion-depth guard (RAII, cap 50). A self-referencing reference_loot_template row (ref 104 -> 104) recursed until the worldserver stack overflowed and crashed on ICC boss/trash loot; this core does not reject such cycles at load. DB row already removed live; this hardens against any future cycle. boss_ragnaros_firelands: heroic phase 4 had no completion path (every SetData(DONE)/EVENT_DIE lived in the !IsHeroic branch), leaving Ragnaros unkillable in heroic. Add a heroic death branch gated on new flag heroicPhaseFour (set after the phase-4 heal) so the encounter completes and grants credit/loot. Normal mode unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -585,6 +585,20 @@ void LootTemplate::CopyConditions(LootItem* li) const
|
|||||||
// Rolls for every item in the template and adds the rolled items the the loot
|
// Rolls for every item in the template and adds the rolled items the the loot
|
||||||
void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId, Player const* player /*= nullptr*/, bool specOnly /*= false*/) const
|
void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, uint8 groupId, Player const* player /*= nullptr*/, bool specOnly /*= false*/) const
|
||||||
{
|
{
|
||||||
|
// Sylvania: guard against cyclic loot references (e.g. a reference_loot_template
|
||||||
|
// entry that references itself). Without this, such a cycle recurses until the
|
||||||
|
// worldserver stack overflows and crashes (observed on ICC loot, ref 104). The
|
||||||
|
// load-time loot verification of this core does not reject self/cyclic references,
|
||||||
|
// so we cap recursion defensively. No legitimate loot chain nests this deep.
|
||||||
|
static uint32 const MAX_LOOT_REFERENCE_DEPTH = 50;
|
||||||
|
static thread_local uint32 lootProcessDepth = 0;
|
||||||
|
struct DepthGuard { uint32& d; explicit DepthGuard(uint32& r) : d(r) { ++d; } ~DepthGuard() { --d; } } depthGuard(lootProcessDepth);
|
||||||
|
if (lootProcessDepth > MAX_LOOT_REFERENCE_DEPTH)
|
||||||
|
{
|
||||||
|
TC_LOG_ERROR("sql.sql", "LootTemplate::Process: loot reference recursion exceeded %u levels (likely a self-referencing or cyclic reference_loot_template entry) - aborting to prevent a stack-overflow crash", MAX_LOOT_REFERENCE_DEPTH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (groupId) // Group reference uses own processing of the group
|
if (groupId) // Group reference uses own processing of the group
|
||||||
{
|
{
|
||||||
if (groupId > Groups.size())
|
if (groupId > Groups.size())
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ class boss_ragnaros_firelands: public CreatureScript
|
|||||||
Creature* smash;
|
Creature* smash;
|
||||||
Creature* splitting;
|
Creature* splitting;
|
||||||
uint8 sonOfFlameCount;
|
uint8 sonOfFlameCount;
|
||||||
bool HeartCheck, introDone, intermission1, intermission1InProgress, intermission2, intermission2InProgress, phase3, inMeleeRange, heroicPhase, died;
|
bool HeartCheck, introDone, intermission1, intermission1InProgress, intermission2, intermission2InProgress, phase3, inMeleeRange, heroicPhase, heroicPhaseFour, died;
|
||||||
|
|
||||||
void Reset() override
|
void Reset() override
|
||||||
{
|
{
|
||||||
@@ -454,6 +454,7 @@ class boss_ragnaros_firelands: public CreatureScript
|
|||||||
phase3 = false;
|
phase3 = false;
|
||||||
inMeleeRange = false;
|
inMeleeRange = false;
|
||||||
heroicPhase = false;
|
heroicPhase = false;
|
||||||
|
heroicPhaseFour = false;
|
||||||
died = false;
|
died = false;
|
||||||
sonOfFlameCount = 0;
|
sonOfFlameCount = 0;
|
||||||
|
|
||||||
@@ -706,6 +707,42 @@ class boss_ragnaros_firelands: public CreatureScript
|
|||||||
events.ScheduleEvent(EVENT_PHASE_FOUR, 1000);
|
events.ScheduleEvent(EVENT_PHASE_FOUR, 1000);
|
||||||
heroicPhase = true;
|
heroicPhase = true;
|
||||||
}
|
}
|
||||||
|
// Sylvania fix: heroic phase 4 ("True Power of the Firelord") previously
|
||||||
|
// had NO completion path, leaving Ragnaros unkillable in heroic Firelands
|
||||||
|
// (every SetData(DONE)/EVENT_DIE site lived only in the !IsHeroic() branch).
|
||||||
|
// Mirror the normal-mode death once phase 4 is actually underway so the
|
||||||
|
// encounter completes (me->Kill -> _JustDied -> SetBossState DONE) and
|
||||||
|
// credit/achievement/loot are granted. Gated on heroicPhaseFour (set after
|
||||||
|
// the phase-4 heal) so a sub-10% burst during the ~1s entry window cannot
|
||||||
|
// short-circuit phase 4.
|
||||||
|
else if (HealthBelowPct(10) && IsHeroic() && heroicPhaseFour && !died)
|
||||||
|
{
|
||||||
|
Talk(SAY_DEATH_H);
|
||||||
|
|
||||||
|
me->AttackStop();
|
||||||
|
me->CastStop();
|
||||||
|
me->SetReactState(REACT_PASSIVE);
|
||||||
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
me->RemoveAllAuras();
|
||||||
|
if (instance->GetData(DATA_TEAM) == ALLIANCE)
|
||||||
|
{
|
||||||
|
me->SummonGameObject(GO_CACHE_OF_THE_FIRELORD, 1016.043f, -57.436f, 55.333f, 3.151f, QuaternionData(), 30000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
me->SummonGameObject(GO_CACHE_OF_THE_FIRELORD_H, 1016.043f, -57.436f, 55.333f, 3.151f, QuaternionData(), 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance)
|
||||||
|
{
|
||||||
|
instance->SetData(DATA_RAGNAROS, DONE);
|
||||||
|
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); // Remove
|
||||||
|
}
|
||||||
|
|
||||||
|
events.ScheduleEvent(EVENT_DIE, 2000);
|
||||||
|
me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE);
|
||||||
|
died = true;
|
||||||
|
}
|
||||||
|
|
||||||
events.Update(diff);
|
events.Update(diff);
|
||||||
|
|
||||||
@@ -1032,6 +1069,7 @@ class boss_ragnaros_firelands: public CreatureScript
|
|||||||
Talk(SAY_PHASE_HEROIC_TEXT);
|
Talk(SAY_PHASE_HEROIC_TEXT);
|
||||||
me->RemoveAurasDueToSpell(SPELL_BASE_VISUAL);
|
me->RemoveAurasDueToSpell(SPELL_BASE_VISUAL);
|
||||||
me->SetHealth(me->GetMaxHealth() / 2);
|
me->SetHealth(me->GetMaxHealth() / 2);
|
||||||
|
heroicPhaseFour = true; // Sylvania: phase 4 is now active -> arm the heroic death check
|
||||||
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
|
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
|
||||||
me->SetObjectScale(1.2f);
|
me->SetObjectScale(1.2f);
|
||||||
events.ScheduleEvent(EVENT_SUPERHEATED, 10000);
|
events.ScheduleEvent(EVENT_SUPERHEATED, 10000);
|
||||||
|
|||||||
Reference in New Issue
Block a user