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>
This commit is contained in:
SylvaniaCore deploy
2026-08-27 12:05:59 +02:00
parent 85b0251bc1
commit 7f8cdd2d4b
+33 -8
View File
@@ -192,7 +192,7 @@ bool Scenario::CanCompleteCriteriaTree(CriteriaTree const* tree)
return true; return true;
} }
void Scenario::CompletedCriteriaTree(CriteriaTree const* tree, Player* referencePlayer) void Scenario::CompletedCriteriaTree(CriteriaTree const* tree, Player* /*referencePlayer*/)
{ {
ScenarioStepEntry const* step = tree->ScenarioStep; ScenarioStepEntry const* step = tree->ScenarioStep;
if (!step) if (!step)
@@ -233,10 +233,28 @@ void Scenario::CompletedCriteriaTree(CriteriaTree const* tree, Player* reference
// la racine n'etant jamais transmise ici, l'etape ne s'acheverait // la racine n'etant jamais transmise ici, l'etape ne s'acheverait
// alors JAMAIS. // alors JAMAIS.
// //
// `CheckCompletedCriteriaTree` peut, en cas de succes, rappeler // LA VERIFICATION DOIT ETRE PUREMENT LECTRICE.
// cette fonction avec la racine : on relit donc l'etat de l'etape //
// ensuite, faute de quoi `CompleteStep` serait execute deux fois et // Une premiere version appelait `CheckCompletedCriteriaTree` sur la
// la quete de recompense octroyee en double. // racine. C'ETAIT UNE ERREUR, et elle a fait planter le serveur par
// debordement de pile (SIGSEGV, vidage de 389 Mo contre 120 Mo en
// temps normal). Enchainement :
//
// 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 (ligne 1109 de
// CriteriaHandler.cpp), 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 donc de LIRE la progression des criteres, sans
// jamais repasser par le moteur d'evaluation.
// ================================================================= // =================================================================
if (step->Criteriatreeid && step->Criteriatreeid != tree->Entry->ID) if (step->Criteriatreeid && step->Criteriatreeid != tree->Entry->ID)
{ {
@@ -244,10 +262,17 @@ void Scenario::CompletedCriteriaTree(CriteriaTree const* tree, Player* reference
if (!racine) if (!racine)
return; return;
if (!CheckCompletedCriteriaTree(racine, referencePlayer)) bool complet = true;
return; CriteriaMgr::WalkCriteriaTree(racine, [this, &complet](CriteriaTree const* noeud)
{
if (!complet || !noeud->Criteria)
return;
if (GetStepState(step) == SCENARIO_STEP_DONE) if (!IsCompletedCriteria(noeud->Criteria, noeud->Entry->Amount))
complet = false;
});
if (!complet)
return; return;
} }