Mercenaires : double liberation du Group, et bots congedies par le balayage d inactivite
Double liberation ----------------- Group::Disband() declenche sScriptMgr->OnGroupDisband(this) en toute premiere instruction et se termine par sGroupMgr->RemoveGroup(this); delete this; MercenaryMgr::OnGroupDisband congediait aussitot, donc ReleaseBot appelait bot->RemoveFromGroup(), donc Group::RemoveMember, qui disband le groupe des qu il repasse sous deux membres : un second Disband() imbrique faisait delete this, puis le Disband() exterieur reprenait la main sur un objet libere et le liberait une seconde fois. Le garde-fou m_releasing ne protegeait que nos propres hooks, pas la reentrance du core. Le tas s en trouvait corrompu et le serveur tombait bien plus tard, dans un malloc sans rapport (Object::_InitValues sur un chargement de grille). Piege pose avec MALLOC_CONF=junk:true,redzone:true,quarantine:...,abort:true, qui a signale le bloc passe deux fois par le chemin de liberation. Les deux hooks de groupe se contentent desormais de marquer les contrats (pendingRelease) ; Update() les libere au tick suivant, hors de la pile d appel du core. Balayage d inactivite --------------------- PlayerBotMgr::UpdateIdleBotLogout exemptait les bots du siege des capitales mais pas les mercenaires : un bot loue, immobile aupres de son employeur, etait juge inactif et deconnecte au bout de pbotbg_idlelogout secondes. Exemption ajoutee via MercenaryMgr::IsAccountHired(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -428,15 +428,23 @@ void MercenaryMgr::DismissAll(ObjectGuid ownerGuid)
|
||||
// Un membre a quitte un groupe, de son plein gre ou expulse. Si c est un
|
||||
// mercenaire, son contrat s arrete la ; si c est son employeur, toute sa
|
||||
// compagnie se dissout.
|
||||
//
|
||||
// On se contente de MARQUER les contrats, la liberation a lieu au tick suivant.
|
||||
// Ce hook est appele depuis Group::RemoveMember et depuis Group::Disband ;
|
||||
// congedier sur-le-champ rappelle Group::RemoveMember par en dessous, or
|
||||
// RemoveMember disband le groupe des qu il repasse sous deux membres et
|
||||
// Disband() se termine par `delete this`. L appel exterieur reprenait alors la
|
||||
// main sur un Group libere et le liberait une seconde fois -- double liberation
|
||||
// qui corrompait le tas et faisait tomber le serveur bien plus tard, dans un
|
||||
// malloc sans rapport.
|
||||
void MercenaryMgr::OnPlayerLeftGroup(ObjectGuid guid)
|
||||
{
|
||||
if (m_releasing)
|
||||
return;
|
||||
|
||||
if (IsMercenary(guid))
|
||||
DismissOne(guid);
|
||||
else
|
||||
DismissAll(guid);
|
||||
for (MercenaryContract& contract : m_contracts)
|
||||
if (contract.botGuid == guid || contract.ownerGuid == guid)
|
||||
contract.pendingRelease = true;
|
||||
}
|
||||
|
||||
void MercenaryMgr::OnGroupDisband(Group* group)
|
||||
@@ -444,13 +452,12 @@ void MercenaryMgr::OnGroupDisband(Group* group)
|
||||
if (m_releasing || !group || m_contracts.empty())
|
||||
return;
|
||||
|
||||
Group::MemberSlotList const& members = group->GetMemberSlots();
|
||||
std::vector<ObjectGuid> guids;
|
||||
for (Group::MemberSlot const& slot : members)
|
||||
guids.push_back(slot.guid);
|
||||
|
||||
for (ObjectGuid const& guid : guids)
|
||||
OnPlayerLeftGroup(guid);
|
||||
// Meme regle que ci-dessus : ne rien toucher pendant que le core demonte le
|
||||
// groupe. Disband() retire lui-meme tous les membres juste apres ce hook.
|
||||
for (Group::MemberSlot const& slot : group->GetMemberSlots())
|
||||
for (MercenaryContract& contract : m_contracts)
|
||||
if (contract.botGuid == slot.guid || contract.ownerGuid == slot.guid)
|
||||
contract.pendingRelease = true;
|
||||
}
|
||||
|
||||
void MercenaryMgr::OnPlayerLogout(Player* player)
|
||||
@@ -473,6 +480,22 @@ void MercenaryMgr::Update(uint32 diff)
|
||||
|
||||
for (std::vector<MercenaryContract>::iterator it = m_contracts.begin(); it != m_contracts.end(); )
|
||||
{
|
||||
// Contrat rompu par un hook de groupe : la liberation a ete differee
|
||||
// jusqu ici pour ne pas reentrer dans Group::RemoveMember / Disband().
|
||||
if (it->pendingRelease)
|
||||
{
|
||||
MercenaryContract const contract = *it;
|
||||
it = m_contracts.erase(it);
|
||||
|
||||
if (Player* owner = ObjectAccessor::FindConnectedPlayer(contract.ownerGuid))
|
||||
ChatHandler(owner->GetSession()).PSendSysMessage(
|
||||
"|cff00ff00[Portail]|r Votre %s retourne d'où il vient. Il vous faudra payer de nouveau pour en invoquer un autre.",
|
||||
GetRoleName(contract.role));
|
||||
|
||||
ReleaseBot(contract);
|
||||
continue;
|
||||
}
|
||||
|
||||
// L employeur d abord : plus de maitre, plus de contrat. C est le filet
|
||||
// de securite si un hook n a pas ete appele (crash client, timeout...).
|
||||
Player* owner = ObjectAccessor::FindConnectedPlayer(it->ownerGuid);
|
||||
|
||||
@@ -63,7 +63,8 @@ enum MercenaryResult
|
||||
|
||||
struct MercenaryContract
|
||||
{
|
||||
MercenaryContract() : accountId(0), role(0), stage(MERC_STAGE_SUMMONING), waitSeconds(0) { }
|
||||
MercenaryContract() : accountId(0), role(0), stage(MERC_STAGE_SUMMONING), waitSeconds(0),
|
||||
pendingRelease(false) { }
|
||||
|
||||
uint32 accountId; // compte bot reserve
|
||||
ObjectGuid ownerGuid; // joueur qui a paye
|
||||
@@ -71,6 +72,10 @@ struct MercenaryContract
|
||||
uint8 role; // ROLE_TANK, ROLE_HEALER ou ROLE_DAMAGE
|
||||
uint8 stage;
|
||||
uint32 waitSeconds;
|
||||
|
||||
// Contrat rompu par un hook de groupe du core, mais dont la liberation est
|
||||
// differee au prochain Update() : voir MercenaryMgr::OnPlayerLeftGroup().
|
||||
bool pendingRelease;
|
||||
};
|
||||
|
||||
class TC_GAME_API MercenaryMgr
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "LFGMgr.h"
|
||||
#include "Config.h"
|
||||
#include "PlayerBotSession.h"
|
||||
#include "MercenaryMgr.h"
|
||||
#include "AccountMgr.h"
|
||||
#include "BattlenetAccountMgr.h"
|
||||
#include "CharacterPackets.h"
|
||||
@@ -3515,7 +3516,12 @@ void PlayerBotMgr::UpdateIdleBotLogout()
|
||||
bool inSiege = false;
|
||||
if (BotBGAI* botAI = dynamic_cast<BotBGAI*>(player->GetAI()))
|
||||
inSiege = botAI->IsSiegeMode();
|
||||
bool busy = inSiege || pSession->HasSchedules() || player->InBattleground() || player->InArena()
|
||||
// Module Mercenaires : un bot loue est sous contrat meme s il reste
|
||||
// immobile aupres de son employeur. Sans cette exemption le balayage
|
||||
// d inactivite le deconnectait au bout de pbotbg_idlelogout secondes et
|
||||
// le mercenaire disparaissait sans explication.
|
||||
bool const hired = sMercenaryMgr->IsAccountHired(accId);
|
||||
bool busy = inSiege || hired || pSession->HasSchedules() || player->InBattleground() || player->InArena()
|
||||
|| player->InBattlegroundQueue() || player->GetMap()->IsDungeon() || player->isUsingLfg()
|
||||
|| player->IsInCombat();
|
||||
if (busy)
|
||||
|
||||
Reference in New Issue
Block a user