Commit Graph

157 Commits

Author SHA1 Message Date
SylvaniaCore deploy e0b2fcf3e0 Creatures volantes : aucun chemin vers une cible hors du maillage, evade et butin perdu
_setTargetLocation ne passait pas CanFly() en forceDest a CalculatePath. Une
creature volante dont la cible se tient sur un relief non navigable obtenait
PATHFIND_NOPATH, se marquait injoignable, puis evadait au bout de cinq secondes
(CREATURE_NOPATH_EVADE_TIME).

Chaque evade passe par CreatureAI::_EnterEvadeMode, qui lache le proprietaire de
butin et reinitialise m_PlayerDamageReq. A la mort, Unit::Kill force alors
SetLootRecipient(NULL) : le corps est vide et ne rapporte aucune experience. Le
symptome remonte comme un bug de table de butin alors que la table est saine.

Signale sur le Proto-drake perdu dans le temps (32491) aux Pics Foudroyes, qui
lachait l aggro tant que le joueur n etait pas sur une plateforme reliee au
maillage, puis n a rien donne une fois tue. 3709 creatures volantes du monde
etaient concernees.

Aligne sur TrinityCore amont, identique en 3.3.5 et master :
    bool success = _path->CalculatePath(x, y, z, owner->CanFly());

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 08:43:25 +02:00
Sylvania 0b2a042175 Arenes cotees : bracket 14 code en dur, hors plage et introuvable
BattlegroundMgr::Update forcait la mise a jour des files cotees avec
BattlegroundBracketId(14) -- la boucle sur les brackets avait ete
commentee en amont. Deux consequences :

- m_QueuedGroups ne compte que MAX_BATTLEGROUND_BRACKETS (12) entrees :
  chaque passage lisait au-dela du tableau, toutes les 5 secondes.
- aucune entree PVPDifficulty ne correspond au couple (carte du template
  BATTLEGROUND_AA, bracket 14), donc la fonction sortait aussitot en
  journalisant une erreur : 585 000 lignes dans dc-world.log, soit la
  moitie du fichier, et le force-update des arenes cotees ne servait
  a rien depuis toujours.

On parcourt desormais les brackets reellement declares pour la carte, et
BattlegroundQueueUpdate refuse un bracket hors plage au lieu de lire a
cote.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 22:43:06 +02:00
Sylvania 04b6dd20f5 Playerbots : opcodes manquants sur les paquets de sortie de champ de bataille
ProcessLeaveBG et ProcessLeaveAA construisaient BattlefieldLeave a partir
dun WorldPacket() par defaut (opcode 65535), et ProcessInAAQueue passait
CMSG_BATTLEMASTER_JOIN_SKIRMISH a BattlemasterJoinArena. Le constructeur
de ClientPacket asserte GetOpcode() == expectedOpcode : le thread monde
mourait des quun bot devait quitter un champ de bataille (fin de match ou
depart dun vrai joueur), laissant le processus fige et les joueurs bloques
a lecran de chargement.

Signale sur le Discord avec une pile gdb par un utilisateur du core.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 22:27:43 +02:00
BlaMacfly ac40810f25 Champs de bataille : le vrai appelant hors limites, et un opcode client diffuse
Suite du signalement Discord. Le garde-fou pose dans GetBGObject a tenu
-- plus de lecture hors limites -- mais les appels avec 24 et 32
persistaient : le correctif precedent visait BattlegroundAB.cpp alors que
l'appelant fautif est le module BotFill.

CommandAB.cpp indexait les bannieres en abNode * 8, disposition de
l'ancien Bassin d'Arathi de TrinityCore ou chaque noeud possedait huit
objets. Le notre n'en a qu'UN par noeud, aux indices 0 a 4. Le calcul
etait donc faux pour TOUS les noeuds :

    noeud 0 -> 0   banniere              juste, par hasard
    noeud 1 -> 8   REGENBUFF_STABLES
    noeud 2 -> 16  SPEEDBUFF_LUMBER_MILL
    noeud 3 -> 24  hors limites
    noeud 4 -> 32  hors limites

Les bots visaient des objets de bonus au lieu des bannieres et ne
voyaient tout simplement pas la scierie ni la mine d'or. Les deux sites
etant proteges contre le pointeur nul, cela echouait en silence : seul le
garde-fou a fini par le rendre visible. Le controle de borne manquant sur
abNode est ajoute.

Verifie au passage : Gilneas a REELLEMENT huit objets par noeud
(BG_BFG_OBJECT_MAX = 37), son node * 8 + 5 est correct. Rien a y changer.

Second defaut, sans lien avec le premier : deux fonctions de l'IA des
bots construisaient un paquet portant l'opcode CLIENT CMSG_MOVE_FALL_LAND
et le diffusaient. WorldSession le refuse toujours et journalise une
ERREUR a chaque tentative -- des milliers de lignes par match dans le
journal du rapporteur. BotBGAIMovement::SyncPosition n'avait meme aucun
autre effet : elle ne deplacait pas le bot cote serveur. Les envois sont
retires ; aucun comportement de remplacement n'a ete invente, le defaut
n'etant pas reproductible chez nous.

RESERVE IMPORTANTE : rien de ceci n'explique le PLANTAGE signale. Les
deux sites fautifs testaient deja le pointeur nul, et le journal fourni
ne contient aucune trace d'arret. Une pile d'appels est necessaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 08:08:00 +02:00
BlaMacfly a95670ac0a Rivage brise : portage de l'Assaut 7.2 (scenario 1280, carte 1666)
La carte 1666 etait declaree mais entierement vide, et instance_template
y reclamait un script fantome. Les quetes 45102 « Begin the Assault » et
46734 « Assault on Broken Shore » etaient donc infranchissables : elles
relevent de ce scenario, et non de l'introduction 7.0 deja portee sur la
carte 1460.

PORTAGE, PAS TRANSCRIPTION. La source (dufernst/LegionCore-7.3.5, meme
lignee uwow) repose sur quatre extensions que nous n'avons pas :
onScenarionNextStep/getScenarionStep, CRITERIA_TYPE_SCRIPT_EVENT_2,
FunctionProcessor et une surcharge de GetClosestGraveYard. La
progression est donc inversee : au lieu d'etre rappele a chaque etape,
le script alimente les criteres officiels et le moteur avance seul --
la meme architecture que sur la carte 1460.

Les neuf criteres des huit etapes ont ete releves dans les DB2 du build
7.3.5.26972 (ScenarioStep, CriteriaTree, Criteria) et non recopies de la
source ; ils confirment ses identifiants d'asset. Sept sont de type 92
(SEND_EVENT_SCENARIO), un de type 73, un de type 68.

Six ecarts d'API traites, tous constates et non supposes :
InstanceScript(InstanceMap*), Conversation::CreateConversation (Player
n'a pas cette methode -- la source l'appelait sur une creature, cela
n'aurait pas compile), UNIT_NPC_FLAGS avec SetFlag64, DamageTaken a 2
arguments, OnSpellClick a 2, MovePath a 2.

NON PORTE : player_scripts_for_start_assault, qui sondait chaque joueur
a chaque tick pour lui imposer la quete 46730. Un donneur en base fait
le meme travail sans ce cout.

SQL joint :
- rattachement des 5 scripts a leurs 11 entrees, toutes verifiees
  exclusives a la carte 1666 ;
- les 87 points des 5 chemins scriptes. Ils vivaient dans
  waypoint_data_script, table absente de notre schema, d'ou leur oubli
  lors de l'extraction du 27/08. Le 11322708 est le vol d'arrivee :
  l'atteinte de son point 20 declenche l'etape 0, sans lui rien ne
  demarrait ;
- le donneur manquant de 45102, l'archimage Khadgar 116302, deja pose
  chez nous aux coordonnees de la reference mais jamais declare.

Les 593 creatures et 59 objets de la carte etaient generes depuis le
27/08 et n'avaient jamais ete appliques ; ils le sont desormais.

RESERVE : rien de tout ceci n'a ete verifie en jeu. Les credits de quete
116253 et 116279 sont en outre cables sur la fermeture des deux premiers
portails par deduction -- la quete parle de « First » et « Second Legion
Spire destroyed » -- et non sur une donnee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 23:34:53 +02:00
BlaMacfly ce69bad85b Bassin d'Arathi : lecture hors limites de BgObjects
Signale par un utilisateur de SylvaniaCore : ses matchs de Bassin
d'Arathi avec pbotbg=1 faisaient tomber le serveur, avec dans le journal
  GetBGObject: gameobject (type: 24, ... Entry: 7735234) not found
  GetBGObject: gameobject (type: 32, ... Entry: 0)       not found
soit des GUID manifestement arbitraires, sur une carte 529 dont le
tableau BgObjects ne compte que 22 cases.

GetNearGameObjectFlag, introduit par le module BG BotFill (a7b1fa78),
indexait BgObjects en node*8+status. Cette disposition -- huit objets
par noeud -- est celle de l'ancien Bassin d'Arathi de TrinityCore. Le
notre n'a qu'UNE banniere par noeud, aux indices 0 a 4 : _ChangeBanner
modifie son visuel selon l'etat au lieu d'echanger huit objets. Le
calcul donnait donc 24 pour le noeud 3 et 32 pour le noeud 4.

Le defaut de fond est ailleurs : huit accesseurs de Battleground.cpp
indexent BgObjects ou BgCreatures avec une valeur fournie par
l'appelant, sans jamais verifier la borne -- AddObject y ECRIT meme.
Une faute de calcul y devient une corruption memoire, et le plantage
survient loin de son origine. Tous sont desormais bornes : ils
journalisent l'indice fautif et renvoient l'echec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:32:29 +02:00
BlaMacfly a4d245ecbe Commandes : .npc add perdait la phase du maitre de jeu
Les PNJ crees en jeu n'etaient visibles qu'en mode MJ. InheritPhaseShift
donnait bien la phase du joueur a la creature, mais SaveToDB n'ecrit que
GetDBPhase() -- la phase DECLAREE EN BASE, nulle pour une creature creee
a la volee. La commande detruit ensuite l'objet et le recree depuis la
ligne enregistree, donc sans phase. Dans une aire phasee, PhaseShift::
CanSee exigeant une intersection, la creature etait invisible a tout
joueur normal ; le mode MJ masquait le defaut, SetAlwaysVisible
court-circuitant le test de phase.

TrinityCore 3.3.5 transmettait la phase explicitement a Create et a
SaveToDB, via un GetPhaseMaskForSpawn qui ignorait volontairement l'etat
« MJ voit tout ». La reecriture du systeme de phases a perdu ce principe :
master a le meme defaut. On le restaure, en annoncant la phase retenue
puisqu'un joueur moderne peut en porter plusieurs alors qu'une ligne de
la table creature n'a qu'un champ.

SQL joint : phase 6666 posee sur l'aubergiste deja pose au port de
Hurlevent, et suppression de 18 options de dialogue de type 1 qui
doublonnaient une option fonctionnelle et se placaient au-dessus d'elle.
Les options porteuses de conditions sont preservees -- une premiere
version avait supprime a tort les options d'Halloween.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:21:31 +02:00
SylvaniaCore deploy e1ccf30afc Sessions : les comptes au-dessus du niveau 0 ne sont plus coupes pour inactivite
Demande : « j aimerai que les comptes au-dessus du numero 0, c est a dire
les comptes GM/testeur/admin, ne soient pas soumis en jeu a la
deconnexion automatique en cas d absence prolongee ».

La coupure vient de WorldSession::Update :

    if (!IsBotSession() && IsConnectionIdle())
        m_Socket[CONNECTION_TYPE_REALM]->CloseSocket();

IsConnectionIdle() s appuie sur m_timeOutTime, initialise depuis
SocketTimeOutTime (900 000 ms, soit 15 minutes) et decremente a chaque
tick sans paquet entrant.

On ajoute GetSecurity() <= SEC_PLAYER a la condition. Les comptes de
niveau superieur restent connectes indefiniment ; les joueurs ordinaires
demeurent soumis au delai, la protection contre les sessions fantomes
gardant tout son interet pour eux.

Motif concret : ces comptes restent volontairement inactifs de longues
minutes pour observer une zone, suivre un evenement scripte ou attendre
le resultat d un test -- et se faisaient couper en plein travail, parfois
au milieu d une instance.

Portee actuelle : 5 comptes concernes (3 de niveau 1, 2 de niveau 4).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 10:32:05 +02:00
SylvaniaCore deploy 80a4a589f6 worldserver.conf.dist : pbotqa et les deux cles du Bond heroique manquaient 2026-08-31 10:29:26 +02:00
SylvaniaCore deploy 0bc9b016d6 Commandes : .npc near faisait tomber le serveur
Signale en jeu : « la commande .npc near fait planter le serveur ».
L hypothese de l utilisateur etait proche : ce n est pas le rayon en soi,
c est l absence de plafond.

La requete preparee WORLD_SEL_CREATURE_NEAREST n a AUCUNE limite :

  SELECT ... FROM creature
   WHERE map = ? AND (POW(x-?,2)+POW(y-?,2)+POW(z-?,2)) <= ?
   ORDER BY order_

Elle calcule trois puissances sur les 384 000 lignes de la table, sans
index utilisable, puis la boucle d affichage envoie UN message de
discussion PAR RESULTAT. Avec un rayon large cela represente des dizaines
de milliers de paquets pour une seule commande, ce qui sature la session.

La requete etant partagee avec d autres commandes, on pose les garde-fous
cote appelant : rayon plafonne a 500, affichage plafonne a 150 lignes.

Les omissions sont ANNONCEES et non silencieuses : la commande indique
combien de resultats n ont pas ete montres et invite a reduire le rayon.
Un plafond muet ferait croire a une recherche complete.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 10:26:10 +02:00
SylvaniaCore deploy 3a4c6c01dd 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>
2026-08-31 10:20:45 +02:00
SylvaniaCore deploy fa7a543b94 worldserver.conf.dist : les 10 cles du module Mercenaires manquaient 2026-08-31 10:09:14 +02:00
SylvaniaCore deploy 5408eae697 Rivage brise 1460 : UNIT_FLAG2_UNK5 rendait les demons inattaquables
Signale en jeu, nommement : Gangreseigneur Rakkan, Legionnaire
gangregarde, Molosse de l effroi gangrene. Et l observation decisive :
« les demons inattaquables se battent avec les demons attaquables ».

LA CAUSE EST DE MON FAIT. La transposition des modeles a importe
unit_flags2 sur 147 entrees. Ces creatures valaient 0 avant ; elles ont
recu 2097152, soit 0x200000 -- UNIT_FLAG2_UNK5.

CE DRAPEAU EST PARTICULIER : notre serveur l IGNORE totalement, aucune
de ses verifications ne le consulte. Le client, lui, l interprete et
refuse de designer la creature comme cible.

C est ce qui explique le symptome le plus deroutant de la serie : la
sonde posee dans _IsValidAttackTarget n a JAMAIS produit la moindre
ligne pour ces creatures. Le client refusait de lui-meme, sans jamais
interroger le serveur. L ABSENCE DE TRACE ETAIT L INFORMATION -- j ai
mis trois tours a la lire comme telle.

CORRELATION QUI TRANCHE :
  demons INVOQUES (attaquables) : unit_flags2 = 0
  demons STATIQUES (bloques)    : bit 0x200000 present
  67 entrees, 420 spawns.

Seul ce bit est retire, le reste de la colonne est preserve. Le serveur
ne s en sert pas : le retrait ne peut rien casser cote logique.

Egalement dans ce lot, deux sondes conservees le temps de la validation :
ATTDBG (recentree sur les factions 2780/1768) et SPAWNDBG (etat reel des
creatures a leur creation). Deux divergences d API relevees au passage :
getFaction() et getLevel(), en minuscule dans ce fork.

Retour arriere joint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 22:51:16 +02:00
SylvaniaCore deploy 0ac509e421 Rivage brise 1460 : le phasage rendait les creatures inattaquables
Signale en jeu : creatures visibles, hostiles, mais impossibles a cibler
-- alors qu elles pouvaient attaquer le joueur.

J avais enchaine trois hypotheses (phase, factions, modeles souches).
Les trois etaient de VRAIS defauts et meritaient correction, mais aucune
n etait la cause. J aurais du poser la sonde d abord.

VERDICT DE LA SONDE, dans _IsValidAttackTarget :
    Felblade Destroyer | vivante=1 | aucun drapeau bloquant
    reaction=1 (hostile) | mesPhases=0  sesPhases=1

Le joueur n a AUCUNE phase, la creature en a une. PhaseShift::CanSee
exige une intersection des phases, et UpdateUnphasedFlag retire le
statut « non phase » des qu un objet en possede une. Aucune intersection
possible.

C est MA modification precedente qui a introduit ce decalage : j avais
place les 757 spawns en phase 169 en croyant que le joueur y etait,
en me fiant a un commentaire du script. Il ne l etait pas.

CORRECTIF : suppression du phasage des deux cotes, ce qui rejoint la
configuration de la reference -- le dump laisse les 757 placements de
cette carte SANS phase. La phase 169 n existe d ailleurs pas dans
Phase.db2 du build 7.3.5.26972.
  - PhaseId remis a 0 sur les 757 creatures et les 101 objets ;
  - AddPhase retire de OnPlayerEnter ;
  - FinalizeSummon ne phase plus rien (conservee comme point de passage
    unique, sans quoi les invocations deviendraient invisibles a un
    joueur non phase -- le meme probleme en sens inverse).

La sonde ATTDBG est CONSERVEE le temps de la validation en jeu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 22:11:21 +02:00
SylvaniaCore deploy 966a866e7c Mardum : le point de foyer etait fixe a l origine au lieu de la destination
Signale en jeu : « j ai utilise ma pierre de foyer qui ne m a pas
teleporte, je devais atterrir en foret d Elwynn et il est ecrit vous
renvoie a Fleur-de-l hiver alors que je n ai pas change le lieu ».

La pierre fonctionnait parfaitement : elle renvoyait a Mardum, ou le
joueur se trouvait deja -- d ou l impression qu elle ne faisait rien.

CAUSE, dans spell_mardum_back_to_black_temple :

    player->TeleportTo(1468, 4325.46f, -620.53f, -281.40f, 1.517563f);
    player->SetHomebind(player->GetWorldLocation(), 7873);

TeleportTo est ASYNCHRONE : le joueur n est pas deplace a la ligne
suivante, si bien que GetWorldLocation() renvoyait encore la position de
MARDUM. Le point de foyer se retrouvait donc fixe la ou le joueur venait
de partir, au lieu du Caveau des Gardiennes (carte 1468) visé.

Constate en base : character_homebind du personnage pointait sur
mapId 1481 (Mardum), zone 6383.

On lie desormais explicitement a la DESTINATION, via une WorldLocation
construite une fois et passee aux deux appels.

Le point de foyer du personnage affecte a ete remis en foret d Elwynn,
avec des coordonnees reprises d un homebind Alliance deja valide dans
notre base plutot qu inventees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:36:45 +02:00
SylvaniaCore deploy 42427a8a72 Rivage brise 1460 : rebrancher les vrais compteurs de l etape 1
Signale en jeu : « la phase 1 ou il fallait tuer 33 demons ainsi que
d autres objectifs a ete skip et je suis passe en phase 3 direct », puis
« les objectifs ne se remplissent jamais ».

DEUX COMPTEURS VIVAIENT EN PARALLELE, et ne disaient pas la meme chose.

Le client affichait les criteres officiels de l arbre 42935 (« Broken
Shore - Stage 1 », operateur ALL) du build 7.3.5.26972 :
    43010  Demons slain             33   critere 27653
    46549  Fel Lords slain           3   critere 29377
    46548  Spires of Woe destroyed   3   critere 27619
Tous trois de type CRITERIA_TYPE_SEND_EVENT_SCENARIO (92) : ils ne se
remplissent PAS en tuant, mais quand le script emet l evenement
correspondant. Le script ne l emettait jamais -- d ou le 0/33 fige.

En parallele, il tenait sa propre comptabilite et cloturait l etape a
KILLS_BEACH = 12, un chiffre invente. D ou l impression de saut : a 12
demons il invoquait Arganoth avec SetInCombatWithZone(), lequel mourait
aussitot et faisait franchir une seconde etape.

CORRECTIF -- on ne touche PAS aux objectifs, qui sont officiels et de
toute facon dans les donnees du client. On supprime le seuil invente et
on emet les vrais evenements :
  - chaque demon tue emet 44095 ;
  - chaque Seigneur gangrebois emet 52643 (les treize entrees placees
    sur la carte sont enumerees explicitement, le nom n etant pas une
    donnee stable) ;
  - chaque Fleche actionnee emet 44077 ;
  - l etape ne s acheve que si les TROIS seuils officiels sont atteints
    (33 / 3 / 3), conformement a l operateur ALL de l arbre.

LES FLECHES sont des objets de type 10 : ACTIONNES, pas detruits. Ni
OnGameObjectCreate ni OnGameObjectRemove ne rapportent cette
utilisation. Le seul accrochage est GameObjectScript::OnGossipHello, que
GameObject::Use appelle avant tout traitement specifique
(GameObject.cpp:1379). D ou la classe go_spire_of_woe -- ET son
rattachement en base, sans lequel elle n aurait jamais ete invoquee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 13:05:22 +02:00
SylvaniaCore deploy 1507d1084d Brasserie : les vermifuges s accumulaient sans aucune limite
Signale en jeu : « il y a beaucoup trop de Cognedur et de Sautilleur,
c est totalement anormal », et plus tot « les lapins bouclent ».

Le script d instance appelle DoAction toutes les 12 a 14 secondes, EN
BOUCLE, tant que l etat de Hoptallus n est pas SPECIAL -- c est-a-dire
tant qu il n a pas jailli de son tonneau. La boucle demarre dix secondes
apres la creation de l instance. Chaque passage invoquait 3 a 4
Sautillons et 1 a 2 Sautilleurs, sans AUCUNE limite et sans qu aucun
joueur ait besoin d etre present.

Consequence : pendant qu on affronte Ook-Ook, a 105 m de la (distance
mesuree entre les deux spawns), la salle des tonneaux se remplit toute
seule. Sur vingt minutes de donjon, de l ordre de quatre cents creatures
vivantes accumulees.

En jeu officiel ces vermifuges sortent quand le joueur CASSE les
tonneaux. Faute de cette mecanique, on garde le minuteur mais on lui
pose deux garde-fous : aucune invocation si aucun joueur n est a 60 m
(portee choisie pour couvrir la salle sans jamais atteindre l arene
d Ook-Ook, mesuree a 105 m), et vague sautee au-dela de 20 vermifuges
vivants. summons suit deja les invocations vivantes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 12:34:29 +02:00
SylvaniaCore deploy 7f8cdd2d4b Scenarios : la verification de l etape doit etre purement lectrice
Correctif du commit precedent, qui FAISAIT PLANTER LE SERVEUR.

Il appelait CheckCompletedCriteriaTree sur l arbre racine. Enchainement
du debordement de pile (SIGSEGV, vidage de 389 Mo contre 120 Mo en temps
normal) :

  feuille achevee -> Scenario::CompletedCriteriaTree(feuille)
    -> CheckCompletedCriteriaTree(racine)
      -> operateur ALL : parcourt les enfants
        -> CheckCompletedCriteriaTree(enfant)
          -> enfant complet ET absent de _completedCriteriaTree
            -> CompletedCriteriaTree(enfant)  ... et on reboucle.

Le garde anti-reentrance existe pourtant (CriteriaHandler.cpp:1109),
mais l insertion dans _completedCriteriaTree se fait dans
CriteriaHandler::CompletedCriteriaTree -- que cette surcharge ne
rappelle jamais. L arbre n est donc jamais enregistre et le garde ne se
declenche pas.

On se contente desormais de LIRE la progression via IsCompletedCriteria,
en parcourant l arbre avec WalkCriteriaTree, sans jamais repasser par le
moteur d evaluation. Plus aucun effet de bord, donc plus de reentrance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 12:05:59 +02:00
SylvaniaCore deploy 85b0251bc1 Scenarios : le premier critere achevé cloturait toute l etape
Signale en jeu, capture a l appui : a la Brasserie brune d Orage, la
liste des trois boss disparaissait entierement des qu Ook-Ook tombait,
au lieu de cocher 1/1 et de conserver les deux autres.

Le scenario 537 n a qu UNE etape (972), dont l arbre de criteres 36317
porte trois feuilles : 36318 Ook-Ook, 36319 Hoptallus, 36320 Yan-Zhu.

CriteriaMgr::LoadCriteriaList attribue l etape via GetEntry, qui REMONTE
la chaine des parents jusqu a trouver une correspondance -- chaque
feuille herite donc de l etape 972. Et le moteur n evalue que les
feuilles : GetCriteriaTreesByCriteria ne renvoie que les arbres portant
directement le critere, jamais la racine.

Resultat : la premiere feuille achevee marquait l ETAPE ENTIERE
terminee. CompleteStep ne trouvait alors aucune etape suivante,
IsComplete() repondait oui, et le scenario etait cloture des le premier
boss.

On verifie desormais l arbre RACINE de l etape avant de conclure. Un
garde naif sur « tree doit etre la racine » ne conviendrait pas : la
racine n etant jamais transmise ici, l etape ne s acheverait alors
JAMAIS. CheckCompletedCriteriaTree pouvant rappeler cette fonction avec
la racine, on relit l etat de l etape ensuite -- sans quoi CompleteStep
serait execute deux fois et la quete de recompense octroyee en double.

Correctif dans le moteur partage : il vaut pour tous les scenarios dont
une etape porte plusieurs criteres.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:46:52 +02:00
SylvaniaCore deploy 8238ca0925 Brasserie : l instance n enregistrait jamais rien
Signale en jeu : « le donjon semble avoir declenche le done total au
1er boss, je n ai plus la liste des autres boss a tuer ».

La chaine de sauvegarde etait construite par une methode Save() qui
n etait APPELEE NULLE PART. Le tampon SaveDataBuffer restait donc vide,
et InstanceScript::SaveToDB abandonne des que la chaine est vide :

    std::string data = GetSaveData();
    if (data.empty())
        return;

Ni l etat des boss ni le masque des rencontres accomplies n atteignaient
la base. Constate directement : completedEncounters = 0 et data = '' sur
l instance 3 apres un boss tue.

Second defaut cumule : Save() n ecrivait pas l en-tete « S S B » que
Load() exige pour accepter la chaine. Meme appelee, la relecture aurait
echoue -- la progression aurait ete perdue a chaque rechargement.

GetSaveData() construit desormais la chaine lui-meme, en-tete compris
(motif habituel de TrinityCore), ce qui supprime le tampon et Save().
SetBossState declenche aussi une sauvegarde : auparavant seule la mort
d un hozen en provoquait une, via SetData.

Verifie au passage, et ecarte : les 7 lignes instance_encounters ajoutees
sont bien chargees (389 -> 396 au demarrage), le drapeau DUNGEON_BOSS est
pose dynamiquement au chargement et IsDungeonBoss le relit correctement,
et DifficultyID 0 est bien etendu a toutes les difficultes de la carte.

A noter : Yan-Zhu the Uncasked (59479) n a aucun spawn sur la carte 961.
Uncle Gao (59074) est present, donc l invocation par evenement reste
plausible -- a verifier quand le donjon sera jouable jusque-la.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:21:31 +02:00
SylvaniaCore deploy b0174ab55f Brasserie : ligne de vue refusee a bout portant dans l arene d Ook-Ook
Signale en jeu : « Paume du tigre » et les autres attaques de melee
refusees sur Ook-Ook avec « cible hors du champ de vision ». Meme defaut
qu au Temple du Serpent de jade juste au-dessus.

MESURE PAR SONDE. Sur 17 echecs releves, 13 sont LEGITIMES : Ook-Ook
depuis son perchoir (Z ~161,8, sol a 162,63) vers un joueur dans l arene
(Z ~147), a 23-26 m -- il y a un plancher entre les deux.

Les 2 autres ne le sont pas :
  Ook-Ook (-756,26 1353,21 146,97) -> joueur a 4,38 m
  joueur  (-757,19 1356,00 147,06) -> Ook-Ook a 2,93 m
Les deux extremites reposent sur le sol de l arene, a la meme altitude
que le plancher sous leurs pieds, et l echec se produit DANS LES DEUX
SENS. A trois metres sur un sol plat, une ligne de vue ne peut pas
echouer legitimement.

BORNES MESUREES. Tranche d altitude etroite (145-149) : le rez-de-
chaussee est un plan unique (Z 146,6 a 147,5 sur 189 spawns), ce qui
exclut le perchoir et preserve les 13 echecs legitimes. Rayon de 15 m
autour du centre d arene : la repartition des spawns donne 6 creatures a
moins de 10 m, AUCUNE entre 10 et 20 m, puis 46 au-dela -- ce vide est le
mur de l arene. On s arrete avant les salles voisines pour ne pas laisser
les monstres se voir a travers les cloisons.

RESERVE ASSUMEE : deux echecs mesures seulement, la ou le correctif du
Temple s appuyait sur trente-neuf. Bornes volontairement serrees.

Egalement dans ce lot :
- PV du hozen ivre bornes a 1 minimum. La sonde a montre que le calcul
  etait sain (pvMax=285568 -> 28556) : mon hypothese du zero etait
  FAUSSE. Le garde reste, mais le cadavre qui frappe n est pas explique.
- Sonde CADAVREDBG conservee : elle journalise toute creature infligeant
  des degats alors qu elle est morte ou a zero PV. Elle attrapera le cas
  tout seule s il se represente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 22:39:45 +02:00
SylvaniaCore deploy e657f4fb96 Brasserie : Ook-Ook ne descendait jamais, tonneaux encastres, barre decalee
Trois defauts distincts, tous confirmes par la sonde OOKDBG et par les
donnees de spawn.

1. OOK-OOK NE VENAIT PAS DANS L ARENE
La sonde montre que le chemin scripte fonctionne : « SEUIL ATTEINT »,
« Ook-Ook trouve, appel DoAction(0) », puis StartIntro. Le defaut etait
en aval, et double.
 a) L appel passait CINQ arguments -- MoveJump(x, y, z, 25.0f, 25.0f) --
    a une surcharge (x, y, z, o, speedXY, speedZ). Le premier 25.0f
    atterrissait sur l ORIENTATION. On passe par la surcharge prenant
    une Position.
 b) Il passait en reaction AGRESSIVE juste avant de sauter. Les joueurs
    sont 16 m plus bas, donc a portee : il acquerait une cible et le
    mouvement de poursuite ecrasait le saut. N ayant aucun chemin pour
    descendre, il restait plante sur son perchoir -- ce qui explique
    aussi qu il ait ete attaquable trop tot. Il reste passif pendant le
    saut et devient agressif a l atterrissage (MovementInform, en
    EFFECT_MOTION_TYPE, que le filtre existant ecartait).
Le domicile est fixe avant le saut, sinon l evade le ramene en haut.

2. TONNEAUX ENCASTRES DANS LE DECOR (capture a l appui)
DoCastBarrel figeait l altitude a 146.79, soit le sol de l arene
(ookJumpPos = 146.92). Or les Hurleurs se tiennent entre 155,6 et 161,5 :
le tonneau etait depose 9 a 15 m SOUS son lanceur, a la verticale du
balcon mais a l altitude du plancher. On interroge desormais le sol reel
sous le point vise, et le tonneau suit le sol pendant qu il roule.

3. BARRE DE BANANES BLOQUEE A 39
Le test portait sur la valeur APRES incrementation (« + 1 < 40 ») : la
barre n atteignait jamais 40/40. Signale comme « Ook-Ook apparait avant
40/40 » -- il apparaissait au bon moment, c est l affichage qui etait en
retard d une unite. Le compteur d instance, lui, etait monte a 94.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 22:28:23 +02:00
SylvaniaCore deploy 8e9b65291f Brasserie : roulement des tonneaux, et sonde sur le reveil d Ook-Ook
Signale en jeu : « les tonneaux sont completement bugges, ils se
deplacent bizarrement ».

Deux defauts certains dans npc_barrel :

1. Le trajet etait reinitialise en plein vol. Move() relancait un
   MovePoint vers un point a 5 m devant TOUTES LES 300 ms, alors que le
   tonneau avance a 0,85 de vitesse (environ 5,95 m/s) : parcourir ces
   5 m lui demande 840 ms. Le trajet etait donc ecrase au tiers, quatre
   fois par segment. Cote client chaque reinitialisation est une rupture
   de trajectoire, d ou le roulement saccade. Le deplacement est
   desormais relance A L ARRIVEE via MovementInform, par segments de
   20 m.

2. L orientation de depart n etait jamais fixee. Le tonneau est invoque
   par un sort a 5-10 m devant le Hurleur, mais rien ne lui donnait de
   cap propre. Il part maintenant dans la direction du lancer.

Arbitrage assume : la generation de chemin de MovePoint est CONSERVEE.
Un tonneau devrait rouler droit et la couper serait plus juste sur le
papier, mais c est elle qui le maintient sur le sol praticable -- sans
elle il traverserait les murs. Sur sol degage le chemin est de toute
facon quasi rectiligne.

Sonde OOKDBG ajoutee (4 points) : Ook-Ook se reveille avant le seuil de
40 hozens alors qu InitializeAI le pose en NON_ATTACKABLE et en reaction
passive, et que le chemin scripte (SEUIL ATTEINT) n apparait jamais dans
les traces. Pistes deja ecartees : les evenements 2 et 3 de l instance
ne le concernent pas, et EVENT_INTROCHECK est programme mais traite
nulle part -- evenement mort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 12:21:14 +02:00
SylvaniaCore deploy 8200eb3961 Serpent de jade : degats de l eau proportionnels a l effectif, sondes retirees
Le sort 106778 inflige 950 points fixes (EffectBasePoints, DB2 du build
7.3.5.26972 -- valeur confirmee par la sonde : exactement 950 par tic,
sans mitigation). A quatre tics par seconde cela fait 3800 par seconde,
un chiffre calibre pour cinq joueurs.

Or ce sont des degats BRUTS : ni l armure ni les statistiques ne les
reduisent, donc Solocraft n y peut rien (verifie, il s applique bien :
+400 % de statistiques, 61 000 PV au lieu de 12 299). Un joueur seul
encaissait la charge prevue pour cinq.

La valeur est desormais multipliee par l effectif present et divisee par
les cinq joueurs pour lesquels le contenu est calibre -- le meme rapport
que celui de Solocraft. Un joueur seul prend un cinquieme. Les maitres
de jeu ne comptent pas. La valeur de base est relue dans le sort plutot
que recopiee.

La cartographie du sol par la sonde (411 releves) a confirme que le
combat est jouable tel quel : bassin central jusqu a 5 m, ANNEAU SEC de
5 a 8 m -- le « inner most circle of dry zone » de la consigne
officielle --, rigole de 8 a 10 m, dallage sec au-dela. Le corps a corps
se fait depuis l anneau interieur.

Sondes TJSWATER et TJSDBG retirees. Celles de la Brasserie (SSBDBG)
restent en place, le blocage d Ook-Ook n est toujours pas elucide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 12:01:25 +02:00
SylvaniaCore deploy b03d31d161 Sagesse de Mari : l'eau brûlait partout et rendait le combat infaisable
Signalé en jeu par deux joueurs. Une sonde posée dans la boucle de dégâts
a montré que le joueur n'occupe que deux altitudes dans la salle :
174,698 sur la coursive sèche (103 relevés sur 124) et 174,157 dans la
cuvette centrale (12 relevés), à 3,1-4,5 m du centre — c'est-à-dire au
corps à corps.

Toute la cuvette infligeait 950 points toutes les 250 ms, soit 3 800 par
seconde, dès l'entrée en combat et sans aucune condition. Or la Sagesse
de Mari se tient dedans (174,24) : l'approcher revenait à se suicider.
Solocraft n'y peut rien, ces dégâts ignorent les statistiques — vérifié,
il s'applique correctement (+400 % de statistiques, 61 000 PV).

La sonde a aussi écarté deux fausses pistes : les dégâts ne venaient pas
de « Lessiver » (68 des 70 relevés portaient le sort 106778, lancé par le
joueur sur lui-même à zéro mètre), et les bandes d'altitude ne sont pas
mal calibrées — elles distinguent correctement la cuvette de la coursive.

CE QUE FAIT LE JEU OFFICIEL
L'infobulle du vrai sort (115167) dit : « les eaux corrompues DE LA
FONTAINE infligent des dégâts de Givre ». L'eau n'est pas dangereuse en
soi : elle le devient fontaine par fontaine, pendant que le boss les
souille l'une après l'autre — une toutes les 29 s dans son propre script.
D'où la consigne connue du combat : tourner le long des zones sèches
jusqu'à ce que les quatre Ondes vivantes soient mortes, puis se placer
sur l'anneau sec pour la phase du jet.

CORRECTIF
Un joueur dans l'eau ne prend des dégâts que s'il est à portée d'une
fontaine DÉJÀ corrompue (aura 106518, posée par le script du boss) :
  - aucune fontaine souillée -> l'eau est inoffensive ;
  - chaque fontaine corrompue noie la moitié de cuvette la plus proche
    d'elle, les zones sèches se réduisent ;
  - les quatre corrompues -> toute la cuvette est mortelle, et c'est le
    moment où la phase 2 commence.

La portée de 30 m est le rayon du sort officiel 115167 (SpellRadius
ligne 10, build 7.3.5.26972), pas un chiffre choisi au jugé. Les
fontaines étant à 24,7-29,2 m du centre et la cuvette faisant une
dizaine de mètres de rayon, il découpe naturellement la cuvette en deux
par fontaine.

Reset() du boss purge les auras des fontaines : une mort de groupe
remet bien le compteur à zéro.

RESTE À FAIRE
- La sonde TJSWATER est conservée le temps de la validation en jeu ;
  elle sera retirée ensuite.
- Nous lançons toujours 106778 « Corrupted Waters NOT READY », le
  brouillon inachevé de Blizzard, au lieu de 115167. Dégâts comparables
  (5 000 contre 4 749 en normal) mais sans les paliers héroïque et
  mythique. Non basculé ici : 115167 utilise un ciblage par destination
  qui demande d'être traité à part.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 23:23:13 +02:00
SylvaniaCore deploy 39171026a7 Sagesse de Mari : « Lessiver » frappait aussi dans le dos du boss
Signalé en jeu : le jet d'eau de la phase 2 infligeait des dégâts aux
joueurs placés derrière le boss, « comme s'il y avait des dégâts de zone
autour ».

Le sort est en réalité une chaîne de trois. 106329 canalise — et son
effet 1 est un « script côté serveur », c'est-à-dire une logique Blizzard
jamais distribuée avec les données du client. 106331 est l'aura posée sur
le boss, qui déclenche 106334 toutes les 250 ms. C'est 106334 qui blesse :
école Givre, recul 200, rayon 60 m, drapeau « ignore la ligne de vue ».

Toute la forme du jet tenait donc à une seule valeur, et à une entrée DB2
facultative :

    ConeAngle = _target ? _target->ConeDegrees : 0.f;

Le mode de contrôle associé au ciblage 110, TARGET_CHECK_ENTRY, ne filtre
par ailleurs — sans `conditions` — ni l'hostilité ni la position.

Valeurs officielles relevées dans les DB2 du build 7.3.5.26972 :
    SpellTargetRestrictions ligne 7514 : ConeDegrees = 12
    SpellRadius             ligne 48   : Radius      = 60
soit un cône frontal de 12° sur 60 mètres.

Le script `spell_wise_mari_wash_away` réimpose ces 12° en dur plutôt que
de dépendre d'une donnée invérifiable côté serveur (la table spell_effect
de dc_hotfixes est vide, ces données vivent dans les fichiers DB2), et
rétablit le contrôle d'hostilité laissé de côté par TARGET_CHECK_ENTRY.

Le boss pivote de PI/48 toutes les 300 ms, soit 3,75° : avec un jet de
12°, un joueur immobile est balayé environ une seconde par tour de salle.
C'est le comportement d'origine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 22:44:32 +02:00
SylvaniaCore deploy 673bad73b3 Sagesse de Mari : « Lessiver » ne balayait pas, le parametre force manquait
Signale en jeu apres le correctif de ligne de vue : le sort restait fige
au lieu de balayer la salle.

Unit::SetFacingTo abandonne en silence quand la creature nest pas a
larret :
    if (!force && !IsStopped()) return;
Or la phase 2 lance MovePoint vers la position de depart juste avant :
elle EST en deplacement, la rotation ne partait donc jamais. Et le
SetOrientation qui precede ne met a jour que la valeur serveur, sans
prevenir les clients.

Seuls les deux appels de la phase 2 recoivent force = true. Le troisieme,
celui de lHydrolance en phase 1, est laisse tel quel : elle est a larret
a ce moment-la et il fonctionne.

Sondes retirees : la sonde a montre que la mecanique du combat est saine
— quatre fontaines trouvees, un elementaire invoque a chaque appel
(compteur 0 puis 1, 2, 3) et bouclier retire a foutainCount=4. Le seul
defaut etait la ligne de vue, corrigee au commit precedent. Le blocage
signale par le joueur — « les elementaires sont coinces, on ne peut pas
les buter, du coup le boss garde son bouclier » — sexplique entierement
par la : il ne pouvait frapper personne.
2026-08-24 12:08:45 +02:00
SylvaniaCore deploy c880268e8c Temple du Serpent de jade : aucun sort a distance ne passait dans la salle du bassin
Signale par « le baltring » puis reproduit : aucun sort a distance
naboutit sur aucun PNJ de la Fontaine des Temoins, et la Sagesse de Mari
etait annoncee « hors de portee de vue » alors quelle est visible a
7 metres.

Fausses pistes ecartees avant de trouver, toutes verifiees : les drapeaux
(32832 est exactement celui du Sha du doute, jouable), un objet du monde
qui bloquerait (aucun a moins de 15 m), des tuiles de collision
manquantes (0960_36_30.vmtile fait 82 Ko, la plus grosse de la carte),
une position aberrante, et le phasage.

Mesure faite par une sonde posee dans IsWithinLOS. Sur 39 echecs :
tous tiennent dans une boite de 26 x 18 m — la salle et rien quelle, le
reste du donjon est indemne ; la tranche daltitude est etroite, 172,6 a
174,3 ; les distances vont de 4 a 18 m, donc meme a bout portant ca
bloque ; et la Sagesse de Mari elle-meme echoue a viser les joueurs.
Il y a donc de la matiere de collision A LINTERIEUR de la piece, dans la
tranche ou tout le monde se tient : lextraction du modele est fautive,
pas un fichier manquant.

Deplacer les creatures ne servirait a rien, le joueur aussi est dans la
zone fautive. La ligne de vue est donc neutralisee, mais UNIQUEMENT
quand les DEUX extremites du rayon sont dans le bassin. Hors de cette
boite, et sur toute autre carte, le calcul reste strictement inchange.

A retirer si les cartes de collision de la carte 960 sont un jour
re-extraites correctement.
2026-08-24 11:53:45 +02:00
SylvaniaCore deploy bfb9fe864b Plantage : m_playerStorage netait alloue nulle part
Le serveur est tombe le 24/08/2026 dans le Temple du Serpent de jade,
SIGSEGV. Pile du vidage memoire, sans ambiguite :
  PlayerStorage::IsEntryExists
  <- spell_monk_mastery_combo_strikes::HandleHit
  <- Spell::HandleEffects <- HandleCastSpellOpcode
Larret est a IsEntryExists+4, cest-a-dire au tout premier acces a this.

m_playerStorage est DECLARE dans Player.h et lu par GetStorage(), mais
alloue NULLE PART dans tout le depot : ni liste dinitialisation, ni
corps du constructeur, aucune affectation. Le pointeur contenait donc
des ordures des la construction du joueur.

Quatre scripts du Moine le deferencent sans controle dans spell_monk.cpp
— maitrise « Frappes combinees », Poings de furie. Le premier moine a en
declencher un faisait tomber le worldserver. Ce netait quune question de
temps : la classe est utilisee depuis toujours sans jamais avoir existe.

La classe PlayerStorage est parfaitement fonctionnelle, il ne manquait
que son allocation. Elle est desormais creee dans le constructeur et
liberee dans le destructeur : le plantage disparait ET la maitrise du
Moine devient operante.

Ceinture et bretelles : les quatre appels recoivent en plus une garde de
nullite, pour quun oubli similaire ne soit plus jamais fatal.
2026-08-24 11:17:57 +02:00
SylvaniaCore deploy 82eb6e808e Nouveau type de condition : specialisation du joueur (53)
« Arretez Guldan ! » etait proposee en double par Maiev. Les quetes 38723
et 40253 partagent titre, prerequis, tri, et sont ouvertes a toutes les
races : rien ne les departageait. Ce ne sont pas des variantes
Horde/Alliance malgre le nom des constantes QUEST_STOP_GULDAN_H/_A — le
meme fichier les nomme correctement plus bas DMG_SPEC et TANK_SPEC. Ce
sont des jumelles de specialisation.

Le gestionnaire de conditions navait aucun moyen de tester la
specialisation. Le contournement par un sort caracteristique netait pas
tenable : les sorts de specialisation ne sont quasiment pas enseignes
sur ce serveur, les trois Chasseurs de demons existants ne connaissent
que Morsure de demon et celui de niveau 98 na pas sa Metamorphose. Une
condition batie dessus aurait masque les DEUX quetes.

CONDITION_SPECIALIZATION lit Player::GetPrimarySpecialization() et
valide la valeur contre ChrSpecialization.db2 au chargement.

Formulation retenue en base : la quete Vengeance exige la spe Vengeance,
la quete Devastation exige de NE PAS etre Vengeance. Il y a ainsi
toujours exactement une quete proposee, meme si la specialisation vaut
zero — un doublon agace, zero quete bloque.

Reutilisable : le serveur compte 1488 paires de quetes homonymes
partageant prerequis et races.
2026-08-22 14:47:59 +02:00
SylvaniaCore deploy cf21fde938 Glaives 38669 : les credits se declenchaient sur un evenement qui narrive jamais
Signale en jeu : le joueur arrive au Caveau des Gardiennes avec ses
glaives DEJA EQUIPES. La quete « Notre dernier espoir » lui demande de
les equiper, mais levenement dequipement a eu lieu avant quil ne prenne
la quete. Ne guetter que OnItemLevelChange laissait donc la quete
bloquee dans le cas le plus courant.

Le script verifie desormais a trois moments : equipement dun glaive,
acceptation de la quete, et connexion. Le dernier rattrape les
personnages deja bloques — une simple reconnexion suffit.
2026-08-22 13:11:37 +02:00
SylvaniaCore deploy 1474d24cc1 Zone DH : six quetes debloquees et butin realigne sur Wowhead
Audit complet des 42 quetes de Mardum (1481) et du Caveau des Gardiennes
(1468) apres une traversee de la campagne ou les blocages avaient ete
franchis a la commande GM. Six quetes restaient impossibles a terminer
normalement.

Quetes sans aucun donneur
  38668 / 38669 « Notre dernier espoir » et 38689 « Infusion gangrenee »
  n'avaient aucune ligne creature_queststarter. Attribuees a leur
  recepteur deja en place (Maiev 92718, Altruis 92986), tous deux deja
  porteurs du drapeau de donneur. L'ordre officiel de la chaine est
  retabli via PrevQuestID (38668 -> 38669 -> 38672 -> 38689 -> 38690 ->
  38723/40253), ce qui corrige au passage le piege signale en jeu :
  accepter « La gangr'evasion » faisait partir Maiev avant que les
  autres quetes aient pu etre prises.

Objectifs obligatoires sans aucune source
  38690 : les huit « Warden Cell » (244588) etaient posees mais inertes
          — aucun AIName, aucun script, tous les Data a zero. SmartAI
          ajoute sur le motif de la « Scourge Cage » 187854.
  38672 : les cellules 103655/103658 avaient tout leur SmartAI sauf
          l'action 33. Le script C++ prevu n'aurait rien regle : non
          rattache, lignes de credit commentees, et entrees erronees.
  38723/40253 : le credit « Face Gul'dan » 99303 dependait d'une scene
          absente de scene_template. Ajoute a la liste d'actions de
          Maiev, qui reproduit deja la scene.
  38689 : les quatre demons du caveau accordent desormais l'energie
          gangrenee, dix par mort, comme le prevoyait npc_fel_infusion.
  38765 : go_mardum_portal_shivarra appelait ForceCompleteQuest sous la
          condition inverse de ses deux jumeaux. Remplace par les credits
          94407 et 97831, sur le schema des portails Cendrelangue et
          Glissentaille.
  38669 : nouveau PlayerScript accordant les credits des glaives via
          OnItemLevelChange. Aucun identifiant d'objet code en dur : on
          verifie la sous-classe WARGLAIVES dans chaque emplacement.

Butin
  Compare aux tables officielles de Wowhead pour les 17 creatures
  tuables de la zone. Le menu fretin de Mardum ne lache officiellement
  rien : lootid = 0 etait correct, rien n'a ete touche. Manquaient en
  revanche 132753 « Rations de la Legion » (absente des 46 000 lignes de
  butin du serveur), 129196 sur quatre creatures, 147430, 132138, et
  sept des huit lignes de l'Inquisiteur Funeste. Les objets
  post-Legion que Wowhead attribue a ces creatures sont ecartes, et les
  taux existants ne sont pas modifies.
2026-08-22 12:23:11 +02:00
SylvaniaCore deploy adaaa7d232 Bastillax : sorts lances en mode declenche, boss intuable en solo
Signale en jeu : boss juge trop puissant. Ses statistiques sont pourtant
authentiques - modificateur de vie 14, rang elite, VerifiedBuild 25549 -
et identiques a celles de Sledge, Crusher et Tyranna deja vaincus.

Le probleme venait du script : les trois sorts etaient lances avec
triggered = true, donc instantanes et non interruptibles. Or ils ont tous un
temps dincantation cote client :
  200007 Annihilation gangrenee : 3 s, cone frontal   -> il faut en sortir
  200027 Ombres ecrasantes      : canalise 5 s        -> interruptible
  200002 Effacement tenebreux   : 2 s, +100% desquive -> interruptible

Toute la parade disparaissait. Surtout, le bouclier desquive a 100% montait
instantanement toutes les 6 s : le boss esquivait la quasi-totalite des
coups. Ce netait pas un boss surpuissant mais un boss intouchable.

Passage en incantation normale sur les trois. Comportement authentique
retabli, aucune statistique modifiee.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 11:22:34 +02:00
SylvaniaCore deploy d10873b2b2 Ecran de chargement fige : le serveur ne repondait pas aux tables DB2 inconnues
Preuve dans Server.log, exactement une fois par tentative de connexion
bloquee :
  CMSG_DB_QUERY_BULK: [Daemon] requested unsupported unknown hotfix type:
  3386291891

HandleDBQueryBulk se contentait de journaliser puis de RETURN sans envoyer
la moindre reponse. Le client, qui attend une reponse par enregistrement
demande, restait bloque indefiniment a lecran de chargement.

Corrige en emettant une reponse negative (Allow a false) pour chaque
enregistrement demande, comme le fait deja la branche enregistrement
introuvable juste en dessous.

Portee generale : nimporte quel client reclamant une table que ce core ne
connait pas restait bloque, quelle que soit la carte. La table en cause ici
etait 0xC9D6B6B3, absente des metadonnees du core.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 22:56:32 +02:00
SylvaniaCore deploy 95913f26d2 Choix du compagnon : la fermeture du dialogue effacait le choix autorise
Preuve dans Server.log : "tried to respond to invalid player choice 234
(allowed 0)". Le clic atteignait bien le serveur, mais celui-ci avait
memorise 0 au lieu de 234.

Enchainement fautif dans npc_korvas_bloodthorn::OnGossipSelect :
  1. CastSpell(196650) -> EffectLaunchQuestChoice -> SendPlayerChoice(234)
     inscrit PlayerChoiceId = 234
  2. CloseGossipMenuFor() -> SendCloseGossip() -> _interactionData.Reset()
     remet PlayerChoiceId a 0
La fenetre saffichait (le paquet etait deja parti) mais le serveur avait
oublie quel choix il autorisait. Cest pour cette raison que le tome de
Mardum fonctionne : lui ne ferme pas le dialogue.

Corrige en fermant AVANT de lancer le sort. Balayage de tout le core :
34 occurrences du motif CastSpell puis CloseGossipMenuFor, mais une seule
autre concerne un sort qui affiche un choix (class_hall_hunter.cpp), les
autres lancent des sorts ordinaires ou reinitialiser est sans effet.
Les deux sont corrigees.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 22:42:40 +02:00
SylvaniaCore deploy 69c9781828 Quatre hooks morts OnCompleteQuestChoice + quete 40373 injouable
Signale en jeu : choisir entre Kayn et Altruis ne declenche rien.

1) HOOK MORT, 4 occurrences. OnCompleteQuestChoice est declare dans
ScriptMgr mais appele DE NULLE PART ; le seul hook invoque a la reception
dun choix est OnPlayerChoiceResponse (QuestHandler.cpp). Corrige dans :
  zone_vault_of_wardens.cpp      choix Kayn / Altruis
  zone_legion_dalaran_legion.cpp Dalaran
  class_hall_dh.cpp              fief chasseur de demons
  class_hall_hunter.cpp          fief chasseur
Meme defaut que celui corrige a Mardum (commit 81b37487). Dautres fichiers
(class_hall_monk, class_hall_artifact_choices) utilisaient deja le bon nom,
ce qui confirme le diagnostic.

2) CREDIT MANQUANT. Le handler ne lancait quun sort : lobjectif 99278
(choisir entre Kayn et Altruis) de la quete 40373 netait accorde par rien.

3) QUETE SANS DONNEUR. 40373 a un recepteur et deux objectifs coherents
mais aucune ligne creature_queststarter. Attribuee a Korvas 97644 : elle en
est deja la receptrice, cest son propre script qui ouvre la fenetre de
choix, elle recoit aussi la quete precedente 39686, et elle porte npcflag=3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-21 22:28:59 +02:00
SylvaniaCore deploy 01cbf6b3ca Mardum : le tome des secrets gangrenes nest plus recliquable
Le livre relancait le choix de specialisation sans aucune condition : le
joueur pouvait le rouvrir et reprendre lautre spe a volonte. Chaque passage
relance LearnSpell + ActivateTalentGroup et reaccorde le credit de quete.

Consequence observee le 18/08/2026 sur un personnage de test :
primarySpecialization restee a 577 (Devastation) alors quil portait larme
de Vengeance et avait recu la variante Vengeance de Cry Havoc.

Deux gardes : le livre ne souvre plus une fois la quete 40051 validee, et
le gestionnaire de reponse refuse de sappliquer deux fois quelle que soit
la facon dont la fenetre serait rouverte.

NOTE : ces 16 lignes etaient deja EN SERVICE depuis hier - embarquees par
les compilations suivantes - mais jamais commitees. Elles le sont enfin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 11:46:27 +02:00
SylvaniaCore deploy 193d36dcbc Sage Mari : ecriture hors limites sur le tableau des fontaines
Signale en jeu : premier boss du Temple du serpent de jade infaisable, le
bouclier du boss ne tombe jamais.

Le tableau foutainTrigger a 4 cases (indices 0-3), mais les deux acces
utilisaient la PRE-incrementation :
  ecriture : foutainTrigger[++tab]          -> indices 1,2,3,4
  lecture  : foutainTrigger[++foutainCount] -> indices 1,2,3,4
Lindice 0 netait jamais rempli, et lindice 4 ecrivait 16 octets hors
limites - juste par-dessus hydrolancePhase, declare immediatement apres le
tableau. La salle contenant exactement 4 Fountain Stalker (56586), le
debordement etait systematique a chaque pull.

Corrige en post-incrementation des deux cotes, avec garde de borne a
lecriture. La condition de fin de phase (foutainCount == 4) reste juste.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 22:33:55 +02:00
SylvaniaCore deploy 0ed9cfa20d Mercenaires : le mercenaire sort du portail, et sait enfin qui il est
Quatre corrections nees d une soiree d essais en jeu.

Le mercenaire etait teleporte sur son employeur et non sur la structure qui
l invoque : en reculant de quelques pas avant de payer, on le voyait apparaitre
loin du portail. Le contrat retient desormais la position de la structure ; le
mercenaire en sort a un ou trois metres, l angle tire au hasard pour que quatre
recrues ne s empilent pas, face au portail qu il vient de franchir, la hauteur
recalee sur le terrain. BotGroupAI expose pour cela un teleport vers un point
precis - un bot n a pas de client pour accuser reception, seul BotAITeleport
simule cet echange.

Il jurait ensuite selon sa faction : un demoniste de l Alliance invoquait donc la
Lumiere, qui est precisement ce qui le brule. Les serments viennent maintenant de
la CLASSE et l origine de la RACE. Nuance importante : demonistes et chevaliers
de la mort ne se reclament jamais de la Lumiere, mais peuvent la maudire et s en
moquer - c est leur role.

Enfin, les refus passagers des fournisseurs (503, 429) sont retentes deux fois
avant d abandonner, dans le fil dedie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 11:04:30 +02:00
SylvaniaCore deploy a3055a32f6 Mercenaires : le mercenaire connait son monde, et sait ou il se tient
Il tenait son role mais parlait dans le vide. Trois ajouts.

Un socle sur Azeroth a l heure de la Legion : les Iles Brisees, Dalaran qui
flotte, les armes prodigieuses, Sargeras, le retour d Illidan, les continents,
les capitales des deux factions, la monnaie. Avec une contrainte qui compte
autant que le reste : il n a JAMAIS entendu parler de ce qui vient apres -
Kul Tiras, l Ombreterre, les Dragons. Un modele entraine sur tout le corpus de
Warcraft racontait sinon des evenements qui n existent pas sur ce royaume.

Le lieu reel ensuite : la zone et la sous-zone sont lues dans les tables du
client (AreaName->Str[locale]) et injectees a chaque replique. Le mercenaire
peut enfin rechigner sur le froid d une region qu il traverse vraiment.

Enfin, la relance des refus passagers. Les paliers gratuits repondent
regulierement 503 ou 429 ; renoncer au premier essai laissait le mercenaire muet
pour une gene d une seconde. Trois tentatives espacees, dans le fil dedie - la
boucle du monde n en sait rien. Un refus definitif, lui, n est jamais retente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 10:41:15 +02:00
SylvaniaCore deploy bf58c59f3d Chat : la meme faille SQL existait dans le chat de groupe des bots
Le correctif precedent ne visait que le chuchotement, celui que la pile d appel
designait. La requete du canal de groupe (ai_talk_group) portait le meme defaut,
dans une boucle sur les membres qui plus est : une requete par bot present.

Le royaume est retombe cinq minutes apres le premier correctif, sur le meme mot -
« c est » - avec la meme erreur 1064. Lecon : chercher toutes les occurrences du
motif, pas seulement celle que le crash montre.

Retrait au passage de la trace de diagnostic temporaire : le message du
fournisseur, remonte au joueur en jeu, suffit desormais.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:13:33 +02:00
SylvaniaCore deploy 9b4851678f Chat : un simple apostrophe dans un chuchotement a un bot faisait tomber le royaume
Quand un joueur chuchote a un playerbot, le core cherche une reponse toute faite
dans ai_talk_whisper et injectait le message BRUT dans la requete :

    WHERE '%s' REGEXP cname

Une apostrophe - « c est », « j ai », une phrase de francais sur deux - cassait
la requete. MySQLConnection::_HandleMySQLErrno repond a une erreur SQL par un
abandon du processus : n importe quel joueur faisait donc tomber le serveur en
adressant la parole a un bot, et pouvait y injecter du SQL au passage.

Trois crashs SIGSEGV en une soiree (23h06, 23h44, 23h58), tous avec la meme pile
HandleChatMessage -> DatabaseWorkerPool::Query -> _HandleMySQLErrno -> Abort. Le
defaut preexistait, mais le module de dialogue l a reveille : jusqu ici personne
n adressait la parole aux playerbots.

Le message est desormais echappe et borne a 255 caracteres.

Second changement, sans rapport : Mistral devient un fournisseur a part entiere.
Il parlait deja le dialecte d OpenAI, mais « mistral » n etait qu un alias : sans
adresse explicite, la cle du joueur partait chez OpenAI, qui la rejetait par un
401 incomprehensible. Il a maintenant son adresse par defaut, et l aide en jeu
signale que Gemini ne repond pas depuis un serveur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:03:58 +02:00
SylvaniaCore deploy 77c5c68e76 Mercenaires : dialogue par modele de langage, cle fournie par le joueur
Le mercenaire parle desormais avec le modele de langage de son employeur, paye
par la cle d API de celui-ci. Inspire de mod-ollama-chat (AzerothCore), mais
avec trois exigences qui changent l architecture : cle apportee par le joueur,
plusieurs fournisseurs, aucune persistance.

Trois adaptateurs natifs : OpenAI (et tout service parlant son dialecte, via une
adresse personnalisee : Mistral, Groq, OpenRouter, Ollama, LM Studio), Anthropic
et Gemini. Le joueur fournit sa cle en jeu par « !api <fournisseur> <modele>
<cle> », la retire par « !api off ».

La cle n atteint jamais un disque. Elle est interceptee des la premiere
instruction de HandleChatMessage, avant tout journal et toute rediffusion ;
elle vit en memoire, est ecrasee caractere par caractere avant liberation - un
simple clear() laisserait le secret lisible dans le processus - et disparait a
la rupture du dernier contrat, a la deconnexion et a l arret du royaume.

Aucun appel reseau ne part du fil du monde : un fil dedie consomme une file
d envoi, les reponses reviennent par une file relue au tick. Un appel prend une
a dix secondes ; le faire dans la boucle du monde figerait le royaume a chaque
replique.

Le mercenaire repond dans le canal ou on lui parle. Le chuchotement reste
l affaire de l employeur ; dans le groupe et a voix haute, tout compagnon peut
l interpeller, mais un seul mercenaire repond - celui qu on nomme, sinon le
premier sous contrat - et c est toujours la cle de l employeur qui paie.

Contrepartie indispensable : les ordres passent au prefixe « ! » (!follow,
!stop, !@heal stop). Sans cela, le mot « stop » au detour d une phrase aurait
fige le mercenaire.

Deux obstacles contournes : le rapidjson embarque ne compile plus avec GCC 14
et le core ne s en sert que par son lecteur evenementiel - un extracteur JSON
cible le remplace, echappements et sequences Unicode compris ; libcurl est lie
directement plutot que d activer WITH_CPR, qui recompilerait curl et cpr en
entier pour le meme service.

Inclut un correctif sans rapport decouvert en chemin : une recrue du siege des
capitales abandonnee en depassement de delai etait rayee des registres sans
etre deconnectee, et chaque assaut laissait ainsi quelques bots vagabonder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 23:08:07 +02:00
SylvaniaCore deploy 1e1a7d8957 BG BotFill : vague de sortie de cimetiere apres resurrection
Doctrine PvP : repartir seul de son cimetiere revient a nourrir l adversaire
un par un. Les bots ressuscites repartaient isolement et se faisaient cueillir
en chemin. Un bot patiente desormais au cimetiere jusqu a ce que trois allies
soient a portee, au maximum huit secondes. Il repart immediatement s il est
attaque, si un ennemi pousse jusqu au cimetiere, ou s il porte un drapeau.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:59:50 +02:00
SylvaniaCore deploy 537e6df7cd BG BotFill : laisse tactique autour de l objectif + escorte du porteur
1. Laisse : un bot ne se laisse plus entrainer loin de son objectif par un
   fuyard. Au-dela de 50 m du point a tenir la cible est fortement
   deprioritisee, sauf le porteur de drapeau qu il faut poursuivre partout.
   Sans cette regle les defenseurs desertaient leur base des le premier
   ennemi croise, defaut classique des IA de champ de bataille.
2. Escorte a Chanteguerres : jusqu a trois allies deja proches (60 m) collent
   le porteur pour intercepter ses poursuivants. L Oeil du cyclone avait deja
   ce comportement, Chanteguerres laissait le porteur rentrer seul.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:46:16 +02:00
SylvaniaCore deploy 7987f07d48 Tyranna : fiabilise le credit trouvez le chemin vers le bas (quete 38728)
Complement de c25c1305. Le credit 101760 netait accorde que dans
DamageTaken, au coup fatal, aux seuls joueurs figurant a cet instant dans
la liste de menace de Tyranna. Dans un combat ou quatre PNJ compagnons
frappent aux cotes du joueur, cette conjonction rate - constate en jeu :
le joueur tuait bien Tyranna, ramassait la Cle de voute (qui ne tombe que
sur son cadavre), et restait bloque sur ce seul objectif.

La cause exacte de lechec na pas pu etre determinee apres coup : trois
conditions doivent etre reunies simultanement (instance de degats letale,
liste de menace non vide, quete en cours) et rien dans les donnees ne dit
laquelle a manque. Do le choix de supprimer la dependance plutot que de
la diagnostiquer.

Le credit est desormais aussi accorde dans JustDied, par proximite (100 m)
et sans condition de menace, sur le modele du script de lInquisiteur
Baleful. Lancienne voie est conservee : les deux sont idempotentes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 09:41:53 +02:00
SylvaniaCore deploy c25c130506 Quete 38728 (La Cle) : objectif trouvez le chemin vers le bas inatteignable
Signale en jeu : le joueur descend, trouve la Cle de voute sargerite,
clique dessus, rien ne se passe - et la quete reste bloquee.

Le troisieme objectif (credit 101760, Find the way downstairs) netait
accorde que par le script de Tyranna, dans DamageTaken, aux seuls joueurs
presents dans sa liste de menace au coup fatal. Deux facons detre bloque :
le script non rattache au moment du kill (cas rencontre), ou le coup fatal
porte par Kayn. Une fois en bas, plus aucun recours sur place : il faut
remonter et retuer le boss.

Lobjectif sappelle pourtant trouvez le chemin vers le bas et la cle se
trouve justement en bas. Le gameobject 245728 laccorde desormais aussi,
en plus du credit 100651 qui appartient a la quete suivante (38729).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 01:46:53 +02:00
SylvaniaCore deploy 723b200dbe Quete 39515 (A moi la vengeance) : les 5 PNJ ne reagissaient pas
Signale en jeu : cliquer sur loption de dialogue dAllari, Cyana, Kayn,
Korvas ou Mannethrel ne produisait rien.

Cry Havoc existe en DEUX versions strictement jumelles - memes cinq
objectifs, meme donneur (Kayn 93127) - une par specialisation :
  39516 Semer la devastation  (Devastation)
  39515 A moi la vengeance !  (Vengeance)

Le script ne testait que 39516, en six endroits. Pour un joueur ayant
choisi Vengeance, HasQuest renvoyait faux et chaque handler sortait
immediatement. Le script de la Vigie brisee gere pourtant bien ses propres
variantes (_HA/_HH/_VA/_VH, _H/_V, _DMG_SPEC/_TANK_SPEC) : loubli est
propre a Mardum.

Verification systematique faite : en croisant toutes les quetes codees en
dur dans les deux scripts DH avec la liste des quetes partageant une
signature dobjectifs identique, Cry Havoc est la SEULE de Mardum a avoir
une jumelle non geree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 01:25:53 +02:00
SylvaniaCore deploy 81b37487dd Quete 40051 (Secrets gangrenes) : le choix de specialisation ne validait rien
Signale en jeu : le tome ouvre bien le choix, on selectionne Devastation ou
Vengeance, mais la quete reste incomplete.

Trois defauts :
1. Le script implementait OnCompleteQuestChoice, un hook declare dans
   ScriptMgr mais appele DE NULLE PART. Le seul hook invoque par le core
   est OnPlayerChoiceResponse (QuestHandler.cpp). La methode etait donc du
   code mort - preuve mesuree en base : le joueur navait pas appris le sort
   200749 et son activeTalentGroup restait a 0.
2. Aucun credit de quete nulle part, alors que 40051 exige lentree 99071.
3. La branche Devastation nactivait pas la specialisation, contrairement a
   celle de Vengeance : asymetrie qui laissait le joueur sans spe.

AssertEntry remplace par LookupEntry avec garde, pour ne pas abattre le
serveur si un identifiant de specialisation manquait.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 01:06:50 +02:00
SylvaniaCore deploy c9f4c87e61 PlayerChoice : boutons vides (choix de specialisation chasseur de demons)
Signale en jeu : le tome des secrets gangrenes ouvre le choix de
specialisation, la question saffiche mais les deux boutons sont vides.

Trois defauts cumules dans Player::SendPlayerChoice :
1. Le filtre exigeait une recompense avec un sort VALIDE pour quune
   reponse soit envoyee. Le choix 231 (Devastation / Vengeance) a
   SpellID=0 : les deux reponses etaient silencieusement ecartees.
2. displayPlayerChoice.Responses etait redimensionne AVANT le filtrage,
   laissant une entree vide par reponse ecartee - do les boutons vides.
3. Reward->SpellID etait dereference sans controle alors que Reward peut
   etre nul, comme le montre le test plus bas dans la meme fonction :
   un plantage en attente pour toute reponse sans ligne de recompense.

Correctif general, valable pour tous les choix, pas seulement celui-ci.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 00:56:55 +02:00