diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 3c2411f7f..c03323e4b 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -647,7 +647,7 @@ void BattlegroundMgr::BuildBattlegroundListPacket(WorldPacket* data, ObjectGuid } } -void BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, BattlegroundTypeId bgTypeId) +bool BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, BattlegroundTypeId bgTypeId) { if (Battleground* bg = GetBattleground(instanceId, bgTypeId)) { @@ -655,12 +655,11 @@ void BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, Batt Position const* pos = bg->GetTeamStartPosition(player->GetBgTeamId()); LOG_DEBUG("bg.battleground", "BattlegroundMgr::SendToBattleground: Sending {} to map {}, {} (bgType {})", player->GetName(), mapid, pos->ToString(), bgTypeId); - player->TeleportTo(mapid, pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation()); - } - else - { - LOG_ERROR("bg.battleground", "BattlegroundMgr::SendToBattleground: Instance {} (bgType {}) not found while trying to teleport player {}", instanceId, bgTypeId, player->GetName()); + return player->TeleportTo(mapid, pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation()); } + + LOG_ERROR("bg.battleground", "BattlegroundMgr::SendToBattleground: Instance {} (bgType {}) not found while trying to teleport player {}", instanceId, bgTypeId, player->GetName()); + return false; } void BattlegroundMgr::SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid guid) diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h index 313bee8f0..50d5cc27a 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.h +++ b/src/server/game/Battlegrounds/BattlegroundMgr.h @@ -93,7 +93,10 @@ public: void LoadBattlegroundTemplates(); void DeleteAllBattlegrounds(); - void SendToBattleground(Player* player, uint32 InstanceID, BattlegroundTypeId bgTypeId); + // Returns false when the teleport could not start (instance gone, or a + // synchronous TeleportTo failure) so the accept path can release the + // otherwise-orphaned invited reservation. + bool SendToBattleground(Player* player, uint32 InstanceID, BattlegroundTypeId bgTypeId); /* Battleground queues */ BattlegroundQueue& GetBattlegroundQueue(BattlegroundQueueTypeId bgQueueTypeId) { return m_BattlegroundQueues[bgQueueTypeId]; } diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 169f642ec..1fac5ad54 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -1302,13 +1302,16 @@ int32 BattlegroundQueue::GetQueueAnnouncementTimer(uint32 bracketId) const void BattlegroundQueue::InviteGroupToBG(GroupQueueInfo* ginfo, Battleground* bg, TeamId teamId) { + // An already-invited group keeps the side it was invited under: writing + // teamId here would split a future re-invite's IncreaseInvitedCount from the + // original side's DecreaseInvitedCount at leave, desyncing the ledger. + if (ginfo->IsInvitedToBGInstanceGUID) + return; + // set side if needed if (teamId != TEAM_NEUTRAL) ginfo->teamId = teamId; - if (ginfo->IsInvitedToBGInstanceGUID) - return; - // set invitation ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index ff5a57c47..9051801e5 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -544,7 +544,31 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) sLFGMgr->LeaveAllLfgQueues(_player->GetGUID(), false); _player->SetBattlegroundId(bg->GetInstanceID(), bg->GetBgTypeID(), queueSlot, true, bgTypeId == BATTLEGROUND_RB, teamId); - sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + + if (!sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId)) + { + // The teleport never started (instance gone, or a synchronous + // TeleportTo veto such as a DK still locked to Ebon Hold). The accept + // already pulled the player out of the queue and he can't decline + // now, so undo the accept here -- otherwise the invited reservation + // leaks forever, permanently skewing team selection and blocking the + // empty instance's cleanup. + bg->DecreaseInvitedCount(teamId); + _player->RemoveBattlegroundQueueId(bgQueueTypeId); + _player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE, PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL); + + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); + SendPacket(&data); + + // Free slot -> let the queue refill it. BG only, like the sibling + // leave-queue path: an arena update needs its own type/rating. + if (!ginfo.ArenaType) + sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); + + LOG_ERROR("bg.battleground", "Battleground: player {} {} failed to teleport into bg {}, bgtype {}; released the invited reservation.", + _player->GetName(), _player->GetGUID().ToString(), bg->GetInstanceID(), bg->GetBgTypeID()); + return; + } LOG_DEBUG("bg.battleground", "Battleground: player {} {} joined battle for bg {}, bgtype {}, queue type {}.", _player->GetName(), _player->GetGUID().ToString(), bg->GetInstanceID(), bg->GetBgTypeID(), bgQueueTypeId); } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 6b66f6f27..456628d03 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -499,8 +499,21 @@ public: // Remove from LFG queues sLFGMgr->LeaveAllLfgQueues(player->GetGUID(), false); + // Book the reservation like the queue path does, so it is symmetric + // with RemovePlayerAtLeave's decrement and the 0-players/0-invited + // state can't let Battleground::Update delete the arena while players + // are still on the loading screen. + bg->IncreaseInvitedCount(teamId); player->SetBattlegroundId(bg->GetInstanceID(), bgTypeId, queueSlot, true, false, teamId); - sBattlegroundMgr->SendToBattleground(player, bg->GetInstanceID(), bgTypeId); + + // A synchronous teleport failure would strand that reservation (the + // player never enters and never reaches RemovePlayerAtLeave), leaving + // the arena undeletable; release it and reset his bg data. + if (!sBattlegroundMgr->SendToBattleground(player, bg->GetInstanceID(), bgTypeId)) + { + bg->DecreaseInvitedCount(teamId); + player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE, PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL); + } } handler->PSendSysMessage("Success! Players are now being teleported to the arena.");