From 6fe5f2a7f6be0271f61784991b06d8af6db6ec35 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Wed, 12 Oct 2016 17:19:02 +0200 Subject: [PATCH] Fix segfault / assertion failure during battle The cause of the assertion failure at BattleStackAttacked::applyGs "at" was shooting and casting some spell in handleAttackBeforeCasting() and eventually killing the whole stack. Fix: filter dead stacks in the end of handleAttackBeforeCasting(). --- server/CGameHandler.cpp | 19 +++++++++++++------ server/CGameHandler.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 8b2395864..f5307ed9c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3892,7 +3892,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) BattleAttack bat; prepareAttack(bat, stack, destinationStack, (i ? 0 : distance), ba.additionalInfo); //no distance travelled on second attack //prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo); - handleAttackBeforeCasting(bat); //only before first attack + handleAttackBeforeCasting(&bat); //only before first attack sendAndApply(&bat); handleAfterAttackCasting(bat); } @@ -3936,7 +3936,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) BattleAttack bat; bat.flags |= BattleAttack::SHOT; prepareAttack(bat, stack, destinationStack, 0, ba.destinationTile); - handleAttackBeforeCasting(bat); + handleAttackBeforeCasting(&bat); sendAndApply(&bat); handleAfterAttackCasting(bat); } @@ -5209,16 +5209,23 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta } } -void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat) +void CGameHandler::handleAttackBeforeCasting(BattleAttack *bat) { - const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking); - attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no death stare / acid breath needed? + const CStack * attacker = gs->curB->battleGetStackByID(bat->stackAttacking); + attackCasting(*bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no death stare / acid breath needed? + // filter possibly dead stacks + bat->bsa.erase(std::remove_if(bat->bsa.begin(), bat->bsa.end(), + [this](const BattleStackAttacked &bsa) + { + return battleGetStackByID(bsa.stackAttacked) == nullptr; + }), + bat->bsa.end()); } void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) { const CStack * attacker = gs->curB->battleGetStackByID(bat.stackAttacking); - if (!attacker) //could be already dead + if (!attacker || bat.bsa.empty()) // can be already dead return; auto cast = [=](SpellID spellID, int power) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index f2b6b0a6a..a707bbfe2 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -279,7 +279,7 @@ public: void run(bool resume); void newTurn(); - void handleAttackBeforeCasting (const BattleAttack & bat); + void handleAttackBeforeCasting(BattleAttack *bat); void handleAfterAttackCasting (const BattleAttack & bat); void attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker); bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ArtifactPosition slot);