Hyjal : parler a Jaina faisait tomber le serveur -- et faction 16 au Rivage brise

1) PLANTAGE DU SERVEUR, cause reelle de la coupure de cette nuit

Trace du vidage 3007513 :
  #0 Trinity::Assert(...)
  #1 npc_jaina_proudmoore::OnGossipHello(Player*, Creature*)
  #2 WorldSession::HandleGossipHelloOpcode

hyjal.cpp utilisait ENSURE_AI(hyjalAI, creature->AI()), qui ASSERTE. Or
GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve, donc
uniquement dans l instance du Mont Hyjal. Ailleurs la creature recoit une
IA quelconque et l assertion tue le process.

Declencheur : un exemplaire de Jaina (17772) se trouve sur la carte 0, a
Hurlevent (-8295, 1386), en plus de celui du Mont Hyjal. Lui parler
suffisait a planter le serveur -- faille exploitable par n importe quel
joueur, sans commande ni privilege.

Les 5 occurrences passent en dynamic_cast avec sortie propre.

J avais d abord conclu a une recursion en me fiant a la taille du vidage
(387 Mo contre 133). C ETAIT FAUX : cette taille reflete la memoire
allouee au moment du plantage, pas un debordement de pile. La trace, elle,
etait parfaitement lisible.

2) RIVAGE BRISE : factions demoniaques ramenees a 16

Etabli par TEST A/B, pas par raisonnement. Une seule entree basculee en
faction 16 (Molosse de l effroi gangrene, 90686), toutes choses egales
par ailleurs. Verdict en jeu : « les molosses sont maintenant
attaquables », les autres non.

La sonde SPAWNDBG confirme que seule la faction distingue les deux :
  Felstalker Dreadhound  faction=16    drapeaux=32768 drapeaux2=0  OK
  Felguard Legionnaire   faction=2780  drapeaux=32768 drapeaux2=0  bloque

Pourquoi 2780 echoue reste INEXPLIQUE : FactionTemplate.db2 lui donne
EnemyGroup=15 (ennemi de tous) et sa faction 1786 a ReputationIndex=-1,
donc aucune reputation n intervient. Sur le papier elle devrait etre
hostile. Le refus vient du client, qu aucune sonde serveur ne peut
observer. On retient donc la faction 16, demontree fonctionnelle dans ce
scenario meme -- ecart assume avec la donnee de reference.

101 entrees, 464 spawns. Effet secondaire bienvenu : les demons cessent
de s entretuer (Friend_0=14).

3) COMPTEUR : tout demon compte desormais

Signale : « les molosses sont attaquables mais ne comptent pas dans l
objectif ». OnUnitDeath ne reconnaissait que les cinq entrees invoquees
par le script. On s appuie desormais sur le type demon plutot que sur une
liste de 101 entrees qui vieillirait mal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
SylvaniaCore deploy
2026-08-31 10:20:45 +02:00
parent b0b876b76b
commit 3a4c6c01dd
5 changed files with 262 additions and 25 deletions
+23 -20
View File
@@ -1574,6 +1574,29 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad
GetMap()->AddBattlePet(this);
// SONDE TEMPORAIRE - carte 1460. Etat REEL en memoire une fois la
// creature entierement chargee depuis la base, pour le comparer a ce
// que dit la table. Une sur dix : il y en a 757.
//
// La version precedente etait posee dans un bloc de rechargement que
// ce chemin n'emprunte jamais -- elle n'a produit aucune ligne.
if (GetMapId() == 1460)
{
static uint32 sondeSpawn = 0;
if ((++sondeSpawn % 10) == 1)
{
TC_LOG_ERROR("misc",
"SPAWNDBG %s (entree %u) | faction=%u niveau=%u | pv=" UI64FMTD "/" UI64FMTD
" | vivante=%u | drapeaux=%u drapeaux2=%u dynamiques=%u | phases=%u",
GetName().c_str(), GetEntry(), getFaction(), getLevel(),
GetHealth(), GetMaxHealth(),
uint32(IsAlive() ? 1 : 0),
GetUInt32Value(UNIT_FIELD_FLAGS), GetUInt32Value(UNIT_FIELD_FLAGS_2),
GetUInt32Value(OBJECT_DYNAMIC_FLAGS),
uint32(GetPhaseShift().GetPhases().size()));
}
}
return true;
}
@@ -3336,26 +3359,6 @@ void Creature::ReLoad(bool skipDB)
if (IsAIEnabled)
AI()->EnterEvadeMode();
// SONDE TEMPORAIRE - carte 1460 uniquement. Journalise l'etat REEL en
// memoire, pour le comparer a la base. Une creature sur dix suffit :
// il y en a 757, on veut un echantillon, pas un deluge.
if (GetMapId() == 1460)
{
static uint32 sondeSpawn = 0;
if ((++sondeSpawn % 10) == 1)
{
TC_LOG_ERROR("misc",
"SPAWNDBG %s (entree %u) | faction=%u niveau=%u | pv=" UI64FMTD "/" UI64FMTD
" | vivante=%u | drapeaux=%u drapeaux2=%u dynamiques=%u | phases=%u | reactState=%u",
GetName().c_str(), GetEntry(), getFaction(), getLevel(),
GetHealth(), GetMaxHealth(),
uint32(IsAlive() ? 1 : 0),
GetUInt32Value(UNIT_FIELD_FLAGS), GetUInt32Value(UNIT_FIELD_FLAGS_2),
GetUInt32Value(OBJECT_DYNAMIC_FLAGS),
uint32(GetPhaseShift().GetPhases().size()),
uint32(GetReactState()));
}
}
TC_LOG_DEBUG("sql.sql", "Creature SpawnID (" SI64FMTD ") reloaded.", GetSpawnId());
}
@@ -363,6 +363,27 @@ struct scenario_broken_shore_intro : public InstanceScript
}
break;
default:
// =====================================================
// SylvaniaCore : tout demon compte pour l objectif.
//
// SIGNALE EN JEU : « les molosses sont maintenant
// attaquables mais ne comptent pas dans l objectif du
// scenario ».
//
// Le script ne reconnaissait que les CINQ entrees qu il
// invoque lui-meme. Or la carte porte 101 entrees de
// demons placees, affrontees tout au long de l assaut :
// elles ne crediraient rien.
//
// Plutot que d enumerer 101 entrees -- liste qui
// vieillirait mal --, on s appuie sur la donnee : le type
// demon. Tous les demons de cette carte sont desormais
// hostiles (faction 16). Les Seigneurs gangrebois,
// Arganoth, Krosus et Gul dan sont traites avant et n
// arrivent jamais ici : ils ont leurs propres criteres.
// =====================================================
if (creature->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON)
OnDemonDied();
break;
}
}
@@ -57,7 +57,20 @@ public:
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
hyjalAI* ai = ENSURE_AI(hyjalAI, creature->AI());
// SylvaniaCore : ENSURE_AI faisait tomber TOUT le serveur.
//
// GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve,
// c est-a-dire dans l instance du Mont Hyjal. Ailleurs la creature
// recoit une IA quelconque -- et ENSURE_AI, qui ASSERTE, tuait le
// process. Constate en production : un exemplaire de Jaina (17772)
// se trouvait sur la carte 0, a Hurlevent ; lui parler suffisait a
// planter le serveur (SIGSEGV dans Trinity::Assert, appele depuis
// npc_jaina_proudmoore::OnGossipHello).
//
// On echoue desormais proprement : le PNJ ne repond simplement pas.
hyjalAI* ai = dynamic_cast<hyjalAI*>(creature->AI());
if (!ai)
return false;
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
@@ -81,7 +94,20 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
hyjalAI* ai = ENSURE_AI(hyjalAI, creature->AI());
// SylvaniaCore : ENSURE_AI faisait tomber TOUT le serveur.
//
// GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve,
// c est-a-dire dans l instance du Mont Hyjal. Ailleurs la creature
// recoit une IA quelconque -- et ENSURE_AI, qui ASSERTE, tuait le
// process. Constate en production : un exemplaire de Jaina (17772)
// se trouvait sur la carte 0, a Hurlevent ; lui parler suffisait a
// planter le serveur (SIGSEGV dans Trinity::Assert, appele depuis
// npc_jaina_proudmoore::OnGossipHello).
//
// On echoue desormais proprement : le PNJ ne repond simplement pas.
hyjalAI* ai = dynamic_cast<hyjalAI*>(creature->AI());
if (!ai)
return false;
if (ai->EventBegun)
return false;
@@ -136,7 +162,20 @@ public:
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
hyjalAI* ai = ENSURE_AI(hyjalAI, creature->AI());
// SylvaniaCore : ENSURE_AI faisait tomber TOUT le serveur.
//
// GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve,
// c est-a-dire dans l instance du Mont Hyjal. Ailleurs la creature
// recoit une IA quelconque -- et ENSURE_AI, qui ASSERTE, tuait le
// process. Constate en production : un exemplaire de Jaina (17772)
// se trouvait sur la carte 0, a Hurlevent ; lui parler suffisait a
// planter le serveur (SIGSEGV dans Trinity::Assert, appele depuis
// npc_jaina_proudmoore::OnGossipHello).
//
// On echoue desormais proprement : le PNJ ne repond simplement pas.
hyjalAI* ai = dynamic_cast<hyjalAI*>(creature->AI());
if (!ai)
return false;
ai->DeSpawnVeins();//despawn the alliance veins
switch (action)
{
@@ -161,7 +200,20 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
hyjalAI* ai = ENSURE_AI(hyjalAI, creature->AI());
// SylvaniaCore : ENSURE_AI faisait tomber TOUT le serveur.
//
// GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve,
// c est-a-dire dans l instance du Mont Hyjal. Ailleurs la creature
// recoit une IA quelconque -- et ENSURE_AI, qui ASSERTE, tuait le
// process. Constate en production : un exemplaire de Jaina (17772)
// se trouvait sur la carte 0, a Hurlevent ; lui parler suffisait a
// planter le serveur (SIGSEGV dans Trinity::Assert, appele depuis
// npc_jaina_proudmoore::OnGossipHello).
//
// On echoue desormais proprement : le PNJ ne repond simplement pas.
hyjalAI* ai = dynamic_cast<hyjalAI*>(creature->AI());
if (!ai)
return false;
if (ai->EventBegun)
return false;
@@ -244,7 +296,20 @@ public:
bool OnGossipHello(Player* player, Creature* creature) override
{
hyjalAI* ai = ENSURE_AI(hyjalAI, creature->AI());
// SylvaniaCore : ENSURE_AI faisait tomber TOUT le serveur.
//
// GetAI() ne fabrique une hyjalAI que si GetHyjalAI() la trouve,
// c est-a-dire dans l instance du Mont Hyjal. Ailleurs la creature
// recoit une IA quelconque -- et ENSURE_AI, qui ASSERTE, tuait le
// process. Constate en production : un exemplaire de Jaina (17772)
// se trouvait sur la carte 0, a Hurlevent ; lui parler suffisait a
// planter le serveur (SIGSEGV dans Trinity::Assert, appele depuis
// npc_jaina_proudmoore::OnGossipHello).
//
// On echoue desormais proprement : le PNJ ne repond simplement pas.
hyjalAI* ai = dynamic_cast<hyjalAI*>(creature->AI());
if (!ai)
return false;
uint32 AzgalorEvent = ai->GetInstanceData(DATA_AZGALOREVENT);
// Only let them get item if Azgalor is dead.