diff --git a/data/sql/updates/pending_db_world/rev_1645819759880724600.sql b/data/sql/updates/pending_db_world/rev_1645819759880724600.sql new file mode 100644 index 000000000..0e9d9bf8c --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1645819759880724600.sql @@ -0,0 +1,5 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1645819759880724600'); + +DELETE FROM `acore_string` WHERE `entry` = 726; +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(726, '|cffff0000[Arena Queue]:|r %s (skirmish %s) -- [%u-%u] [%u/%u]|r', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/src/common/Debugging/Errors.h b/src/common/Debugging/Errors.h index fec93713d..0155d0ddb 100644 --- a/src/common/Debugging/Errors.h +++ b/src/common/Debugging/Errors.h @@ -56,7 +56,7 @@ AC_COMMON_API std::string GetDebugInfo(); #define WPAssert(cond, ...) do { if (!(cond)) Acore::Assert(__FILE__, __LINE__, __FUNCTION__, GetDebugInfo(), #cond, ##__VA_ARGS__); } while(0) #define WPAssert_NODEBUGINFO(cond) do { if (!(cond)) Acore::Assert(__FILE__, __LINE__, __FUNCTION__, "", #cond); } while(0) -#define WPFatal(cond, ...) do { if (!(cond)) Acore::Fatal(__FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); } while(0) +#define WPFatal(cond, ...) do { if (!(cond)) Acore::Fatal(__FILE__, __LINE__, __FUNCTION__, #cond, ##__VA_ARGS__); } while(0) #define WPError(cond, msg) do { if (!(cond)) Acore::Error(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) #define WPWarning(cond, msg) do { if (!(cond)) Acore::Warning(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) #define WPAbort(...) do { Acore::Abort(__FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); } while(0) diff --git a/src/server/game/ArenaSpectator/ArenaSpectator.cpp b/src/server/game/ArenaSpectator/ArenaSpectator.cpp index cc31c7d8a..6048b7636 100644 --- a/src/server/game/ArenaSpectator/ArenaSpectator.cpp +++ b/src/server/game/ArenaSpectator/ArenaSpectator.cpp @@ -137,7 +137,7 @@ bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, std::s if (uint32 inviteInstanceId = player->GetPendingSpectatorInviteInstanceId()) { - if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId)) + if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId, BATTLEGROUND_TYPE_NONE)) tbg->RemoveToBeTeleported(player->GetGUID()); player->SetPendingSpectatorInviteInstanceId(0); } diff --git a/src/server/game/Battlegrounds/Arena.cpp b/src/server/game/Battlegrounds/Arena.cpp index fe6e7a439..dfb84271d 100644 --- a/src/server/game/Battlegrounds/Arena.cpp +++ b/src/server/game/Battlegrounds/Arena.cpp @@ -312,6 +312,10 @@ void Arena::EndBattleground(TeamId winnerTeamId) } } + // update previous opponents for arena queue + winnerArenaTeam->SetPreviousOpponents(loserArenaTeam->GetId()); + loserArenaTeam->SetPreviousOpponents(winnerArenaTeam->GetId()); + // save the stat changes if (bValidArena) { diff --git a/src/server/game/Battlegrounds/ArenaScore.h b/src/server/game/Battlegrounds/ArenaScore.h index 0528d4830..b51dacf95 100644 --- a/src/server/game/Battlegrounds/ArenaScore.h +++ b/src/server/game/Battlegrounds/ArenaScore.h @@ -61,7 +61,7 @@ protected: { RatingChange = ratingChange; MatchmakerRating = matchMakerRating; - TeamName = teamName; + TeamName = std::string(teamName); } void BuildRatingInfoBlock(WorldPacket& data); diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index c710c9cc9..7b67b68f8 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -345,7 +345,7 @@ void ArenaTeam::DelMember(ObjectGuid guid, bool cleanDb) WorldPacket data; playerMember->RemoveBattlegroundQueueId(bgQueue); sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, nullptr, playerMember->GetBattlegroundQueueIndex(bgQueue), STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); - queue.RemovePlayer(playerMember->GetGUID(), true, 0); + queue.RemovePlayer(playerMember->GetGUID(), true); playerMember->GetSession()->SendPacket(&data); } } diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h index 316737727..fdad00fd5 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.h +++ b/src/server/game/Battlegrounds/ArenaTeam.h @@ -209,6 +209,9 @@ public: void FinishWeek(); void FinishGame(int32 mod, const Map* bgMap); + void SetPreviousOpponents(uint32 arenaTeamId) { PreviousOpponents = arenaTeamId; } + uint32 GetPreviousOpponents() { return PreviousOpponents; } + void CreateTempArenaTeam(std::vector playerList, uint8 type, std::string const& teamName); // Containers @@ -229,5 +232,7 @@ protected: MemberList Members; ArenaTeamStats Stats; + + uint32 PreviousOpponents = 0; }; #endif diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 35c65b278..3ac6b1922 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -232,6 +232,9 @@ Battleground::~Battleground() m_Map = nullptr; } + // remove from bg free slot queue + RemoveFromBGFreeSlotQueue(); + for (auto const& itr : PlayerScores) delete itr.second; } @@ -251,8 +254,21 @@ void Battleground::Update(uint32 diff) if (!GetPlayersSize()) { + //BG is empty + // if there are no players invited, delete BG + // this will delete arena or bg object, where any player entered + // [[ but if you use battleground object again (more battles possible to be played on 1 instance) + // then this condition should be removed and code: + // if (!GetInvitedCount(TEAM_HORDE) && !GetInvitedCount(TEAM_ALLIANCE)) + // AddToFreeBGObjectsQueue(); // not yet implemented + // should be used instead of current + // ]] + // Battleground Template instance cannot be updated, because it would be deleted if (!GetInvitedCount(TEAM_HORDE) && !GetInvitedCount(TEAM_ALLIANCE)) + { m_SetDeleteThis = true; + } + return; } @@ -768,6 +784,7 @@ void Battleground::EndBattleground(PvPTeamId winnerTeamId) if (GetStatus() == STATUS_WAIT_LEAVE) return; + RemoveFromBGFreeSlotQueue(); SetStatus(STATUS_WAIT_LEAVE); SetWinner(winnerTeamId); @@ -956,13 +973,17 @@ void Battleground::RemovePlayerAtLeave(Player* player) // if the player was a match participant if (participant) { - WorldPacket data; - player->ClearAfkReports(); + WorldPacket data; sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetCurrentBattlegroundQueueSlot(), STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); player->GetSession()->SendPacket(&data); + BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetBgTypeID(), GetArenaType()); + + // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg + player->RemoveBattlegroundQueueId(bgQueueTypeId); + // remove from raid group if player is member if (Group* group = GetBgRaid(teamId)) if (group->IsMember(player->GetGUID())) @@ -977,6 +998,19 @@ void Battleground::RemovePlayerAtLeave(Player* player) if (isBattleground() && !player->IsGameMaster() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER)) if (status == STATUS_IN_PROGRESS || status == STATUS_WAIT_JOIN) player->ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER); + + DecreaseInvitedCount(teamId); + + //we should update battleground queue, but only if bg isn't ending + if (isBattleground() && GetStatus() < STATUS_WAIT_LEAVE) + { + BattlegroundTypeId bgTypeId = GetBgTypeID(); + BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetBgTypeID(), GetArenaType()); + + // a player has left the battleground, so there are free slots -> add to queue + AddToBGFreeSlotQueue(); + sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, GetBracketId()); + } } // Remove shapeshift auras @@ -1009,6 +1043,7 @@ void Battleground::Init() m_BgInvitedPlayers[TEAM_ALLIANCE] = 0; m_BgInvitedPlayers[TEAM_HORDE] = 0; + _InBGFreeSlotQueue = false; m_Players.clear(); @@ -1028,9 +1063,15 @@ void Battleground::StartBattleground() SetStartTime(0); SetLastResurrectTime(0); + // add BG to free slot queue + AddToBGFreeSlotQueue(); + // add bg to update list // this must be done here, because we need to have already invited some players when first Battleground::Update() method is executed sBattlegroundMgr->AddBattleground(this); + + if (m_IsRated) + LOG_DEBUG("bg.arena", "Arena match type: {} for Team1Id: {} - Team2Id: {} started.", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE]); } void Battleground::AddPlayer(Player* player) @@ -1129,6 +1170,26 @@ void Battleground::AddOrSetPlayerToCorrectBgGroup(Player* player, TeamId teamId) } } +// This method should be called only once ... it adds pointer to queue +void Battleground::AddToBGFreeSlotQueue() +{ + if (!_InBGFreeSlotQueue && isBattleground()) + { + sBattlegroundMgr->AddToBGFreeSlotQueue(m_RealTypeID, this); + _InBGFreeSlotQueue = true; + } +} + +// This method removes this battleground from free queue - it must be called when deleting battleground +void Battleground::RemoveFromBGFreeSlotQueue() +{ + if (_InBGFreeSlotQueue) + { + sBattlegroundMgr->RemoveFromBGFreeSlotQueue(m_RealTypeID, m_InstanceID); + _InBGFreeSlotQueue = false; + } +} + uint32 Battleground::GetFreeSlotsForTeam(TeamId teamId) const { if (!(GetStatus() == STATUS_IN_PROGRESS || GetStatus() == STATUS_WAIT_JOIN)) @@ -1611,6 +1672,7 @@ void Battleground::SendMessage2ToAll(uint32 entry, ChatMsg type, Player const* s void Battleground::EndNow() { + RemoveFromBGFreeSlotQueue(); SetStatus(STATUS_WAIT_LEAVE); SetEndTime(0); } @@ -1777,6 +1839,12 @@ GraveyardStruct const* Battleground::GetClosestGraveyard(Player* player) return sGraveyard->GetClosestGraveyard(player, player->GetBgTeamId()); } +void Battleground::SetBracket(PvPDifficultyEntry const* bracketEntry) +{ + m_BracketId = bracketEntry->GetBracketId(); + SetLevelRange(bracketEntry->minLevel, bracketEntry->maxLevel); +} + void Battleground::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) { for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index 5f4b2709a..1777699d4 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -316,6 +316,7 @@ public: // Get methods: [[nodiscard]] std::string GetName() const { return m_Name; } [[nodiscard]] BattlegroundTypeId GetBgTypeID(bool GetRandom = false) const { return GetRandom ? m_RandomTypeID : m_RealTypeID; } + [[nodiscard]] BattlegroundBracketId GetBracketId() const { return m_BracketId; } [[nodiscard]] uint32 GetInstanceID() const { return m_InstanceID; } [[nodiscard]] BattlegroundStatus GetStatus() const { return m_Status; } [[nodiscard]] uint32 GetClientInstanceID() const { return m_ClientInstanceID; } @@ -341,6 +342,7 @@ public: void SetName(std::string_view name) { m_Name = std::string(name); } void SetBgTypeID(BattlegroundTypeId TypeID) { m_RealTypeID = TypeID; } void SetRandomTypeID(BattlegroundTypeId TypeID) { m_RandomTypeID = TypeID; } + void SetBracket(PvPDifficultyEntry const* bracketEntry); void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } void SetStatus(BattlegroundStatus Status) { m_Status = Status; } void SetClientInstanceID(uint32 InstanceID) { m_ClientInstanceID = InstanceID; } @@ -361,6 +363,9 @@ public: void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; } void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; } + void AddToBGFreeSlotQueue(); // this queue will be useful when more battlegrounds instances will be available + void RemoveFromBGFreeSlotQueue(); // this method could delete whole BG instance, if another free is available + void DecreaseInvitedCount(TeamId teamId) { if (m_BgInvitedPlayers[teamId]) --m_BgInvitedPlayers[teamId]; } void IncreaseInvitedCount(TeamId teamId) { ++m_BgInvitedPlayers[teamId]; } [[nodiscard]] uint32 GetInvitedCount(TeamId teamId) const { return m_BgInvitedPlayers[teamId]; } @@ -642,7 +647,9 @@ private: uint32 m_ValidStartPositionTimer; int32 m_EndTime; // it is set to 120000 when bg is ending and it decreases itself uint32 m_LastResurrectTime; + BattlegroundBracketId m_BracketId{ BG_BRACKET_ID_FIRST }; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + bool _InBGFreeSlotQueue{ false }; // used to make sure that BG is only once inserted into the BattlegroundMgr.BGFreeSlotQueue[bgTypeId] deque bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave bool m_IsArena; PvPTeamId m_WinnerId; diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index fbb612410..84caf57b1 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -48,15 +48,22 @@ #include "WorldPacket.h" #include +bool BattlegroundTemplate::IsArena() const +{ + return BattlemasterEntry->type == MAP_ARENA; +} + /*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ -BattlegroundMgr::BattlegroundMgr() : m_ArenaTesting(false), m_Testing(false), - m_lastClientVisibleInstanceId(0), m_NextAutoDistributionTime(0), m_AutoDistributionTimeChecker(0), m_NextPeriodicQueueUpdateTime(5 * IN_MILLISECONDS) +BattlegroundMgr::BattlegroundMgr() : + m_ArenaTesting(false), + m_Testing(false), + m_NextAutoDistributionTime(0), + m_AutoDistributionTimeChecker(0), + m_NextPeriodicQueueUpdateTime(5 * IN_MILLISECONDS) { - for (uint32 qtype = BATTLEGROUND_QUEUE_NONE; qtype < MAX_BATTLEGROUND_QUEUE_TYPES; ++qtype) - m_BattlegroundQueues[qtype].SetBgTypeIdAndArenaType(BGTemplateId(BattlegroundQueueTypeId(qtype)), BGArenaType(BattlegroundQueueTypeId(qtype))); } BattlegroundMgr::~BattlegroundMgr() @@ -72,47 +79,68 @@ BattlegroundMgr* BattlegroundMgr::instance() void BattlegroundMgr::DeleteAllBattlegrounds() { - while (!m_Battlegrounds.empty()) - delete m_Battlegrounds.begin()->second; - m_Battlegrounds.clear(); + for (auto& [_, data] : bgDataStore) + { + while (!data._Battlegrounds.empty()) + delete data._Battlegrounds.begin()->second; - while (!m_BattlegroundTemplates.empty()) - delete m_BattlegroundTemplates.begin()->second; - m_BattlegroundTemplates.clear(); + data._Battlegrounds.clear(); + + while (!data.BGFreeSlotQueue.empty()) + delete data.BGFreeSlotQueue.front(); + } + + bgDataStore.clear(); } // used to update running battlegrounds, and delete finished ones void BattlegroundMgr::Update(uint32 diff) { // update all battlegrounds and delete if needed - for (BattlegroundContainer::iterator itr = m_Battlegrounds.begin(), itrDelete; itr != m_Battlegrounds.end(); ) + for (auto& [_, bgData] : bgDataStore) { - itrDelete = itr++; - Battleground* bg = itrDelete->second; - bg->Update(diff); - if (bg->ToBeDeleted()) + auto& bgList = bgData._Battlegrounds; + auto itrDelete = bgList.begin(); + + // first one is template and should not be deleted + for (BattlegroundContainer::iterator itr = ++itrDelete; itr != bgList.end();) { - itrDelete->second = nullptr; - m_Battlegrounds.erase(itrDelete); - delete bg; + itrDelete = itr++; + Battleground* bg = itrDelete->second; + + bg->Update(diff); + if (bg->ToBeDeleted()) + { + itrDelete->second = nullptr; + bgList.erase(itrDelete); + + BattlegroundClientIdsContainer& clients = bgData._ClientBattlegroundIds[bg->GetBracketId()]; + if (!clients.empty()) + clients.erase(bg->GetClientInstanceID()); + + delete bg; + } } } // update events - for (int qtype = BATTLEGROUND_QUEUE_NONE; qtype < MAX_BATTLEGROUND_QUEUE_TYPES; ++qtype) + for (uint8 qtype = BATTLEGROUND_QUEUE_NONE; qtype < MAX_BATTLEGROUND_QUEUE_TYPES; ++qtype) m_BattlegroundQueues[qtype].UpdateEvents(diff); // update using scheduled tasks (used only for rated arenas, initial opponent search works differently than periodic queue update) - if (!m_ArenaQueueUpdateScheduler.empty()) + if (!m_QueueUpdateScheduler.empty()) { std::vector scheduled; - std::swap(scheduled, m_ArenaQueueUpdateScheduler); + std::swap(scheduled, m_QueueUpdateScheduler); + for (uint8 i = 0; i < scheduled.size(); i++) { - uint32 arenaRatedTeamId = scheduled[i] >> 32; + uint32 arenaMMRating = scheduled[i] >> 32; + uint8 arenaType = scheduled[i] >> 24 & 255; BattlegroundQueueTypeId bgQueueTypeId = BattlegroundQueueTypeId(scheduled[i] >> 16 & 255); + BattlegroundTypeId bgTypeId = BattlegroundTypeId((scheduled[i] >> 8) & 255); BattlegroundBracketId bracket_id = BattlegroundBracketId(scheduled[i] & 255); - m_BattlegroundQueues[bgQueueTypeId].BattlegroundQueueUpdate(diff, bracket_id, true, arenaRatedTeamId); // pussywizard: looking for opponents only for this team + m_BattlegroundQueues[bgQueueTypeId].BattlegroundQueueUpdate(diff, bgTypeId, bracket_id, arenaType, arenaMMRating > 0, arenaMMRating); } } @@ -121,16 +149,16 @@ void BattlegroundMgr::Update(uint32 diff) { m_NextPeriodicQueueUpdateTime = 5 * IN_MILLISECONDS; + LOG_TRACE("bg.arena", "BattlegroundMgr: UPDATING ARENA QUEUES"); + // for rated arenas for (uint32 qtype = BATTLEGROUND_QUEUE_2v2; qtype < MAX_BATTLEGROUND_QUEUE_TYPES; ++qtype) + { for (uint32 bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) - m_BattlegroundQueues[qtype].BattlegroundQueueUpdate(m_NextPeriodicQueueUpdateTime, BattlegroundBracketId(bracket), true, 0); // pussywizard: 0 for rated means looking for opponents for every team - - // for battlegrounds and not rated arenas - // in first loop try to fill already running battlegrounds, then in a second loop try to create new battlegrounds - for (uint32 qtype = BATTLEGROUND_QUEUE_AV; qtype < MAX_BATTLEGROUND_QUEUE_TYPES; ++qtype) - for (uint32 bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) - m_BattlegroundQueues[qtype].BattlegroundQueueUpdate(m_NextPeriodicQueueUpdateTime, BattlegroundBracketId(bracket), false, 0); + { + m_BattlegroundQueues[qtype].BattlegroundQueueUpdate(diff, BATTLEGROUND_AA, BattlegroundBracketId(bracket), BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId(qtype)), true, 0); + } + } } else m_NextPeriodicQueueUpdateTime -= diff; @@ -225,36 +253,108 @@ void BattlegroundMgr::BuildPlayerJoinedBattlegroundPacket(WorldPacket* data, Pla *data << player->GetGUID(); } -Battleground* BattlegroundMgr::GetBattleground(uint32 instanceId) +Battleground* BattlegroundMgr::GetBattlegroundThroughClientInstance(uint32 instanceId, BattlegroundTypeId bgTypeId) +{ + //cause at HandleBattlegroundJoinOpcode the clients sends the instanceid he gets from + //SMSG_BATTLEFIELD_LIST we need to find the battleground with this clientinstance-id + Battleground* bg = GetBattlegroundTemplate(bgTypeId); + if (!bg) + return nullptr; + + if (bg->isArena()) + return GetBattleground(instanceId, bgTypeId); + + auto const& it = bgDataStore.find(bgTypeId); + if (it == bgDataStore.end()) + return nullptr; + + for (auto const& itr : it->second._Battlegrounds) + { + if (itr.second->GetClientInstanceID() == instanceId) + return itr.second; + } + + return nullptr; +} + +Battleground* BattlegroundMgr::GetBattleground(uint32 instanceId, BattlegroundTypeId bgTypeId) { if (!instanceId) return nullptr; - BattlegroundContainer::const_iterator itr = m_Battlegrounds.find(instanceId); - if (itr != m_Battlegrounds.end()) - return itr->second; + auto GetBgWithInstanceID = [instanceId](BattlegroundData const* bgData) -> Battleground* + { + auto const& itr = bgData->_Battlegrounds.find(instanceId); + if (itr != bgData->_Battlegrounds.end()) + return itr->second; + + return nullptr; + }; + + if (bgTypeId == BATTLEGROUND_TYPE_NONE) + { + for (auto const& [bgType, bgData] : bgDataStore) + { + if (auto bg = GetBgWithInstanceID(&bgData)) + return bg; + } + } + else + { + auto const& itr = bgDataStore.find(bgTypeId); + if (itr == bgDataStore.end()) + return nullptr; + + if (auto bg = GetBgWithInstanceID(&itr->second)) + return bg; + } return nullptr; } Battleground* BattlegroundMgr::GetBattlegroundTemplate(BattlegroundTypeId bgTypeId) { - BattlegroundTemplateContainer::const_iterator itr = m_BattlegroundTemplates.find(bgTypeId); - if (itr != m_BattlegroundTemplates.end()) - return itr->second; + BattlegroundDataContainer::const_iterator itr = bgDataStore.find(bgTypeId); + if (itr == bgDataStore.end()) + return nullptr; - return nullptr; + BattlegroundContainer const& bgs = itr->second._Battlegrounds; + + // map is sorted and we can be sure that lowest instance id has only BG template + return bgs.empty() ? nullptr : bgs.begin()->second; } -uint32 BattlegroundMgr::GetNextClientVisibleInstanceId() +uint32 BattlegroundMgr::CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { - return ++m_lastClientVisibleInstanceId; + if (IsArenaType(bgTypeId)) + return 0; // arenas don't have client-instanceids + + // we create here an instanceid, which is just for + // displaying this to the client and without any other use.. + // the client-instanceIds are unique for each battleground-type + // the instance-id just needs to be as low as possible, beginning with 1 + // the following works, because std::set is default ordered with "<" + // the optimalization would be to use as bitmask std::vector - but that would only make code unreadable + + BattlegroundClientIdsContainer& clientIds = bgDataStore[bgTypeId]._ClientBattlegroundIds[bracket_id]; + uint32 lastId = 0; + + for (BattlegroundClientIdsContainer::const_iterator itr = clientIds.begin(); itr != clientIds.end();) + { + if ((++lastId) != *itr) // if there is a gap between the ids, we will break.. + break; + + lastId = *itr; + } + + clientIds.emplace(++lastId); + return lastId; } // create a new battleground that will really be used to play -Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId originalBgTypeId, uint32 minLevel, uint32 maxLevel, uint8 arenaType, bool isRated) +Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId originalBgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated) { - BattlegroundTypeId bgTypeId = GetRandomBG(originalBgTypeId, minLevel); + BattlegroundTypeId bgTypeId = GetRandomBG(originalBgTypeId, bracketEntry->minLevel); if (originalBgTypeId == BATTLEGROUND_AA) originalBgTypeId = bgTypeId; @@ -262,9 +362,13 @@ Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId original // get the template BG Battleground* bg_template = GetBattlegroundTemplate(bgTypeId); if (!bg_template) + { + LOG_ERROR("bg.battleground", "Battleground: CreateNewBattleground - bg template not found for {}", bgTypeId); return nullptr; + } Battleground* bg = nullptr; + // create a copy of the BG template if (BattlegroundMgr::bgTypeToTemplate.find(bgTypeId) == BattlegroundMgr::bgTypeToTemplate.end()) { @@ -275,9 +379,9 @@ Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId original bool isRandom = bgTypeId != originalBgTypeId && !bg->isArena(); - bg->SetLevelRange(minLevel, maxLevel); + bg->SetBracket(bracketEntry); bg->SetInstanceID(sMapMgr->GenerateInstanceId()); - bg->SetClientInstanceID(IsArenaType(originalBgTypeId) ? 0 : GetNextClientVisibleInstanceId()); + bg->SetClientInstanceID(CreateClientVisibleInstanceId(originalBgTypeId, bracketEntry->GetBracketId())); bg->Init(); bg->SetStatus(STATUS_WAIT_JOIN); // start the joining of the bg bg->SetArenaType(arenaType); @@ -298,37 +402,40 @@ Battleground* BattlegroundMgr::CreateNewBattleground(BattlegroundTypeId original } // used to create the BG templates -bool BattlegroundMgr::CreateBattleground(CreateBattlegroundData& data) +bool BattlegroundMgr::CreateBattleground(BattlegroundTemplate const* bgTemplate) { // Create the BG - Battleground* bg = nullptr; - bg = BattlegroundMgr::bgtypeToBattleground[data.bgTypeId]; + Battleground* bg = GetBattlegroundTemplate(bgTemplate->Id); - if (bg == nullptr) - return false; + if (!bg) + { + bg = BattlegroundMgr::bgtypeToBattleground[bgTemplate->Id]; - if (data.bgTypeId == BATTLEGROUND_RB) - bg->SetRandom(true); + ASSERT(bg); - bg->SetMapId(data.MapID); - bg->SetBgTypeID(data.bgTypeId); - bg->SetInstanceID(0); - bg->SetArenaorBGType(data.IsArena); - bg->SetMinPlayersPerTeam(data.MinPlayersPerTeam); - bg->SetMaxPlayersPerTeam(data.MaxPlayersPerTeam); - bg->SetName(data.BattlegroundName); - bg->SetTeamStartPosition(TEAM_ALLIANCE, data.StartLocation[TEAM_ALLIANCE]); - bg->SetTeamStartPosition(TEAM_HORDE, data.StartLocation[TEAM_HORDE]); - bg->SetStartMaxDist(data.StartMaxDist); - bg->SetLevelRange(data.LevelMin, data.LevelMax); - bg->SetScriptId(data.scriptId); + if (bgTemplate->Id == BATTLEGROUND_RB) + bg->SetRandom(true); - AddBattleground(bg); + bg->SetBgTypeID(bgTemplate->Id); + bg->SetInstanceID(0); + AddBattleground(bg); + } + + bg->SetMapId(bgTemplate->BattlemasterEntry->mapid[0]); + bg->SetName(bgTemplate->BattlemasterEntry->name[sWorld->GetDefaultDbcLocale()]); + bg->SetArenaorBGType(bgTemplate->IsArena()); + bg->SetMinPlayersPerTeam(bgTemplate->MinPlayersPerTeam); + bg->SetMaxPlayersPerTeam(bgTemplate->MaxPlayersPerTeam); + bg->SetTeamStartPosition(TEAM_ALLIANCE, bgTemplate->StartLocation[TEAM_ALLIANCE]); + bg->SetTeamStartPosition(TEAM_HORDE, bgTemplate->StartLocation[TEAM_HORDE]); + bg->SetStartMaxDist(bgTemplate->MaxStartDistSq); + bg->SetLevelRange(bgTemplate->MinLevel, bgTemplate->MaxLevel); + bg->SetScriptId(bgTemplate->ScriptId); return true; } -void BattlegroundMgr::CreateInitialBattlegrounds() +void BattlegroundMgr::LoadBattlegroundTemplates() { uint32 oldMSTime = getMSTime(); @@ -344,13 +451,11 @@ void BattlegroundMgr::CreateInitialBattlegrounds() return; } - uint32 count = 0; - do { Field* fields = result->Fetch(); - uint32 bgTypeId = fields[0].Get(); + BattlegroundTypeId bgTypeId = static_cast(fields[0].Get()); if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId, nullptr)) continue; @@ -363,72 +468,69 @@ void BattlegroundMgr::CreateInitialBattlegrounds() continue; } - CreateBattlegroundData data; - data.bgTypeId = BattlegroundTypeId(bgTypeId); - data.IsArena = (bl->type == TYPE_ARENA); - data.MinPlayersPerTeam = fields[1].Get(); - data.MaxPlayersPerTeam = fields[2].Get(); - data.LevelMin = fields[3].Get(); - data.LevelMax = fields[4].Get(); + BattlegroundTemplate bgTemplate; + bgTemplate.Id = bgTypeId; + bgTemplate.MinPlayersPerTeam = fields[1].Get(); + bgTemplate.MaxPlayersPerTeam = fields[2].Get(); + bgTemplate.MinLevel = fields[3].Get(); + bgTemplate.MaxLevel = fields[4].Get(); float dist = fields[9].Get(); - data.StartMaxDist = dist * dist; - data.Weight = fields[10].Get(); + bgTemplate.MaxStartDistSq = dist * dist; + bgTemplate.Weight = fields[10].Get(); + bgTemplate.ScriptId = sObjectMgr->GetScriptId(fields[11].Get()); + bgTemplate.BattlemasterEntry = bl; - data.scriptId = sObjectMgr->GetScriptId(fields[11].Get()); - data.BattlegroundName = bl->name[sWorld->GetDefaultDbcLocale()]; - data.MapID = bl->mapid[0]; - - if (data.MaxPlayersPerTeam == 0 || data.MinPlayersPerTeam > data.MaxPlayersPerTeam) + if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.MaxPlayersPerTeam) { - LOG_ERROR("bg.battleground", "Table `battleground_template` for id {} has bad values for MinPlayersPerTeam ({}) and MaxPlayersPerTeam({})", - data.bgTypeId, data.MinPlayersPerTeam, data.MaxPlayersPerTeam); + LOG_ERROR("sql.sql", "Table `battleground_template` for id {} contains bad values for MinPlayersPerTeam ({}) and MaxPlayersPerTeam({}).", + bgTemplate.Id, bgTemplate.MinPlayersPerTeam, bgTemplate.MaxPlayersPerTeam); + continue; } - if (data.LevelMin == 0 || data.LevelMax == 0 || data.LevelMin > data.LevelMax) + if (bgTemplate.MinLevel == 0 || bgTemplate.MaxLevel == 0 || bgTemplate.MinLevel > bgTemplate.MaxLevel) { - LOG_ERROR("bg.battleground", "Table `battleground_template` for id {} has bad values for LevelMin ({}) and LevelMax({})", - data.bgTypeId, data.LevelMin, data.LevelMax); + LOG_ERROR("sql.sql", "Table `battleground_template` for id {} has bad values for LevelMin ({}) and LevelMax({})", + bgTemplate.Id, bgTemplate.MinLevel, bgTemplate.MaxLevel); continue; } - if (data.bgTypeId != BATTLEGROUND_AA && data.bgTypeId != BATTLEGROUND_RB) + if (bgTemplate.Id != BATTLEGROUND_AA && bgTemplate.Id != BATTLEGROUND_RB) { uint32 startId = fields[5].Get(); if (GraveyardStruct const* start = sGraveyard->GetGraveyard(startId)) { - data.StartLocation[TEAM_ALLIANCE].Relocate(start->x, start->y, start->z, fields[6].Get()); + bgTemplate.StartLocation[TEAM_ALLIANCE].Relocate(start->x, start->y, start->z, fields[6].Get()); } else { - LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", data.bgTypeId, startId); + LOG_ERROR("sql.sql", "Table `battleground_template` for id {} contains a non-existing WorldSafeLocs.dbc id {} in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } startId = fields[7].Get(); if (GraveyardStruct const* start = sGraveyard->GetGraveyard(startId)) { - data.StartLocation[TEAM_HORDE].Relocate(start->x, start->y, start->z, fields[8].Get()); + bgTemplate.StartLocation[TEAM_HORDE].Relocate(start->x, start->y, start->z, fields[8].Get()); } else { - LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", data.bgTypeId, startId); + LOG_ERROR("sql.sql", "Table `battleground_template` for id {} contains a non-existing WorldSafeLocs.dbc id {} in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } } - if (!CreateBattleground(data)) + if (!CreateBattleground(&bgTemplate)) continue; - _battlegroundTemplates[BattlegroundTypeId(bgTypeId)] = data; + _battlegroundTemplates[bgTypeId] = bgTemplate; - if (bl->mapid[1] == -1) // in this case we have only one mapId - _battlegroundMapTemplates[bl->mapid[0]] = &_battlegroundTemplates[BattlegroundTypeId(bgTypeId)]; + if (bgTemplate.BattlemasterEntry->mapid[1] == -1) // in this case we have only one mapId + _battlegroundMapTemplates[bgTemplate.BattlemasterEntry->mapid[0]] = &_battlegroundTemplates[bgTypeId]; - ++count; } while (result->NextRow()); - LOG_INFO("server.loading", ">> Loaded {} battlegrounds in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", ">> Loaded {} battlegrounds in {} ms", _battlegroundTemplates.size(), GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -499,23 +601,31 @@ void BattlegroundMgr::BuildBattlegroundListPacket(WorldPacket* data, ObjectGuid size_t count_pos = data->wpos(); *data << uint32(0); // number of bg instances - if (Battleground* bgt = GetBattlegroundTemplate(bgTypeId)) - if (GetBattlegroundBracketByLevel(bgt->GetMapId(), player->getLevel())) + auto const& it = bgDataStore.find(bgTypeId); + if (it != bgDataStore.end()) + { + // expected bracket entry + if (PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(it->second._Battlegrounds.begin()->second->GetMapId(), player->getLevel())) { uint32 count = 0; - /*for (BattlegroundClientIdsContainer::const_iterator itr = clientIds.begin(); itr != clientIds.end(); ++itr) + BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); + BattlegroundClientIdsContainer& clientIds = it->second._ClientBattlegroundIds[bracketId]; + + for (auto const& itr : clientIds) { - *data << uint32(*itr); + *data << uint32(itr); ++count; - }*/ + } + data->put(count_pos, count); } + } } } void BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, BattlegroundTypeId bgTypeId) { - if (Battleground* bg = GetBattleground(instanceId)) + if (Battleground* bg = GetBattleground(instanceId, bgTypeId)) { uint32 mapid = bg->GetMapId(); Position const* pos = bg->GetTeamStartPosition(player->GetBgTeamId()); @@ -636,11 +746,25 @@ void BattlegroundMgr::SetHolidayWeekends(uint32 mask) } } -void BattlegroundMgr::ScheduleArenaQueueUpdate(uint32 arenaRatedTeamId, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundBracketId bracket_id) +void BattlegroundMgr::ScheduleQueueUpdate(uint32 arenaMatchmakerRating, uint8 arenaType, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { - uint64 const scheduleId = ((uint64)arenaRatedTeamId << 32) | (bgQueueTypeId << 16) | bracket_id; - if (std::find(m_ArenaQueueUpdateScheduler.begin(), m_ArenaQueueUpdateScheduler.end(), scheduleId) == m_ArenaQueueUpdateScheduler.end()) - m_ArenaQueueUpdateScheduler.push_back(scheduleId); + //This method must be atomic, @todo add mutex + //we will use only 1 number created of bgTypeId and bracket_id + uint64 const scheduleId = ((uint64)arenaMatchmakerRating << 32) | ((uint64)arenaType << 24) | ((uint64)bgQueueTypeId << 16) | ((uint64)bgTypeId << 8) | (uint64)bracket_id; + if (std::find(m_QueueUpdateScheduler.begin(), m_QueueUpdateScheduler.end(), scheduleId) == m_QueueUpdateScheduler.end()) + m_QueueUpdateScheduler.emplace_back(scheduleId); +} + +uint32 BattlegroundMgr::GetMaxRatingDifference() const +{ + uint32 diff = sWorld->getIntConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + + if (diff == 0) + { + diff = 5000; + } + + return diff; } uint32 BattlegroundMgr::GetRatingDiscardTimer() const @@ -766,24 +890,23 @@ bool BattlegroundMgr::IsBGWeekend(BattlegroundTypeId bgTypeId) BattlegroundTypeId BattlegroundMgr::GetRandomBG(BattlegroundTypeId bgTypeId, uint32 minLevel) { - if (GetBattlegroundTemplateByTypeId(bgTypeId)) + if (BattlegroundTemplate const* bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId)) { std::vector ids; ids.reserve(16); std::vector weights; weights.reserve(16); - BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); - for (int32 mapId : bl->mapid) + for (int32 mapId : bgTemplate->BattlemasterEntry->mapid) { if (mapId == -1) break; - if (CreateBattlegroundData const* bg = GetBattlegroundTemplateByMapId(mapId)) + if (BattlegroundTemplate const* bg = GetBattlegroundTemplateByMapId(mapId)) { - if (bg->LevelMin <= minLevel) + if (bg->MinLevel <= minLevel) { - ids.push_back(bg->bgTypeId); + ids.push_back(bg->Id); weights.push_back(bg->Weight); } } @@ -795,30 +918,38 @@ BattlegroundTypeId BattlegroundMgr::GetRandomBG(BattlegroundTypeId bgTypeId, uin return BATTLEGROUND_TYPE_NONE; } +BGFreeSlotQueueContainer& BattlegroundMgr::GetBGFreeSlotQueueStore(BattlegroundTypeId bgTypeId) +{ + return bgDataStore[bgTypeId].BGFreeSlotQueue; +} + +void BattlegroundMgr::AddToBGFreeSlotQueue(BattlegroundTypeId bgTypeId, Battleground* bg) +{ + bgDataStore[bgTypeId].BGFreeSlotQueue.push_front(bg); +} + +void BattlegroundMgr::RemoveFromBGFreeSlotQueue(BattlegroundTypeId bgTypeId, uint32 instanceId) +{ + BGFreeSlotQueueContainer& queues = bgDataStore[bgTypeId].BGFreeSlotQueue; + for (BGFreeSlotQueueContainer::iterator itr = queues.begin(); itr != queues.end(); ++itr) + if ((*itr)->GetInstanceID() == instanceId) + { + queues.erase(itr); + return; + } +} + void BattlegroundMgr::AddBattleground(Battleground* bg) { - if (bg->GetInstanceID() == 0) - m_BattlegroundTemplates[bg->GetBgTypeID()] = bg; - else - m_Battlegrounds[bg->GetInstanceID()] = bg; + if (bg) + bgDataStore[bg->GetBgTypeID()]._Battlegrounds[bg->GetInstanceID()] = bg; sScriptMgr->OnBattlegroundCreate(bg); } void BattlegroundMgr::RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId) { - if (instanceId == 0) - m_BattlegroundTemplates.erase(bgTypeId); - else - m_Battlegrounds.erase(instanceId); -} - -void BattlegroundMgr::DoForAllBattlegrounds(std::function const& worker) -{ - for (auto const& [_, bg] : m_Battlegrounds) - { - worker(bg); - } + bgDataStore[bgTypeId]._Battlegrounds.erase(instanceId); } // init/update unordered_map diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h index ce5930ac6..bdb17651a 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.h +++ b/src/server/game/Battlegrounds/BattlegroundMgr.h @@ -27,29 +27,38 @@ #include typedef std::map BattlegroundContainer; +typedef std::set BattlegroundClientIdsContainer; typedef std::unordered_map BattleMastersMap; typedef Battleground* (*bgRef)(Battleground*); typedef void(*bgMapRef)(WorldPacket*, Battleground::BattlegroundScoreMap::const_iterator); typedef void(*bgTypeRef)(WorldPacket*, Battleground::BattlegroundScoreMap::const_iterator, Battleground*); -struct CreateBattlegroundData +// this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears +using BGFreeSlotQueueContainer = std::list; + +struct BattlegroundData { - BattlegroundTypeId bgTypeId; - bool IsArena; - uint32 MinPlayersPerTeam; - uint32 MaxPlayersPerTeam; - uint32 LevelMin; - uint32 LevelMax; - char const* BattlegroundName; - uint32 MapID; - float StartMaxDist; - std::array StartLocation; - uint32 scriptId; - uint8 Weight; + BattlegroundContainer _Battlegrounds; + std::array _ClientBattlegroundIds; + BGFreeSlotQueueContainer BGFreeSlotQueue; }; -struct GroupQueueInfo; +struct BattlegroundTemplate +{ + BattlegroundTypeId Id; + uint16 MinPlayersPerTeam; + uint16 MaxPlayersPerTeam; + uint8 MinLevel; + uint8 MaxLevel; + std::array StartLocation; + float MaxStartDistSq; + uint8 Weight; + uint32 ScriptId; + BattlemasterListEntry const* BattlemasterEntry; + + bool IsArena() const; +}; class BattlegroundMgr { @@ -71,21 +80,25 @@ public: void SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid guid); /* Battlegrounds */ - Battleground* GetBattleground(uint32 InstanceID); + Battleground* GetBattlegroundThroughClientInstance(uint32 instanceId, BattlegroundTypeId bgTypeId); + Battleground* GetBattleground(uint32 instanceID, BattlegroundTypeId bgTypeId); Battleground* GetBattlegroundTemplate(BattlegroundTypeId bgTypeId); - Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, uint32 minLevel, uint32 maxLevel, uint8 arenaType, bool isRated); + Battleground* CreateNewBattleground(BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated); void AddBattleground(Battleground* bg); void RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId); + void AddToBGFreeSlotQueue(BattlegroundTypeId bgTypeId, Battleground* bg); + void RemoveFromBGFreeSlotQueue(BattlegroundTypeId bgTypeId, uint32 instanceId); + BGFreeSlotQueueContainer& GetBGFreeSlotQueueStore(BattlegroundTypeId bgTypeId); - void CreateInitialBattlegrounds(); + void LoadBattlegroundTemplates(); void DeleteAllBattlegrounds(); void SendToBattleground(Player* player, uint32 InstanceID, BattlegroundTypeId bgTypeId); /* Battleground queues */ BattlegroundQueue& GetBattlegroundQueue(BattlegroundQueueTypeId bgQueueTypeId) { return m_BattlegroundQueues[bgQueueTypeId]; } - void ScheduleArenaQueueUpdate(uint32 arenaRatedTeamId, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundBracketId bracket_id); + void ScheduleQueueUpdate(uint32 arenaMatchmakerRating, uint8 arenaType, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id); uint32 GetPrematureFinishTime() const; void ToggleArenaTesting(); @@ -105,10 +118,12 @@ public: static BattlegroundTypeId WeekendHolidayIdToBGType(HolidayIds holiday); static bool IsBGWeekend(BattlegroundTypeId bgTypeId); - uint32 GetRatingDiscardTimer() const; + uint32 GetMaxRatingDifference() const; + uint32 GetRatingDiscardTimer() const; void InitAutomaticArenaPointDistribution(); void LoadBattleMastersEntry(); void CheckBattleMasters(); + BattlegroundTypeId GetBattleMasterBG(uint32 entry) const { BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry); @@ -117,8 +132,6 @@ public: return BATTLEGROUND_TYPE_NONE; } - const BattlegroundContainer& GetBattlegroundList() { return m_Battlegrounds; } // pussywizard - static std::unordered_map bgToQueue; // BattlegroundTypeId -> BattlegroundQueueTypeId static std::unordered_map queueToBg; // BattlegroundQueueTypeId -> BattlegroundTypeId static std::unordered_map bgtypeToBattleground; // BattlegroundTypeId -> Battleground* @@ -128,48 +141,46 @@ public: static std::unordered_map ArenaTypeToQueue; // ArenaType -> BattlegroundQueueTypeId static std::unordered_map QueueToArenaType; // BattlegroundQueueTypeId -> ArenaType - void DoForAllBattlegrounds(std::function const& worker); - private: - bool CreateBattleground(CreateBattlegroundData& data); - uint32 GetNextClientVisibleInstanceId(); + bool CreateBattleground(BattlegroundTemplate const* bgTemplate); + uint32 CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id); BattlegroundTypeId GetRandomBG(BattlegroundTypeId id, uint32 minLevel); - typedef std::map BattlegroundTemplateContainer; - BattlegroundTemplateContainer m_BattlegroundTemplates; - BattlegroundContainer m_Battlegrounds; + typedef std::map BattlegroundDataContainer; + BattlegroundDataContainer bgDataStore; BattlegroundQueue m_BattlegroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; - std::vector m_ArenaQueueUpdateScheduler; + std::vector m_QueueUpdateScheduler; bool m_ArenaTesting; bool m_Testing; - uint32 m_lastClientVisibleInstanceId; Seconds m_NextAutoDistributionTime; uint32 m_AutoDistributionTimeChecker; uint32 m_NextPeriodicQueueUpdateTime; BattleMastersMap mBattleMastersMap; - CreateBattlegroundData const* GetBattlegroundTemplateByTypeId(BattlegroundTypeId id) + BattlegroundTemplate const* GetBattlegroundTemplateByTypeId(BattlegroundTypeId id) { - BattlegroundTemplateMap::const_iterator itr = _battlegroundTemplates.find(id); + auto const& itr = _battlegroundTemplates.find(id); if (itr != _battlegroundTemplates.end()) return &itr->second; + return nullptr; } - CreateBattlegroundData const* GetBattlegroundTemplateByMapId(uint32 mapId) + BattlegroundTemplate const* GetBattlegroundTemplateByMapId(uint32 mapId) { - BattlegroundMapTemplateContainer::const_iterator itr = _battlegroundMapTemplates.find(mapId); + auto const& itr = _battlegroundMapTemplates.find(mapId); if (itr != _battlegroundMapTemplates.end()) return itr->second; + return nullptr; } typedef std::map BattlegroundSelectionWeightMap; - typedef std::map BattlegroundTemplateMap; - typedef std::map BattlegroundMapTemplateContainer; + typedef std::map BattlegroundTemplateMap; + typedef std::map BattlegroundMapTemplateContainer; BattlegroundTemplateMap _battlegroundTemplates; BattlegroundMapTemplateContainer _battlegroundMapTemplates; }; diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index d8bab21c6..2e1ea708f 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -35,7 +35,7 @@ /*** BATTLEGROUND QUEUE SYSTEM ***/ /*********************************************************/ -BattlegroundQueue::BattlegroundQueue() : m_bgTypeId(BATTLEGROUND_TYPE_NONE), m_arenaType(ArenaType(0)) +BattlegroundQueue::BattlegroundQueue() { for (uint32 i = 0; i < PVP_TEAMS_COUNT; ++i) { @@ -128,23 +128,25 @@ bool BattlegroundQueue::SelectionPool::AddGroup(GroupQueueInfo* ginfo, uint32 de /*********************************************************/ // add group or player (grp == nullptr) to bg queue with the given leader and bg specifications -GroupQueueInfo* BattlegroundQueue::AddGroup(Player* leader, Group* grp, PvPDifficultyEntry const* bracketEntry, bool isRated, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating, uint32 arenateamid) +GroupQueueInfo* BattlegroundQueue::AddGroup(Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated, bool isPremade, + uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId /*= 0*/, uint32 opponentsArenaTeamId /*= 0*/) { BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); // create new ginfo auto* ginfo = new GroupQueueInfo; - ginfo->BgTypeId = m_bgTypeId; - ginfo->ArenaType = m_arenaType; - ginfo->ArenaTeamId = arenateamid; + ginfo->BgTypeId = bgTypeId; + ginfo->ArenaType = arenaType; + ginfo->ArenaTeamId = arenaTeamId; ginfo->IsRated = isRated; ginfo->IsInvitedToBGInstanceGUID = 0; ginfo->JoinTime = GameTime::GetGameTimeMS().count(); ginfo->RemoveInviteTime = 0; ginfo->teamId = leader->GetTeamId(); ginfo->RealTeamID = leader->GetTeamId(true); - ginfo->ArenaTeamRating = ArenaRating; - ginfo->ArenaMatchmakerRating = MatchmakerRating; + ginfo->ArenaTeamRating = arenaRating; + ginfo->ArenaMatchmakerRating = matchmakerRating; + ginfo->PreviousOpponentsTeamId = opponentsArenaTeamId; ginfo->OpponentsTeamRating = 0; ginfo->OpponentsMatchmakerRating = 0; @@ -159,33 +161,30 @@ GroupQueueInfo* BattlegroundQueue::AddGroup(Player* leader, Group* grp, PvPDiffi if (ginfo->teamId == TEAM_HORDE) index++; - sScriptMgr->OnAddGroup(this, ginfo, index, leader, grp, bracketEntry, isPremade); + sScriptMgr->OnAddGroup(this, ginfo, index, leader, group, bgTypeId, bracketEntry, + arenaType, isRated, isPremade, arenaRating, matchmakerRating, arenaTeamId, opponentsArenaTeamId); - LOG_DEBUG("bg.battleground", "Adding Group to BattlegroundQueue bgTypeId: {}, bracket_id: {}, index: {}", m_bgTypeId, bracketId, index); + LOG_DEBUG("bg.battleground", "Adding Group to BattlegroundQueue bgTypeId: {}, bracket_id: {}, index: {}", bgTypeId, bracketId, index); // pussywizard: store indices at which GroupQueueInfo is in m_QueuedGroups - ginfo->_bracketId = bracketId; - ginfo->_groupType = index; + ginfo->BracketId = bracketId; + ginfo->GroupType = index; //add players from group to ginfo - if (grp) + if (group) { - for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next()) + group->DoForAllMembers([this, ginfo](Player* member) { - Player* member = itr->GetSource(); - if (!member) - continue; - ASSERT(m_QueuedPlayers.count(member->GetGUID()) == 0); m_QueuedPlayers[member->GetGUID()] = ginfo; - ginfo->Players.insert(member->GetGUID()); - } + ginfo->Players.emplace(member->GetGUID()); + }); } else { ASSERT(m_QueuedPlayers.count(leader->GetGUID()) == 0); m_QueuedPlayers[leader->GetGUID()] = ginfo; - ginfo->Players.insert(leader->GetGUID()); + ginfo->Players.emplace(leader->GetGUID()); } //add GroupInfo to m_QueuedGroups @@ -220,10 +219,10 @@ void BattlegroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* g return; // pointer to last index - uint32* lastIndex = &m_WaitTimeLastIndex[team_index][ginfo->_bracketId]; + uint32* lastIndex = &m_WaitTimeLastIndex[team_index][ginfo->BracketId]; // set time at index to new value - m_WaitTimes[team_index][ginfo->_bracketId][*lastIndex] = timeInQueue; + m_WaitTimes[team_index][ginfo->BracketId][*lastIndex] = timeInQueue; // set last index to next one (*lastIndex)++; @@ -244,11 +243,11 @@ uint32 BattlegroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo) const return 0; // if there are enough values: - if (m_WaitTimes[team_index][ginfo->_bracketId][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) + if (m_WaitTimes[team_index][ginfo->BracketId][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1]) { uint32 sum = 0; for (uint32 i = 0; i < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; ++i) - sum += m_WaitTimes[team_index][ginfo->_bracketId][i]; + sum += m_WaitTimes[team_index][ginfo->BracketId][i]; return sum / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; } else @@ -256,56 +255,59 @@ uint32 BattlegroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo) const } //remove player from queue and from group info, if group info is empty then remove it too -void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool sentToBg, uint32 playerQueueSlot) +void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount) { - // pussywizard: leave queue packet - if (playerQueueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) - if (Player* p = ObjectAccessor::FindConnectedPlayer(guid)) - { - WorldPacket data; - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, nullptr, playerQueueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); - p->GetSession()->SendPacket(&data); - } - //remove player from map, if he's there - auto itr = m_QueuedPlayers.find(guid); + auto const& itr = m_QueuedPlayers.find(guid); if (itr == m_QueuedPlayers.end()) { - ABORT(); + //This happens if a player logs out while in a bg because WorldSession::LogoutPlayer() notifies the bg twice + std::string playerName = "Unknown"; + + if (Player* player = ObjectAccessor::FindPlayer(guid)) + { + playerName = player->GetName(); + } + + LOG_ERROR("bg.battleground", "BattlegroundQueue: couldn't find player {} ({})", playerName, guid.ToString()); + //ABORT("BattlegroundQueue: couldn't find player {} ({})", playerName, guid.ToString()); return; } GroupQueueInfo* groupInfo = itr->second; - uint32 _bracketId = groupInfo->_bracketId; - uint32 _groupType = groupInfo->_groupType; + uint32 _bracketId = groupInfo->BracketId; + uint32 _groupType = groupInfo->GroupType; // find iterator auto group_itr = m_QueuedGroups[_bracketId][_groupType].end(); - for (auto k = m_QueuedGroups[_bracketId][_groupType].begin(); k != m_QueuedGroups[_bracketId][_groupType].end(); ++k) + + for (auto k = m_QueuedGroups[_bracketId][_groupType].begin(); k != m_QueuedGroups[_bracketId][_groupType].end(); k++) if ((*k) == groupInfo) { group_itr = k; break; } - //player can't be in queue without group, but just in case + // player can't be in queue without group, but just in case if (group_itr == m_QueuedGroups[_bracketId][_groupType].end()) { - ABORT(); + LOG_ERROR("bg.battleground", "BattlegroundQueue: ERROR Cannot find groupinfo for {}", guid.ToString()); + //ABORT("BattlegroundQueue: ERROR Cannot find groupinfo for {}", guid.ToString()); return; } + LOG_DEBUG("bg.battleground", "BattlegroundQueue: Removing {}, from bracket_id {}", guid.ToString(), _bracketId); + // remove player from group queue info - auto pitr = groupInfo->Players.find(guid); + auto const& pitr = groupInfo->Players.find(guid); ASSERT(pitr != groupInfo->Players.end()); if (pitr != groupInfo->Players.end()) groupInfo->Players.erase(pitr); - // if invited to bg, then always decrease invited count when removed from queue - // sending player to bg will increase it again - if (groupInfo->IsInvitedToBGInstanceGUID) - if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID)) + // if invited to bg, and should decrease invited count, then do it + if (decreaseInvitedCount && groupInfo->IsInvitedToBGInstanceGUID) + if (Battleground* bg = sBattlegroundMgr->GetBattleground(groupInfo->IsInvitedToBGInstanceGUID, groupInfo->BgTypeId)) bg->DecreaseInvitedCount(groupInfo->teamId); // remove player queue info @@ -315,13 +317,20 @@ void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool sentToBg, uint32 play SendExitMessageArenaQueue(groupInfo); // if player leaves queue and he is invited to a rated arena match, then count it as he lost - if (groupInfo->IsInvitedToBGInstanceGUID && groupInfo->IsRated && !sentToBg) + if (groupInfo->IsInvitedToBGInstanceGUID && groupInfo->IsRated && decreaseInvitedCount) + { if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(groupInfo->ArenaTeamId)) { + LOG_DEBUG("bg.battleground", "UPDATING memberLost's personal arena rating for {} by opponents rating: {}", guid.ToString(), groupInfo->OpponentsTeamRating); + if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) + { at->MemberLost(player, groupInfo->OpponentsMatchmakerRating); + } + at->SaveToDB(); } + } // remove group queue info no players left if (groupInfo->Players.empty()) @@ -335,17 +344,20 @@ void BattlegroundQueue::RemovePlayer(ObjectGuid guid, bool sentToBg, uint32 play // if it's a rated arena and any member leaves when group not yet invited - everyone from group leaves too! if (groupInfo->IsRated && !groupInfo->IsInvitedToBGInstanceGUID) { - uint32 queueSlot = PLAYER_MAX_BATTLEGROUND_QUEUES; - if (Player* plr = ObjectAccessor::FindConnectedPlayer(*(groupInfo->Players.begin()))) { + Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(groupInfo->BgTypeId); BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(groupInfo->BgTypeId, groupInfo->ArenaType); - queueSlot = plr->GetBattlegroundQueueIndex(bgQueueTypeId); + uint32 queueSlot = plr->GetBattlegroundQueueIndex(bgQueueTypeId); plr->RemoveBattlegroundQueueId(bgQueueTypeId); + + WorldPacket data; + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); + plr->SendDirectMessage(&data); } // recursive call - RemovePlayer(*(groupInfo->Players.begin()), false, queueSlot); + RemovePlayer(*groupInfo->Players.begin(), decreaseInvitedCount); } } @@ -377,79 +389,116 @@ bool BattlegroundQueue::GetPlayerGroupInfoData(ObjectGuid guid, GroupQueueInfo* } // this function is filling pools given free slots on both sides, result is ballanced -void BattlegroundQueue::FillPlayersToBG(Battleground* bg, const int32 aliFree, const int32 hordeFree, BattlegroundBracketId bracket_id) +void BattlegroundQueue::FillPlayersToBG(Battleground* bg, BattlegroundBracketId bracket_id) { - if (!sScriptMgr->CanFillPlayersToBG(this, bg, aliFree, hordeFree, bracket_id)) - return; - - // clear selection pools - m_SelectionPools[TEAM_ALLIANCE].Init(); - m_SelectionPools[TEAM_HORDE].Init(); - - // quick check if nothing we can do: - if (!sBattlegroundMgr->isTesting()) - if ((aliFree > hordeFree && m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].empty()) || - (hordeFree > aliFree && m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].empty())) - return; - - // ally: at first fill as much as possible - auto Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); - for (; Ali_itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].end() && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); ++Ali_itr); - - // horde: at first fill as much as possible - auto Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); - for (; Horde_itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].end() && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); ++Horde_itr); - - // calculate free space after adding - int32 aliDiff = aliFree - int32(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()); - int32 hordeDiff = hordeFree - int32(m_SelectionPools[TEAM_HORDE].GetPlayerCount()); - - int32 invType = sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE); - int32 invDiff = 0; - - // check balance configuration and set the max difference between teams - switch (invType) + if (!sScriptMgr->CanFillPlayersToBG(this, bg, bracket_id)) { - case BG_QUEUE_INVITATION_TYPE_NO_BALANCE: - return; - case BG_QUEUE_INVITATION_TYPE_BALANCED: - invDiff = 1; - break; - case BG_QUEUE_INVITATION_TYPE_EVEN: - break; - default: - return; + return; } - // balance the teams based on the difference allowed - while (std::abs(aliDiff - hordeDiff) > invDiff && (m_SelectionPools[TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() > 0)) - { - // if results in more alliance players than horde: - if (aliDiff < hordeDiff) - { - // no more alliance in pool, invite whatever we can from horde - if (!m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()) - break; + int32 hordeFree = bg->GetFreeSlotsForTeam(TEAM_HORDE); + int32 aliFree = bg->GetFreeSlotsForTeam(TEAM_ALLIANCE); + uint32 aliCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + uint32 hordeCount = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].size(); - // kick alliance, returns true if kicked more than needed, so then try to fill up - if (m_SelectionPools[TEAM_ALLIANCE].KickGroup(hordeDiff - aliDiff)) - for (; Ali_itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].end() && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree >= hordeDiff ? aliFree - hordeDiff : 0); ++Ali_itr); + // try to get even teams + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_EVEN) + { + // check if the teams are even + if (hordeFree == 1 && aliFree == 1) + { + // if we are here, the teams have the same amount of players + // then we have to allow to join the same amount of players + int32 hordeExtra = hordeCount - aliCount; + int32 aliExtra = aliCount - hordeCount; + + hordeExtra = std::max(hordeExtra, 0); + aliExtra = std::max(aliExtra, 0); + + if (aliCount != hordeCount) + { + aliFree -= aliExtra; + hordeFree -= hordeExtra; + + aliFree = std::max(aliFree, 0); + hordeFree = std::max(hordeFree, 0); + } + } + } + + GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); + //count of groups in queue - used to stop cycles + + //index to queue which group is current + uint32 aliIndex = 0; + for (; aliIndex < aliCount && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) + ++Ali_itr; + + //the same thing for horde + GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].begin(); + + uint32 hordeIndex = 0; + for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++) + ++Horde_itr; + + //if ofc like BG queue invitation is set in config, then we are happy + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == BG_QUEUE_INVITATION_TYPE_NO_BALANCE) + return; + + /* + if we reached this code, then we have to solve NP - complete problem called Subset sum problem + So one solution is to check all possible invitation subgroups, or we can use these conditions: + 1. Last time when BattlegroundQueue::Update was executed we invited all possible players - so there is only small possibility + that we will invite now whole queue, because only 1 change has been made to queues from the last BattlegroundQueue::Update call + 2. Other thing we should consider is group order in queue + */ + + // At first we need to compare free space in bg and our selection pool + int32 diffAli = aliFree - int32(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()); + int32 diffHorde = hordeFree - int32(m_SelectionPools[TEAM_HORDE].GetPlayerCount()); + + while (std::abs(diffAli - diffHorde) > 1 && (m_SelectionPools[TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() > 0)) + { + //each cycle execution we need to kick at least 1 group + if (diffAli < diffHorde) + { + //kick alliance group, add to pool new group if needed + if (m_SelectionPools[TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) + { + for (; aliIndex < aliCount && m_SelectionPools[TEAM_ALLIANCE].AddGroup((*Ali_itr), (aliFree >= diffHorde) ? aliFree - diffHorde : 0); aliIndex++) + ++Ali_itr; + } + + //if ali selection is already empty, then kick horde group, but if there are less horde than ali in bg - break; + if (!m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()) + { + if (aliFree <= diffHorde + 1) + break; + + m_SelectionPools[TEAM_HORDE].KickGroup(diffHorde - diffAli); + } } - // if results in more horde players than alliance: else { - // no more horde in pool, invite whatever we can from alliance - if (!m_SelectionPools[TEAM_HORDE].GetPlayerCount()) - break; + //kick horde group, add to pool new group if needed + if (m_SelectionPools[TEAM_HORDE].KickGroup(diffAli - diffHorde)) + { + for (; hordeIndex < hordeCount && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) + ++Horde_itr; + } - // kick horde, returns true if kicked more than needed, so then try to fill up - if (m_SelectionPools[TEAM_HORDE].KickGroup(aliDiff - hordeDiff)) - for (; Horde_itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_HORDE].end() && m_SelectionPools[TEAM_HORDE].AddGroup((*Horde_itr), hordeFree >= aliDiff ? hordeFree - aliDiff : 0); ++Horde_itr); + if (!m_SelectionPools[TEAM_HORDE].GetPlayerCount()) + { + if (hordeFree <= diffAli + 1) + break; + + m_SelectionPools[TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); + } } - // recalculate free space after adding - aliDiff = aliFree - static_cast(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()); - hordeDiff = hordeFree - static_cast(m_SelectionPools[TEAM_HORDE].GetPlayerCount()); + //count diffs after small update + diffAli = aliFree - int32(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()); + diffHorde = hordeFree - int32(m_SelectionPools[TEAM_HORDE].GetPlayerCount()); } } @@ -457,136 +506,191 @@ void BattlegroundQueue::FillPlayersToBG(Battleground* bg, const int32 aliFree, c // then after 30 mins (default) in queue it moves premade group to normal queue bool BattlegroundQueue::CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) { - // clear selection pools - m_SelectionPools[TEAM_ALLIANCE].Init(); - m_SelectionPools[TEAM_HORDE].Init(); - if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) { - // find premade group for both factions: + //start premade match + //if groups aren't invited GroupsQueueType::const_iterator ali_group, horde_group; for (ali_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group) - if (!(*ali_group)->IsInvitedToBGInstanceGUID && (*ali_group)->Players.size() >= MinPlayersPerTeam) + if (!(*ali_group)->IsInvitedToBGInstanceGUID) break; + for (horde_group = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group) - if (!(*horde_group)->IsInvitedToBGInstanceGUID && (*horde_group)->Players.size() >= MinPlayersPerTeam) + if (!(*horde_group)->IsInvitedToBGInstanceGUID) break; // if found both groups if (ali_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].end()) { - // add premade groups to selection pools m_SelectionPools[TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); m_SelectionPools[TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); - // battleground will be immediately filled (after calling this function and creating new battleground) with more players from normal queue + //add groups/players from normal queue to size of bigger group + uint32 maxPlayers = std::min(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[TEAM_HORDE].GetPlayerCount()); + GroupsQueueType::const_iterator itr; - return m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= MinPlayersPerTeam && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= MinPlayersPerTeam; + for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) + { + for (itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) + { + //if itr can join BG and player count is less that maxPlayers, then add group to selectionpool + if (!(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers)) + break; + } + } + + //premade selection pools are set + return true; } } - // now check if we can move groups from premade queue to normal queue - // this happens if timer has expired or group size lowered - - uint32 premade_time = sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); - uint32 time_before = GameTime::GetGameTimeMS().count() >= premade_time ? GameTime::GetGameTimeMS().count() - premade_time : 0; + // now check if we can move group from Premade queue to normal queue (timer has expired) or group size lowered!! + // this could be 2 cycles but i'm checking only first team in queue - it can cause problem - + // if first is invited to BG and seconds timer expired, but we can ignore it, because players have only 80 seconds to click to enter bg + // and when they click or after 80 seconds the queue info is removed from queue + uint32 time_before = GameTime::GetGameTimeMS().count() - sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) + { if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) - for (auto itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].end(); ) + { + GroupsQueueType::iterator itr = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); + if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) { - if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) - { - (*itr)->_groupType = BG_QUEUE_NORMAL_ALLIANCE + i; // pussywizard: update GroupQueueInfo internal variable - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr++); - continue; - } - ++itr; + //we must insert group to normal queue and erase pointer from premade queue + (*itr)->GroupType = BG_QUEUE_NORMAL_ALLIANCE + i; // pussywizard: update GroupQueueInfo internal variable + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); } + } + } + //selection pools are not set return false; } // this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam bool BattlegroundQueue::CheckNormalMatch(Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) { - uint32 Coef = 1; + if (sScriptMgr->IsCheckNormalMatch(this, bgTemplate, bracket_id, minPlayers, maxPlayers)) + { + return true; + } - sScriptMgr->OnCheckNormalMatch(this, Coef, bgTemplate, bracket_id, minPlayers, maxPlayers); + GroupsQueueType::const_iterator itr_team[PVP_TEAMS_COUNT]; + for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) + { + itr_team[i] = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); + for (; itr_team[i] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) + { + if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID) + { + m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers); + if (m_SelectionPools[i].GetPlayerCount() >= minPlayers) + break; + } + } + } - minPlayers = minPlayers * Coef; + //try to invite same number of players - this cycle may cause longer wait time even if there are enough players in queue, but we want ballanced bg + uint32 j = TEAM_ALLIANCE; + if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() < m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()) + j = TEAM_HORDE; - FillPlayersToBG(bgTemplate, maxPlayers, maxPlayers, bracket_id); + if (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != BG_QUEUE_INVITATION_TYPE_NO_BALANCE + && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers) + { + //we will try to invite more groups to team with less players indexed by j + ++(itr_team[j]); //this will not cause a crash, because for cycle above reached break; + for (; itr_team[j] != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) + { + if (!(*(itr_team[j]))->IsInvitedToBGInstanceGUID) + if (!m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % PVP_TEAMS_COUNT].GetPlayerCount())) + break; + } + + // do not allow to start bg with more than 2 players more on 1 faction + if (std::abs((int32)(m_SelectionPools[TEAM_HORDE].GetPlayerCount() - m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount())) > 2) + return false; + } //allow 1v0 if debug bg if (sBattlegroundMgr->isTesting() && bgTemplate->isBattleground() && (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[TEAM_HORDE].GetPlayerCount())) return true; - switch (sWorld->getIntConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE)) - { - case BG_QUEUE_INVITATION_TYPE_NO_BALANCE: // in this case, as soon as both teams have > mincount, start - return m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers; - - case BG_QUEUE_INVITATION_TYPE_BALANCED: // check difference between selection pools - if = 1 or less start. - return std::abs(static_cast(m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount()) - static_cast(m_SelectionPools[TEAM_HORDE].GetPlayerCount())) <= 1 && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers; - - case BG_QUEUE_INVITATION_TYPE_EVEN: // if both counts are same then it's an even match - return (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() == m_SelectionPools[TEAM_HORDE].GetPlayerCount()) && m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers; - - default: // same as unbalanced (in case wrong setting is entered...) - return m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers; - } + //return true if there are enough players in selection pools - enable to work .debug bg command correctly + return m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayers; } // this method will check if we can invite players to same faction skirmish match bool BattlegroundQueue::CheckSkirmishForSameFaction(BattlegroundBracketId bracket_id, uint32 minPlayersPerTeam) { - for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) - if (!m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].empty()) + if (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + return false; + + TeamId teamIndex = TEAM_ALLIANCE; + TeamId otherTeam = TEAM_HORDE; + + if (m_SelectionPools[TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam) + { + teamIndex = TEAM_HORDE; + otherTeam = TEAM_ALLIANCE; + } + + //clear other team's selection + m_SelectionPools[otherTeam].Init(); + + //store last ginfo pointer + GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back(); + + //set itr_team to group that was added to selection pool latest + GroupsQueueType::iterator itr_team = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].begin(); + for (; itr_team != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].end(); ++itr_team) + if (ginfo == *itr_team) + break; + + if (itr_team == m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].end()) + return false; + + GroupsQueueType::iterator itr_team2 = itr_team; + ++itr_team2; + + //invite players to other selection pool + for (; itr_team2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].end(); ++itr_team2) + { + //if selection pool is full then break; + if (!(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam)) + break; + } + + if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam) + return false; + + //here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue + for (GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr) + { + //set correct team + (*itr)->teamId = otherTeam; + (*itr)->GroupType = static_cast(BG_QUEUE_NORMAL_ALLIANCE) + static_cast(otherTeam); + + //add team to other queue + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(otherTeam)].push_front(*itr); + + //remove team from old queue + GroupsQueueType::iterator itr2 = itr_team; + ++itr2; + + for (; itr2 != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].end(); ++itr2) { - // clear selection pools - m_SelectionPools[TEAM_ALLIANCE].Init(); - m_SelectionPools[TEAM_HORDE].Init(); - - // fill one queue to both selection pools - for (auto itr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) - for (uint32 j = 0; j < PVP_TEAMS_COUNT; j++) // try to add this group to both pools - if (m_SelectionPools[TEAM_ALLIANCE + j].GetPlayerCount() < minPlayersPerTeam) // if this pool is not full - if (m_SelectionPools[TEAM_ALLIANCE + j].AddGroup((*itr), minPlayersPerTeam)) // successfully added - { - // if both selection pools are full - if (m_SelectionPools[TEAM_ALLIANCE].GetPlayerCount() >= minPlayersPerTeam && m_SelectionPools[TEAM_HORDE].GetPlayerCount() >= minPlayersPerTeam) - { - // need to move groups from one pool to another queue (for another faction) - TeamId wrongTeamId = (i == 0 ? TEAM_HORDE : TEAM_ALLIANCE); - - for (auto pitr = m_SelectionPools[wrongTeamId].SelectedGroups.begin(); pitr != m_SelectionPools[wrongTeamId].SelectedGroups.end(); ++pitr) - { - // update internal GroupQueueInfo data - (*pitr)->teamId = wrongTeamId; - (*pitr)->_groupType = static_cast(BG_QUEUE_NORMAL_ALLIANCE) + wrongTeamId; - - // add GroupQueueInfo to new queue - m_QueuedGroups[bracket_id][static_cast(BG_QUEUE_NORMAL_ALLIANCE) + wrongTeamId].push_front(*pitr); - - // remove GroupQueueInfo from old queue - for (auto qitr = m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); qitr != m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++qitr) - if (*qitr == *pitr) - { - m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + i].erase(qitr); - break; - } - } - - return true; - } - - break; // added to one pool, prevent adding it to the second pool - } + if (*itr2 == *itr) + { + m_QueuedGroups[bracket_id][BG_QUEUE_NORMAL_ALLIANCE + static_cast(teamIndex)].erase(itr2); + break; + } } + } - return false; + return true; } void BattlegroundQueue::UpdateEvents(uint32 diff) @@ -594,123 +698,117 @@ void BattlegroundQueue::UpdateEvents(uint32 diff) m_events.Update(diff); } -void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundBracketId bracket_id, bool isRated, uint32 arenaRatedTeamId) +void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating) { // if no players in queue - do nothing if (IsAllQueuesEmpty(bracket_id)) return; - Battleground* bg_template = sBattlegroundMgr->GetBattlegroundTemplate(m_bgTypeId); + auto InviteAllGroupsToBg = [this](Battleground* bg) + { + // invite those selection pools + for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) + { + for (auto const& citr : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups) + { + InviteGroupToBG(citr, bg, citr->teamId); + } + } + }; + + // battleground with free slot for player should be always in the beggining of the queue + // maybe it would be better to create bgfreeslotqueue for each bracket_id + BGFreeSlotQueueContainer& bgQueues = sBattlegroundMgr->GetBGFreeSlotQueueStore(bgTypeId); + for (BGFreeSlotQueueContainer::iterator itr = bgQueues.begin(); itr != bgQueues.end();) + { + Battleground* bg = *itr; ++itr; + // DO NOT allow queue manager to invite new player to rated games + if (!bg->isRated() && bg->GetBgTypeID() == bgTypeId && bg->GetBracketId() == bracket_id && + bg->GetStatus() > STATUS_WAIT_QUEUE && bg->GetStatus() < STATUS_WAIT_LEAVE) + { + // clear selection pools + m_SelectionPools[TEAM_ALLIANCE].Init(); + m_SelectionPools[TEAM_HORDE].Init(); + + // call a function that does the job for us + FillPlayersToBG(bg, bracket_id); + + // now everything is set, invite players + InviteAllGroupsToBg(bg); + + if (!bg->HasFreeSlots()) + bg->RemoveFromBGFreeSlotQueue(); + } + } + + Battleground* bg_template = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); if (!bg_template) + { + LOG_ERROR("bg.battleground", "Battleground: Update: bg template not found for {}", bgTypeId); return; + } PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketById(bg_template->GetMapId(), bracket_id); if (!bracketEntry) - return; - - // battlegrounds with free slots should be populated first using players in queue - if (!BattlegroundMgr::IsArenaType(m_bgTypeId)) { - std::vector bgsToCheck; - - // sort from most needing (most empty) to least needing using a std::set with functor - sBattlegroundMgr->DoForAllBattlegrounds([&](Battleground* bg) - { - if (!BattlegroundMgr::IsArenaType(bg->GetBgTypeID()) && - (bg->GetBgTypeID(true) == m_bgTypeId || m_bgTypeId == BATTLEGROUND_RB) && - bg->HasFreeSlots() && bg->GetMinLevel() <= bracketEntry->minLevel && - bg->GetMaxLevel() >= bracketEntry->maxLevel) - { - bgsToCheck.emplace_back(bg); - } - }); - - std::sort(bgsToCheck.begin(), bgsToCheck.end(), [](Battleground* const& bg1, Battleground* const& bg2) - { - return ((float)bg1->GetMaxFreeSlots() / (float)bg1->GetMaxPlayersPerTeam()) > ((float)bg2->GetMaxFreeSlots() / (float)bg2->GetMaxPlayersPerTeam()); - }); - - // now iterate needing battlegrounds - for (auto const& bg : bgsToCheck) - { - // call a function that fills whatever we can from normal queues - FillPlayersToBG(bg, bg->GetFreeSlotsForTeam(TEAM_ALLIANCE), bg->GetFreeSlotsForTeam(TEAM_HORDE), bracket_id); - - // invite players - for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) - for (auto itr : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups) - InviteGroupToBG(itr, bg, itr->RealTeamID); - } - - // prevent new BGs to be created if there are some non-empty BGs running - // TODO: note that this is a workaround, - // however it shouldn't cause issues as the queue update is constantly called - if (!bg_template->isArena() && !bgsToCheck.empty()) - return; + LOG_ERROR("bg.battleground", "Battleground: Update: bg bracket entry not found for map {} bracket id {}", bg_template->GetMapId(), bracket_id); + return; } - // finished iterating through battlegrounds with free slots, maybe we need to create a new bg - // get min and max players per team uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); if (bg_template->isArena()) { - MinPlayersPerTeam = sBattlegroundMgr->isArenaTesting() ? 1 : m_arenaType; - MaxPlayersPerTeam = m_arenaType; + MinPlayersPerTeam = sBattlegroundMgr->isArenaTesting() ? 1 : arenaType; + MaxPlayersPerTeam = arenaType; } + else if (sBattlegroundMgr->isTesting()) + MinPlayersPerTeam = 1; - sScriptMgr->OnQueueUpdate(this, bracket_id, isRated, arenaRatedTeamId); + sScriptMgr->OnQueueUpdate(this, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating); + + m_SelectionPools[TEAM_ALLIANCE].Init(); + m_SelectionPools[TEAM_HORDE].Init(); // check if can start new premade battleground - if (bg_template->isBattleground() && m_bgTypeId != BATTLEGROUND_RB) - if (CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) + if (bg_template->isBattleground() && bgTypeId != BATTLEGROUND_RB && CheckPremadeMatch(bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam)) + { + // create new battleground + Battleground* bg = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, 0, false); + if (!bg) { - // create new battleground - Battleground* bg = sBattlegroundMgr->CreateNewBattleground(m_bgTypeId, bracketEntry->minLevel, bracketEntry->maxLevel, 0, false); - if (!bg) - return; - - // invite players - for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) - for (auto& SelectedGroup : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups) - InviteGroupToBG(SelectedGroup, bg, SelectedGroup->teamId); - - bg->StartBattleground(); - - // now fill the premade bg if possible (only one team for each side has been invited yet) - if (bg->HasFreeSlots()) - { - // call a function that fills whatever we can from normal queues - FillPlayersToBG(bg, bg->GetFreeSlotsForTeam(TEAM_ALLIANCE), bg->GetFreeSlotsForTeam(TEAM_HORDE), bracket_id); - - // invite players - for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) - for (auto& SelectedGroup : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups) - InviteGroupToBG(SelectedGroup, bg, SelectedGroup->teamId); - } + LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", bgTypeId); + return; } + // invite those selection pools + InviteAllGroupsToBg(bg); + + bg->StartBattleground(); + + // clear structures + m_SelectionPools[TEAM_ALLIANCE].Init(); + m_SelectionPools[TEAM_HORDE].Init(); + } + // check if can start new normal battleground or non-rated arena if (!isRated) { if (CheckNormalMatch(bg_template, bracket_id, MinPlayersPerTeam, MaxPlayersPerTeam) || - (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) + (bg_template->isArena() && CheckSkirmishForSameFaction(bracket_id, MinPlayersPerTeam))) { - BattlegroundTypeId newBgTypeId = m_bgTypeId; - uint32 minLvl = bracketEntry->minLevel; - uint32 maxLvl = bracketEntry->maxLevel; - // create new battleground - Battleground* bg = sBattlegroundMgr->CreateNewBattleground(newBgTypeId, minLvl, maxLvl, m_arenaType, false); + Battleground* bg = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, arenaType, false); if (!bg) + { + LOG_ERROR("bg.battleground", "BattlegroundQueue::Update - Cannot create battleground: {}", bgTypeId); return; + } // invite players - for (uint32 i = 0; i < PVP_TEAMS_COUNT; i++) - for (auto& SelectedGroup : m_SelectionPools[TEAM_ALLIANCE + i].SelectedGroups) - InviteGroupToBG(SelectedGroup, bg, SelectedGroup->teamId); + InviteAllGroupsToBg(bg); bg->StartBattleground(); } @@ -718,163 +816,134 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundBracket // check if can start new rated arenas (can create many in single queue update) else if (bg_template->isArena()) { - // pussywizard: everything inside this section is mine, do NOT destroy! + // found out the minimum and maximum ratings the newly added team should battle against + // arenaRating is the rating of the latest joined team, or 0 + // 0 is on (automatic update call) and we must set it to team's with longest wait time + if (!arenaRating) + { + GroupQueueInfo* front1 = nullptr; + GroupQueueInfo* front2 = nullptr; - const uint32 currMSTime = GameTime::GetGameTimeMS().count(); - const uint32 discardTime = sBattlegroundMgr->GetRatingDiscardTimer(); - const uint32 maxDefaultRatingDifference = (MaxPlayersPerTeam > 2 ? 300 : 200); - const uint32 maxCountedMMR = 2500; + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty()) + { + front1 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].front(); + arenaRating = front1->ArenaMatchmakerRating; + } + + if (!m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty()) + { + front2 = m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].front(); + arenaRating = front2->ArenaMatchmakerRating; + } + + if (front1 && front2) + { + if (front1->JoinTime < front2->JoinTime) + arenaRating = front1->ArenaMatchmakerRating; + } + else if (!front1 && !front2) + return; // queues are empty + } + + //set rating range + uint32 arenaMinRating = (arenaRating <= sBattlegroundMgr->GetMaxRatingDifference()) ? 0 : arenaRating - sBattlegroundMgr->GetMaxRatingDifference(); + uint32 arenaMaxRating = arenaRating + sBattlegroundMgr->GetMaxRatingDifference(); + + // if max rating difference is set and the time past since server startup is greater than the rating discard time + // (after what time the ratings aren't taken into account when making teams) then + // the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account + // else leave the discard time on 0, this way all ratings will be discarded + // this has to be signed value - when the server starts, this value would be negative and thus overflow + int32 discardTime = GameTime::GetGameTimeMS().count() - sBattlegroundMgr->GetRatingDiscardTimer(); + + // timer for previous opponents + int32 discardOpponentsTime = GameTime::GetGameTimeMS().count() - sWorld->getIntConfig(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER); // we need to find 2 teams which will play next game GroupsQueueType::iterator itr_teams[PVP_TEAMS_COUNT]; + uint8 found = 0; + uint8 team = 0; - bool increaseItr = true; - bool reverse1 = urand(0, 1) != 0; - for (uint8 ii = BG_QUEUE_PREMADE_ALLIANCE; ii <= BG_QUEUE_PREMADE_HORDE; ii++) + for (uint8 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) { - uint8 i = reverse1 ? (BG_QUEUE_PREMADE_HORDE - ii) : ii; - for (auto itr = m_QueuedGroups[bracket_id][i].begin(); itr != m_QueuedGroups[bracket_id][i].end(); (increaseItr ? ++itr : itr)) + // take the group that joined first + GroupsQueueType::iterator itr2 = m_QueuedGroups[bracket_id][i].begin(); + for (; itr2 != m_QueuedGroups[bracket_id][i].end(); ++itr2) { - increaseItr = true; - - // if arenaRatedTeamId is set - look for oponents only for one team, if not - pair every possible team - if (arenaRatedTeamId != 0 && arenaRatedTeamId != (*itr)->ArenaTeamId) - continue; - if ((*itr)->IsInvitedToBGInstanceGUID) - continue; - - uint32 MMR1 = std::min((*itr)->ArenaMatchmakerRating, maxCountedMMR); - - GroupsQueueType::iterator oponentItr; - uint8 oponentQueue = BG_QUEUE_MAX; - uint32 minOponentMMRDiff = 0xffffffff; - uint8 oponentValid = 0; - - bool reverse2 = urand(0, 1) != 0; - for (uint8 jj = BG_QUEUE_PREMADE_ALLIANCE; jj <= BG_QUEUE_PREMADE_HORDE; jj++) + // if group match conditions, then add it to pool + if (!(*itr2)->IsInvitedToBGInstanceGUID + && (((*itr2)->ArenaMatchmakerRating >= arenaMinRating && (*itr2)->ArenaMatchmakerRating <= arenaMaxRating) + || (int32)(*itr2)->JoinTime < discardTime)) { - uint8 j = reverse2 ? (BG_QUEUE_PREMADE_HORDE - jj) : jj; - bool brk = false; - for (auto itr2 = m_QueuedGroups[bracket_id][j].begin(); itr2 != m_QueuedGroups[bracket_id][j].end(); ++itr2) - { - if ((*itr)->ArenaTeamId == (*itr2)->ArenaTeamId) - continue; - if ((*itr2)->IsInvitedToBGInstanceGUID) - continue; - uint32 MMR2 = std::min((*itr2)->ArenaMatchmakerRating, maxCountedMMR); - uint32 MMRDiff = (MMR2 >= MMR1 ? MMR2 - MMR1 : MMR1 - MMR2); - - uint32 maxAllowedDiff = maxDefaultRatingDifference; - uint32 shorterWaitTime, longerWaitTime; - if (currMSTime - (*itr)->JoinTime <= currMSTime - (*itr2)->JoinTime) - { - shorterWaitTime = currMSTime - (*itr)->JoinTime; - longerWaitTime = currMSTime - (*itr2)->JoinTime; - } - else - { - shorterWaitTime = currMSTime - (*itr2)->JoinTime; - longerWaitTime = currMSTime - (*itr)->JoinTime; - } - if (longerWaitTime >= discardTime) - maxAllowedDiff += 150; - maxAllowedDiff += shorterWaitTime / 600; // increased by 100 for each minute - - // now check if this team is more appropriate than previous ones: - - if (currMSTime - (*itr)->JoinTime >= 20 * MINUTE * IN_MILLISECONDS && (oponentValid < 3 || MMRDiff < minOponentMMRDiff)) // after 20 minutes of waiting, pair with closest mmr, regardless the difference - { - oponentValid = 3; - minOponentMMRDiff = MMRDiff; - oponentItr = itr2; - oponentQueue = j; - } - else if (MMR1 >= 2000 && MMR2 >= 2000 && longerWaitTime >= 2 * discardTime && (oponentValid < 2 || MMRDiff < minOponentMMRDiff)) // after 6 minutes of waiting, pair any 2000+ vs 2000+ - { - oponentValid = 2; - minOponentMMRDiff = MMRDiff; - oponentItr = itr2; - oponentQueue = j; - } - else if (oponentValid < 2 && MMRDiff < minOponentMMRDiff) - { - if (!oponentValid) - { - minOponentMMRDiff = MMRDiff; - oponentItr = itr2; - oponentQueue = j; - if (MMRDiff <= maxAllowedDiff) - oponentValid = 1; - } - if ((MMR1 < 1800 || MMR2 < 1800) && MaxPlayersPerTeam == 2 && MMRDiff <= maxDefaultRatingDifference) // in 2v2 below 1800 mmr - priority for default allowed difference - { - minOponentMMRDiff = MMRDiff; - oponentItr = itr2; - oponentQueue = j; - brk = true; - break; - } - } - } - if (brk) - break; - } - - if (oponentQueue != BG_QUEUE_MAX) - { - if (oponentValid) - { - itr_teams[i] = itr; - itr_teams[i == 0 ? 1 : 0] = oponentItr; - - { - GroupQueueInfo* aTeam = *itr_teams[TEAM_ALLIANCE]; - GroupQueueInfo* hTeam = *itr_teams[TEAM_HORDE]; - Battleground* arena = sBattlegroundMgr->CreateNewBattleground(m_bgTypeId, bracketEntry->minLevel, bracketEntry->maxLevel, m_arenaType, true); - if (!arena) - return; - - aTeam->OpponentsTeamRating = hTeam->ArenaTeamRating; - hTeam->OpponentsTeamRating = aTeam->ArenaTeamRating; - aTeam->OpponentsMatchmakerRating = hTeam->ArenaMatchmakerRating; - hTeam->OpponentsMatchmakerRating = aTeam->ArenaMatchmakerRating; - - // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer - if (aTeam->teamId != TEAM_ALLIANCE) - { - aTeam->_groupType = BG_QUEUE_PREMADE_ALLIANCE; - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(aTeam); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_teams[TEAM_ALLIANCE]); - increaseItr = false; - itr = m_QueuedGroups[bracket_id][i].begin(); - } - if (hTeam->teamId != TEAM_HORDE) - { - hTeam->_groupType = BG_QUEUE_PREMADE_HORDE; - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(hTeam); - m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_teams[TEAM_HORDE]); - increaseItr = false; - itr = m_QueuedGroups[bracket_id][i].begin(); - } - - arena->SetArenaMatchmakerRating(TEAM_ALLIANCE, aTeam->ArenaMatchmakerRating); - arena->SetArenaMatchmakerRating(TEAM_HORDE, hTeam->ArenaMatchmakerRating); - InviteGroupToBG(aTeam, arena, TEAM_ALLIANCE); - InviteGroupToBG(hTeam, arena, TEAM_HORDE); - - arena->StartBattleground(); - } - - if (arenaRatedTeamId) - return; - else - continue; - } - else if (arenaRatedTeamId) - return; + itr_teams[found++] = itr2; + team = i; + break; } } } + + if (!found) + return; + + if (found == 1) + { + for (GroupsQueueType::iterator itr3 = itr_teams[0]; itr3 != m_QueuedGroups[bracket_id][team].end(); ++itr3) + { + if (!(*itr3)->IsInvitedToBGInstanceGUID + && (((*itr3)->ArenaMatchmakerRating >= arenaMinRating && (*itr3)->ArenaMatchmakerRating <= arenaMaxRating) || (int32)(*itr3)->JoinTime < discardTime) + && ((*itr_teams[0])->ArenaTeamId != (*itr3)->PreviousOpponentsTeamId || ((int32)(*itr3)->JoinTime < discardOpponentsTime)) + && (*itr_teams[0])->ArenaTeamId != (*itr3)->ArenaTeamId) + { + itr_teams[found++] = itr3; + break; + } + } + } + + //if we have 2 teams, then start new arena and invite players! + if (found == 2) + { + GroupQueueInfo* aTeam = *itr_teams[TEAM_ALLIANCE]; + GroupQueueInfo* hTeam = *itr_teams[TEAM_HORDE]; + + Battleground* arena = sBattlegroundMgr->CreateNewBattleground(bgTypeId, bracketEntry, arenaType, true); + if (!arena) + { + LOG_ERROR("bg.battleground", "BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); + return; + } + + aTeam->OpponentsTeamRating = hTeam->ArenaTeamRating; + hTeam->OpponentsTeamRating = aTeam->ArenaTeamRating; + aTeam->OpponentsMatchmakerRating = hTeam->ArenaMatchmakerRating; + hTeam->OpponentsMatchmakerRating = aTeam->ArenaMatchmakerRating; + + LOG_DEBUG("bg.battleground", "setting oposite teamrating for team {} to {}", aTeam->ArenaTeamId, aTeam->OpponentsTeamRating); + LOG_DEBUG("bg.battleground", "setting oposite teamrating for team {} to {}", hTeam->ArenaTeamId, hTeam->OpponentsTeamRating); + + // now we must move team if we changed its faction to another faction queue, because then we will spam log by errors in Queue::RemovePlayer + if (aTeam->teamId != TEAM_ALLIANCE) + { + aTeam->GroupType = BG_QUEUE_PREMADE_ALLIANCE; + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(aTeam); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].erase(itr_teams[TEAM_ALLIANCE]); + } + + if (hTeam->teamId != TEAM_HORDE) + { + hTeam->GroupType = BG_QUEUE_PREMADE_HORDE; + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].push_front(hTeam); + m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_teams[TEAM_HORDE]); + } + + arena->SetArenaMatchmakerRating(TEAM_ALLIANCE, aTeam->ArenaMatchmakerRating); + arena->SetArenaMatchmakerRating(TEAM_HORDE, hTeam->ArenaMatchmakerRating); + InviteGroupToBG(aTeam, arena, TEAM_ALLIANCE); + InviteGroupToBG(hTeam, arena, TEAM_HORDE); + + LOG_DEBUG("bg.battleground", "Starting rated arena match!"); + arena->StartBattleground(); + } } if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED)) @@ -1096,16 +1165,17 @@ int32 BattlegroundQueue::GetQueueAnnouncementTimer(uint32 bracketId) const void BattlegroundQueue::InviteGroupToBG(GroupQueueInfo* ginfo, Battleground* bg, TeamId teamId) { - 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(); + BattlegroundTypeId bgTypeId = bg->GetBgTypeID(); BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(ginfo->BgTypeId, ginfo->ArenaType); BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); @@ -1129,25 +1199,31 @@ void BattlegroundQueue::InviteGroupToBG(GroupQueueInfo* ginfo, Battleground* bg, // increase invited counter for each invited player bg->IncreaseInvitedCount(ginfo->teamId); + player->SetInviteForBattlegroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); + // create remind invite events - BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, ginfo->BgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->ArenaType, ginfo->RemoveInviteTime); bgQueue.AddEvent(inviteEvent, INVITATION_REMIND_TIME); + // create automatic remove events - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgQueueTypeId, ginfo->RemoveInviteTime); + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(player->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); bgQueue.AddEvent(removeEvent, INVITE_ACCEPT_WAIT_TIME); // Check queueSlot uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId); ASSERT(queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES); + LOG_DEBUG("bg.battleground", "Battleground: invited player {} {} to BG instance {} queueindex {} bgtype {}", + player->GetName(), player->GetGUID().ToString(), bg->GetInstanceID(), queueSlot, bgTypeId); + // send status packet WorldPacket data; - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType, TEAM_NEUTRAL, bg->isRated(), ginfo->BgTypeId); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType, TEAM_NEUTRAL, bg->isRated()); player->GetSession()->SendPacket(&data); // pussywizard: if (bg->isArena() && bg->isRated()) - bg->ArenaLogEntries[player->GetGUID()].Fill(player->GetName().c_str(), player->GetGUID().GetCounter(), player->GetSession()->GetAccountId(), ginfo->ArenaTeamId, player->GetSession()->GetRemoteAddress()); + bg->ArenaLogEntries[player->GetGUID()].Fill(player->GetName(), player->GetGUID().GetCounter(), player->GetSession()->GetAccountId(), ginfo->ArenaTeamId, player->GetSession()->GetRemoteAddress()); } } @@ -1163,7 +1239,7 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) if (!player) return true; - Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID); + Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, m_BgTypeId); // if battleground ended, do nothing if (!bg) @@ -1201,7 +1277,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) if (!player) return true; - Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID); + Battleground* bg = sBattlegroundMgr->GetBattleground(m_BgInstanceGUID, m_BgTypeId); // battleground can be already deleted, bg may be nullptr! @@ -1226,8 +1302,19 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) sScriptMgr->OnBattlegroundDesertion(player, BG_DESERTION_TYPE_NO_ENTER_BUTTON); } + + LOG_DEBUG("bg.battleground", "Battleground: removing player {} from bg queue for instance {} because of not pressing enter battle in time.", player->GetGUID().ToString(), m_BgInstanceGUID); + player->RemoveBattlegroundQueueId(m_BgQueueTypeId); - bgQueue.RemovePlayer(m_PlayerGuid, false, queueSlot); + bgQueue.RemovePlayer(m_PlayerGuid, true); + + //update queues if battleground isn't ended + if (bg && bg->isBattleground() && bg->GetStatus() != STATUS_WAIT_LEAVE) + sBattlegroundMgr->ScheduleQueueUpdate(0, 0, m_BgQueueTypeId, m_BgTypeId, bg->GetBracketId()); + + WorldPacket data; + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); + player->SendDirectMessage(&data); } } diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h index b2b10eb09..1ea357fe7 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.h +++ b/src/server/game/Battlegrounds/BattlegroundQueue.h @@ -25,7 +25,7 @@ #include #include -#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10 +constexpr auto COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME = 10; struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) { @@ -43,10 +43,9 @@ struct GroupQueueInfo // stores informatio uint32 ArenaMatchmakerRating; // if rated match, inited to the rating of the team uint32 OpponentsTeamRating; // for rated arena matches uint32 OpponentsMatchmakerRating; // for rated arena matches - - // pussywizard: for internal use - uint8 _bracketId; - uint8 _groupType; + uint32 PreviousOpponentsTeamId; // excluded from the current queue until the timer is met + uint8 BracketId; // BattlegroundBracketId + uint8 GroupType; // BattlegroundQueueGroupTypes }; enum BattlegroundQueueGroupTypes @@ -61,22 +60,21 @@ enum BattlegroundQueueGroupTypes BG_QUEUE_MAX = 10 }; -class Battleground; class BattlegroundQueue { public: BattlegroundQueue(); ~BattlegroundQueue(); - void BattlegroundQueueUpdate(uint32 diff, BattlegroundBracketId bracket_id, bool isRated, uint32 arenaRatedTeamId); + void BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating); void UpdateEvents(uint32 diff); - void FillPlayersToBG(Battleground* bg, int32 aliFree, int32 hordeFree, BattlegroundBracketId bracket_id); + void FillPlayersToBG(Battleground* bg, BattlegroundBracketId bracket_id); bool CheckPremadeMatch(BattlegroundBracketId bracket_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); bool CheckNormalMatch(Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); bool CheckSkirmishForSameFaction(BattlegroundBracketId bracket_id, uint32 minPlayersPerTeam); - GroupQueueInfo* AddGroup(Player* leader, Group* group, PvPDifficultyEntry const* bracketEntry, bool isRated, bool isPremade, uint32 ArenaRating, uint32 MatchmakerRating, uint32 ArenaTeamId); - void RemovePlayer(ObjectGuid guid, bool sentToBg, uint32 playerQueueSlot); + GroupQueueInfo* AddGroup(Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId = 0, uint32 opponentsArenaTeamId = 0); + void RemovePlayer(ObjectGuid guid, bool decreaseInvitedCount); bool IsPlayerInvitedToRatedArena(ObjectGuid pl_guid); bool IsPlayerInvited(ObjectGuid pl_guid, uint32 bgInstanceGuid, uint32 removeTime); bool GetPlayerGroupInfoData(ObjectGuid guid, GroupQueueInfo* ginfo); @@ -89,7 +87,6 @@ public: void SendJoinMessageArenaQueue(Player* leader, GroupQueueInfo* ginfo, PvPDifficultyEntry const* bracketEntry, bool isRated); void SendExitMessageArenaQueue(GroupQueueInfo* ginfo); - void SetBgTypeIdAndArenaType(BattlegroundTypeId b, uint8 a) { m_bgTypeId = b; m_arenaType = ArenaType(a); } // pussywizard void AddEvent(BasicEvent* Event, uint64 e_time); typedef std::map QueuedPlayersMap; @@ -127,15 +124,10 @@ public: //one selection pool for horde, other one for alliance SelectionPool m_SelectionPools[PVP_TEAMS_COUNT]; - ArenaType GetArenaType() { return m_arenaType; } - BattlegroundTypeId GetBGTypeID() { return m_bgTypeId; } - void SetQueueAnnouncementTimer(uint32 bracketId, int32 timer, bool isCrossFactionBG = true); [[nodiscard]] int32 GetQueueAnnouncementTimer(uint32 bracketId) const; private: - BattlegroundTypeId m_bgTypeId; - ArenaType m_arenaType; uint32 m_WaitTimes[PVP_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME]; uint32 m_WaitTimeLastIndex[PVP_TEAMS_COUNT][MAX_BATTLEGROUND_BRACKETS]; @@ -176,9 +168,8 @@ private: class BGQueueRemoveEvent : public BasicEvent { public: - BGQueueRemoveEvent(ObjectGuid pl_guid, uint32 bgInstanceGUID, BattlegroundQueueTypeId bgQueueTypeId, uint32 removeTime) - : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgQueueTypeId(bgQueueTypeId) - {} + BGQueueRemoveEvent(ObjectGuid pl_guid, uint32 bgInstanceGUID, BattlegroundTypeId BgTypeId, BattlegroundQueueTypeId bgQueueTypeId, uint32 removeTime) : + m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId) { } ~BGQueueRemoveEvent() override = default; @@ -188,6 +179,7 @@ private: ObjectGuid m_PlayerGuid; uint32 m_BgInstanceGUID; uint32 m_RemoveTime; + BattlegroundTypeId m_BgTypeId; BattlegroundQueueTypeId m_BgQueueTypeId; }; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 7f763775c..3dad1840d 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1708,7 +1708,7 @@ namespace lfg if (bgQueueTypeId != BATTLEGROUND_QUEUE_NONE) { plr->RemoveBattlegroundQueueId(bgQueueTypeId); - sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(plr->GetGUID(), false, i); + sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(plr->GetGUID(), true); } } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 590e9d8c8..ff04227cb 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -248,7 +248,10 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this) m_swingErrorMsg = 0; for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j) - m_bgBattlegroundQueueID[j] = BATTLEGROUND_QUEUE_NONE; + { + _BgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; + _BgBattlegroundQueueID[j].invitedToInstance = 0; + } m_logintime = GameTime::GetGameTime().count(); m_Last_tick = m_logintime; @@ -11025,6 +11028,8 @@ void Player::LeaveBattleground(Battleground* bg) sScriptMgr->OnBattlegroundDesertion(this, BG_DESERTION_TYPE_LEAVE_BG); } + bg->RemovePlayerAtLeave(this); + // xinef: reset corpse reclaim time m_deathExpireTime = GameTime::GetGameTime().count(); @@ -11853,10 +11858,102 @@ Battleground* Player::GetBattleground(bool create) const if (GetBattlegroundId() == 0) return nullptr; - Battleground* bg = sBattlegroundMgr->GetBattleground(GetBattlegroundId()); + Battleground* bg = sBattlegroundMgr->GetBattleground(GetBattlegroundId(), GetBattlegroundTypeId()); return (create || (bg && bg->FindBgMap()) ? bg : nullptr); } +bool Player::InBattlegroundQueue(bool ignoreArena) const +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_NONE && + (!ignoreArena || (_BgBattlegroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_2v2 && + _BgBattlegroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_3v3 && + _BgBattlegroundQueueID[i].bgQueueTypeId != BATTLEGROUND_QUEUE_5v5))) + return true; + return false; +} + +BattlegroundQueueTypeId Player::GetBattlegroundQueueTypeId(uint32 index) const +{ + return _BgBattlegroundQueueID[index].bgQueueTypeId; +} + +uint32 Player::GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId) const +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId) + return i; + + return PLAYER_MAX_BATTLEGROUND_QUEUES; +} + +bool Player::IsInvitedForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId) + return _BgBattlegroundQueueID[i].invitedToInstance != 0; + + return false; +} + +bool Player::InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const +{ + return GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES; +} + +uint32 Player::AddBattlegroundQueueId(BattlegroundQueueTypeId val) +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + { + if (_BgBattlegroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE || _BgBattlegroundQueueID[i].bgQueueTypeId == val) + { + _BgBattlegroundQueueID[i].bgQueueTypeId = val; + _BgBattlegroundQueueID[i].invitedToInstance = 0; + return i; + } + } + + return PLAYER_MAX_BATTLEGROUND_QUEUES; +} + +bool Player::HasFreeBattlegroundQueueId() const +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].bgQueueTypeId == BATTLEGROUND_QUEUE_NONE) + return true; + + return false; +} + +void Player::RemoveBattlegroundQueueId(BattlegroundQueueTypeId val) +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + { + if (_BgBattlegroundQueueID[i].bgQueueTypeId == val) + { + _BgBattlegroundQueueID[i].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; + _BgBattlegroundQueueID[i].invitedToInstance = 0; + return; + } + } +} + +void Player::SetInviteForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId, uint32 instanceId) +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].bgQueueTypeId == bgQueueTypeId) + _BgBattlegroundQueueID[i].invitedToInstance = instanceId; +} + +bool Player::IsInvitedForBattlegroundInstance(uint32 instanceId) const +{ + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + if (_BgBattlegroundQueueID[i].invitedToInstance == instanceId) + return true; + + return false; +} + bool Player::InArena() const { Battleground* bg = GetBattleground(); @@ -11868,16 +11965,6 @@ bool Player::InArena() const void Player::SetBattlegroundId(uint32 id, BattlegroundTypeId bgTypeId, uint32 queueSlot, bool invited, bool isRandom, TeamId teamId) { - // if leaving current bg (and was invited) - decrease invited count for current one - if (m_bgData.bgInstanceID && m_bgData.isInvited) - if (Battleground* bg = sBattlegroundMgr->GetBattleground(m_bgData.bgInstanceID)) - bg->DecreaseInvitedCount(m_bgData.bgTeamId); - - // if entering new bg (and is invited) - increase invited count for new one - if (id && invited) - if (Battleground* bg = sBattlegroundMgr->GetBattleground(id)) - bg->IncreaseInvitedCount(teamId); - m_bgData.bgInstanceID = id; m_bgData.bgTypeID = bgTypeId; m_bgData.bgQueueSlot = queueSlot; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3896615e5..1d2b0246b 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -177,7 +177,7 @@ enum TalentTree // talent tabs // Spell modifier (used for modify other spells) struct SpellModifier { - SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), mask(), ownerAura(_ownerAura) {} + SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), mask(), ownerAura(_ownerAura) {} SpellModOp op : 8; SpellModType type : 8; int16 charges : 16; @@ -2188,59 +2188,20 @@ public: void SetBGData(BGData& bgdata) { m_bgData = bgdata; } [[nodiscard]] Battleground* GetBattleground(bool create = false) const; - [[nodiscard]] bool InBattlegroundQueue() const - { - for (auto i : m_bgBattlegroundQueueID) - if (i != BATTLEGROUND_QUEUE_NONE) - return true; - return false; - } + [[nodiscard]] bool InBattlegroundQueue(bool ignoreArena = false) const; + [[nodiscard]] bool IsDeserter() const { return HasAura(26013); } - [[nodiscard]] BattlegroundQueueTypeId GetBattlegroundQueueTypeId(uint32 index) const { return m_bgBattlegroundQueueID[index]; } - - [[nodiscard]] uint32 GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId) const - { - for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattlegroundQueueID[i] == bgQueueTypeId) - return i; - return PLAYER_MAX_BATTLEGROUND_QUEUES; - } - - [[nodiscard]] bool InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const - { - return GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES; - } + [[nodiscard]] BattlegroundQueueTypeId GetBattlegroundQueueTypeId(uint32 index) const; + [[nodiscard]] uint32 GetBattlegroundQueueIndex(BattlegroundQueueTypeId bgQueueTypeId) const; + [[nodiscard]] bool IsInvitedForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const; + [[nodiscard]] bool InBattlegroundQueueForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId) const; void SetBattlegroundId(uint32 id, BattlegroundTypeId bgTypeId, uint32 queueSlot, bool invited, bool isRandom, TeamId teamId); - - uint32 AddBattlegroundQueueId(BattlegroundQueueTypeId val) - { - for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (m_bgBattlegroundQueueID[i] == BATTLEGROUND_QUEUE_NONE || m_bgBattlegroundQueueID[i] == val) - { - m_bgBattlegroundQueueID[i] = val; - return i; - } - return PLAYER_MAX_BATTLEGROUND_QUEUES; - } - - bool HasFreeBattlegroundQueueId() - { - for (auto & i : m_bgBattlegroundQueueID) - if (i == BATTLEGROUND_QUEUE_NONE) - return true; - return false; - } - - void RemoveBattlegroundQueueId(BattlegroundQueueTypeId val) - { - for (auto & i : m_bgBattlegroundQueueID) - if (i == val) - { - i = BATTLEGROUND_QUEUE_NONE; - return; - } - } + uint32 AddBattlegroundQueueId(BattlegroundQueueTypeId val); + bool HasFreeBattlegroundQueueId() const; + void RemoveBattlegroundQueueId(BattlegroundQueueTypeId val); + void SetInviteForBattlegroundQueueType(BattlegroundQueueTypeId bgQueueTypeId, uint32 instanceId); + bool IsInvitedForBattlegroundInstance(uint32 instanceId) const; [[nodiscard]] TeamId GetBgTeamId() const { return m_bgData.bgTeamId != TEAM_NEUTRAL ? m_bgData.bgTeamId : GetTeamId(); } @@ -2601,7 +2562,13 @@ public: /*** BATTLEGROUND SYSTEM ***/ /*********************************************************/ - BattlegroundQueueTypeId m_bgBattlegroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES]; + struct BgBattlegroundQueueID_Rec + { + BattlegroundQueueTypeId bgQueueTypeId; + uint32 invitedToInstance; + }; + + std::array _BgBattlegroundQueueID; BGData m_bgData; bool m_IsBGRandomWinner; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 7549e58eb..c638cfbf0 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -2189,6 +2189,11 @@ ObjectGuid Group::GetLeaderGUID() const return m_leaderGuid; } +Player* Group::GetLeader() +{ + return ObjectAccessor::FindConnectedPlayer(m_leaderGuid); +} + ObjectGuid Group::GetGUID() const { return m_guid; @@ -2425,3 +2430,15 @@ void Group::SetDifficultyChangePrevention(DifficultyPreventionChangeType type) _difficultyChangePreventionTime = GameTime::GetGameTime().count() + MINUTE; _difficultyChangePreventionType = type; } + +void Group::DoForAllMembers(std::function const& worker) +{ + for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member) + continue; + + worker(member); + } +} diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 278cc5168..a1d142fb8 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -23,6 +23,7 @@ #include "LootMgr.h" #include "QueryResult.h" #include "SharedDefines.h" +#include class Battlefield; class Battleground; @@ -214,6 +215,7 @@ public: bool isBGGroup() const; bool IsCreated() const; ObjectGuid GetLeaderGUID() const; + Player* GetLeader(); ObjectGuid GetGUID() const; const char* GetLeaderName() const; LootMethod GetLootMethod() const; @@ -313,6 +315,8 @@ public: DifficultyPreventionChangeType GetDifficultyChangePreventionReason() const { return _difficultyChangePreventionType; } void SetDifficultyChangePrevention(DifficultyPreventionChangeType type); + void DoForAllMembers(std::function const& worker); + protected: void _homebindIfInstance(Player* player); void _cancelHomebindIfInstance(Player* player); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index ddf30758f..ae8da6564 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -72,8 +72,9 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { ObjectGuid guid; uint32 bgTypeId_; - uint32 instanceId; // sent to queue for particular bg from battlemaster's list, currently not used + uint32 instanceId; uint8 joinAsGroup; + bool isPremade = false; recvData >> guid; // battlemaster guid recvData >> bgTypeId_; // battleground type id (DBC id) @@ -82,7 +83,10 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) // entry not found if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) + { + LOG_ERROR("network", "Battleground: invalid bgtype ({}) received. possible cheater? player {}", bgTypeId_, _player->GetGUID().ToString()); return; + } // chosen battleground type is disabled if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, nullptr)) @@ -91,6 +95,8 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) return; } + LOG_DEBUG("network", "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from {}", guid.ToString()); + // get queue typeid and random typeid to check if already queued for them BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_); BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, 0); @@ -100,35 +106,26 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) if (bgQueueTypeId == BATTLEGROUND_QUEUE_NONE) return; - // get bg template - Battleground* bgt = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); - if (!bgt) + // ignore if player is already in BG + if (_player->InBattleground()) + return; + + // get bg instance or bg template if instance not found + Battleground* bg = nullptr; + if (instanceId) + bg = sBattlegroundMgr->GetBattlegroundThroughClientInstance(instanceId, bgTypeId); + + if (!bg) + bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); + + if (!bg) return; // expected bracket entry - PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgt->GetMapId(), _player->getLevel()); + PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel()); if (!bracketEntry) return; - // pussywizard: if trying to queue for already queued - // just remove from queue and it will requeue! - uint32 qSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId); - if (qSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) - { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - - if (bgQueue.IsPlayerInvitedToRatedArena(_player->GetGUID())) - { - WorldPacket data; - sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED); - SendPacket(&data); - return; - } - - bgQueue.RemovePlayer(_player->GetGUID(), false, qSlot); - _player->RemoveBattlegroundQueueId(bgQueueTypeId); - } - // must have free queue slot if (!_player->HasFreeBattlegroundQueueId()) { @@ -139,7 +136,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) } // queue result (default ok) - GroupJoinBattlegroundResult err = GroupJoinBattlegroundResult(bgt->GetBgTypeID()); + GroupJoinBattlegroundResult err = GroupJoinBattlegroundResult(bg->GetBgTypeID()); if (!sScriptMgr->CanJoinInBattlegroundQueue(_player, guid, bgTypeId, joinAsGroup, err) && err <= 0) { @@ -149,6 +146,8 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) return; } + BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); + // check if player can queue: if (!joinAsGroup) { @@ -169,6 +168,10 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { err = ERR_IN_RANDOM_BG; } + else if (_player->InBattlegroundQueueForBattlegroundQueueType(bgQueueTypeId)) // queued for this bg + { + err = ERR_BATTLEGROUND_NONE; + } else if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB) // already in queue, so can't queue for random { err = ERR_IN_NON_RANDOM_BG; @@ -193,15 +196,13 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) return; } - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, bracketEntry, false, false, 0, 0, 0); + GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0); uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); - uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId); // send status packet WorldPacket data; - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL); SendPacket(&data); sScriptMgr->OnPlayerJoinBG(_player); @@ -214,30 +215,6 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) if (!grp || grp->GetLeaderGUID() != _player->GetGUID()) return; - // pussywizard: for party members - remove queues for which leader is not queued to! - std::set leaderQueueTypeIds; - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - leaderQueueTypeIds.insert((uint32)_player->GetBattlegroundQueueTypeId(i)); - for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next()) - if (Player* member = itr->GetSource()) - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (BattlegroundQueueTypeId mqtid = member->GetBattlegroundQueueTypeId(i)) - if (leaderQueueTypeIds.count((uint32)mqtid) == 0) - { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(mqtid); - - if (bgQueue.IsPlayerInvitedToRatedArena(member->GetGUID())) - { - WorldPacket data; - sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED); - SendPacket(&data); - return; - } - - bgQueue.RemovePlayer(member->GetGUID(), false, i); - member->RemoveBattlegroundQueueId(mqtid); - } - if (_player->InBattlegroundQueueForBattlegroundQueueType(bgQueueTypeIdRandom)) // queued for random bg, so can't queue for anything else err = ERR_IN_RANDOM_BG; else if (_player->InBattlegroundQueue() && bgTypeId == BATTLEGROUND_RB) // already in queue, so can't queue for random @@ -248,44 +225,42 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) err = ERR_BATTLEGROUND_QUEUED_FOR_RATED; if (err > 0) - err = grp->CanJoinBattlegroundQueue(bgt, bgQueueTypeId, 0, bgt->GetMaxPlayersPerTeam(), false, 0); + err = grp->CanJoinBattlegroundQueue(bg, bgQueueTypeId, 0, bg->GetMaxPlayersPerTeam(), false, 0); - bool isPremade = (grp->GetMembersCount() >= bgt->GetMinPlayersPerTeam() && bgTypeId != BATTLEGROUND_RB); + isPremade = (grp->GetMembersCount() >= bg->GetMinPlayersPerTeam() && bgTypeId != BATTLEGROUND_RB); uint32 avgWaitTime = 0; if (err > 0) { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bracketEntry, false, isPremade, 0, 0, 0); + GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, 0, false, isPremade, 0, 0); avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); } - WorldPacket data; - for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next()) + grp->DoForAllMembers([bg, err, bgQueueTypeId, avgWaitTime](Player* member) { - Player* member = itr->GetSource(); - if (!member) - continue; + WorldPacket data; if (err <= 0) { sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err); member->GetSession()->SendPacket(&data); - continue; + return; } uint32 queueSlot = member->AddBattlegroundQueueId(bgQueueTypeId); // send status packet - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, 0, TEAM_NEUTRAL); member->GetSession()->SendPacket(&data); sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err); member->GetSession()->SendPacket(&data); sScriptMgr->OnPlayerJoinBG(member); - } + }); } + + sBattlegroundMgr->ScheduleQueueUpdate(0, 0, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); } void WorldSession::HandleBattlegroundPlayerPositionsOpcode(WorldPacket& /*recvData*/) @@ -373,7 +348,10 @@ void WorldSession::HandleBattlefieldListOpcode(WorldPacket& recvData) BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if (!bl) + { + LOG_DEBUG("bg.battleground", "BattlegroundHandler: invalid bgtype ({}) with player (Name: {}, {}) received.", bgTypeId, _player->GetName(), _player->GetGUID().ToString()); return; + } WorldPacket data; sBattlegroundMgr->BuildBattlegroundListPacket(&data, ObjectGuid::Empty, _player, BattlegroundTypeId(bgTypeId), fromWhere); @@ -382,21 +360,27 @@ void WorldSession::HandleBattlefieldListOpcode(WorldPacket& recvData) void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) { - uint8 arenaType; // arenatype if arena - uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 - uint32 bgTypeId_; // type id from dbc - uint16 unk; // 0x1F90 constant? - uint8 action; // enter battle 0x1, leave queue 0x0 + uint8 arenaType; // arenatype if arena + uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 + uint32 bgTypeId_; // type id from dbc + uint16 unk; // 0x1F90 constant? + uint8 action; // enter battle 0x1, leave queue 0x0 recvData >> arenaType >> unk2 >> bgTypeId_ >> unk >> action; // bgTypeId not valid if (!sBattlemasterListStore.LookupEntry(bgTypeId_)) + { + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}. Invalid BgType!", GetPlayerInfo(), arenaType, unk2, bgTypeId_, action); return; + } // player not in any queue, so can't really answer if (!_player->InBattlegroundQueue()) + { + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}. Player not in queue!", GetPlayerInfo(), arenaType, unk2, bgTypeId_, action); return; + } // get BattlegroundQueue for received BattlegroundTypeId bgTypeId = BattlegroundTypeId(bgTypeId_); @@ -409,20 +393,40 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) // get group info from queue GroupQueueInfo ginfo; if (!bgQueue.GetPlayerGroupInfoData(_player->GetGUID(), &ginfo)) + { + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}. Player not in queue (No player Group Info)!", + GetPlayerInfo(), arenaType, unk2, bgTypeId_, action); return; + } // to accept, player must be invited to particular battleground id if (!ginfo.IsInvitedToBGInstanceGUID && action == 1) + { + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}. Player is not invited to any bg!", + GetPlayerInfo(), arenaType, unk2, bgTypeId_, action); return; + } - Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID); - - // use template if leaving queue (instance might not be created yet) - if (!bg && action == 0) - bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); - + Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); if (!bg) - return; + { + if (action) + { + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}. Cant find BG with id {}!", + GetPlayerInfo(), arenaType, unk2, bgTypeId_, action, ginfo.IsInvitedToBGInstanceGUID); + return; + } + + bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); + if (!bg) + { + LOG_ERROR("network", "BattlegroundHandler: bg_template not found for type id {}.", bgTypeId); + return; + } + } + + LOG_DEBUG("bg.battleground", "CMSG_BATTLEFIELD_PORT {} ArenaType: {}, Unk: {}, BgType: {}, Action: {}.", + GetPlayerInfo(), arenaType, unk2, bgTypeId_, action); // expected bracket entry PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bg->GetMapId(), _player->getLevel()); @@ -439,74 +443,90 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); SendPacket(&data); action = 0; + LOG_DEBUG("bg.battleground", "Player {} {} has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUID().ToString()); } if (_player->getLevel() > bg->GetMaxLevel()) + { + LOG_ERROR("network", "Player {} {} has level ({}) higher than maxlevel ({}) of battleground ({})! Do not port him to battleground!", + _player->GetName(), _player->GetGUID().ToString(), _player->getLevel(), bg->GetMaxLevel(), bg->GetBgTypeID()); action = 0; + } } // get player queue slot index for this bg (can be in up to 2 queues at the same time) uint32 queueSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId); - WorldPacket data; - switch (action) + + if (action) // accept { - case 1: // accept + // check Freeze debuff + if (_player->HasAura(9454)) + return; + + if (!_player->IsInvitedForBattlegroundQueueType(bgQueueTypeId)) + return; // cheating? + + // set entry point if not in battleground + if (!_player->InBattleground()) + _player->SetEntryPoint(); + + // resurrect the player + if (!_player->IsAlive()) + { + _player->ResurrectPlayer(1.0f); + _player->SpawnCorpseBones(); + } + + TeamId teamId = ginfo.teamId; + + // send status packet + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), teamId); + SendPacket(&data); + + // remove battleground queue status from BGmgr + bgQueue.RemovePlayer(_player->GetGUID(), false); + + // this is still needed here if battleground "jumping" shouldn't add deserter debuff + // also this is required to prevent stuck at old battleground after SetBattlegroundId set to new + if (Battleground* currentBg = _player->GetBattleground()) + currentBg->RemovePlayerAtLeave(_player); + + // Remove from LFG queues + sLFGMgr->LeaveAllLfgQueues(_player->GetGUID(), false); + + _player->SetBattlegroundId(bg->GetInstanceID(), bg->GetBgTypeID(), queueSlot, true, bgTypeId == BATTLEGROUND_RB, teamId); + sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + + LOG_DEBUG("bg.battleground", "Battleground: player {} {} joined battle for bg {}, bgtype {}, queue type {}.", _player->GetName(), _player->GetGUID().ToString(), bg->GetInstanceID(), bg->GetBgTypeID(), bgQueueTypeId); + } + else // leave queue + { + bgQueue.RemovePlayer(_player->GetGUID(), true); + _player->RemoveBattlegroundQueueId(bgQueueTypeId); + + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0, TEAM_NEUTRAL); + SendPacket(&data); + + LOG_DEBUG("bg.battleground", "Battleground: player {} {} left queue for bgtype {}, queue type {}.", _player->GetName(), _player->GetGUID().ToString(), bg->GetBgTypeID(), bgQueueTypeId); + + // player left queue, we should update it - do not update Arena Queue + if (!ginfo.ArenaType) + sBattlegroundMgr->ScheduleQueueUpdate(ginfo.ArenaMatchmakerRating, ginfo.ArenaType, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); + + // track if player refuses to join the BG after being invited + if (bg->isBattleground() && (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN)) + { + if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { - // set entry point if not in battleground - if (!_player->InBattleground()) - _player->SetEntryPoint(); - - // resurrect the player - if (!_player->IsAlive()) - { - _player->ResurrectPlayer(1.0f); - _player->SpawnCorpseBones(); - } - - TeamId teamId = ginfo.teamId; - - // remove player from all bg queues - for (uint32 qslot = 0; qslot < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qslot) - if (BattlegroundQueueTypeId q = _player->GetBattlegroundQueueTypeId(qslot)) - { - BattlegroundQueue& queue = sBattlegroundMgr->GetBattlegroundQueue(q); - queue.RemovePlayer(_player->GetGUID(), (bgQueueTypeId == q), qslot); - _player->RemoveBattlegroundQueueId(q); - } - - // send status packet - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), teamId); - SendPacket(&data); - - // Remove from LFG queues - sLFGMgr->LeaveAllLfgQueues(_player->GetGUID(), false); - - _player->SetBattlegroundId(bg->GetInstanceID(), bg->GetBgTypeID(), queueSlot, true, bgTypeId == BATTLEGROUND_RB, teamId); - sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); + stmt->SetData(0, _player->GetGUID().GetCounter()); + stmt->SetData(1, BG_DESERTION_TYPE_LEAVE_QUEUE); + CharacterDatabase.Execute(stmt); } - break; - case 0: // leave queue - { - bgQueue.RemovePlayer(_player->GetGUID(), false, queueSlot); - _player->RemoveBattlegroundQueueId(bgQueueTypeId); - // track if player refuses to join the BG after being invited - if (bg->isBattleground() && (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN)) - { - if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) - { - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); - stmt->SetData(0, _player->GetGUID().GetCounter()); - stmt->SetData(1, BG_DESERTION_TYPE_LEAVE_QUEUE); - CharacterDatabase.Execute(stmt); - } - sScriptMgr->OnBattlegroundDesertion(_player, BG_DESERTION_TYPE_LEAVE_QUEUE); - } - } - break; - default: - break; + sScriptMgr->OnBattlegroundDesertion(_player, BG_DESERTION_TYPE_LEAVE_QUEUE); + } } } @@ -562,7 +582,7 @@ void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket& /*recvData*/) // if invited - send STATUS_WAIT_JOIN if (ginfo.IsInvitedToBGInstanceGUID) { - Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID); + Battleground* bg = sBattlegroundMgr->GetBattleground(ginfo.IsInvitedToBGInstanceGUID, bgTypeId); if (!bg) continue; @@ -591,10 +611,12 @@ void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket& /*recvData*/) void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) { - ObjectGuid guid; // arena Battlemaster guid - uint8 arenaslot; // 2v2, 3v3 or 5v5 - uint8 asGroup; // asGroup - uint8 isRated; // isRated + LOG_DEBUG("network", "WORLD: CMSG_BATTLEMASTER_JOIN_ARENA"); + + ObjectGuid guid; // arena Battlemaster guid + uint8 arenaslot; // 2v2, 3v3 or 5v5 + uint8 asGroup; // asGroup + uint8 isRated; // isRated recvData >> guid >> arenaslot >> asGroup >> isRated; @@ -602,6 +624,10 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) if (isRated && !asGroup) return; + // ignore if we already in BG or BG queue + if (_player->InBattleground()) + return; + // find creature by guid Creature* unit = GetPlayer()->GetMap()->GetCreature(guid); if (!unit || !unit->IsBattleMaster()) @@ -609,6 +635,11 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) // get arena type uint8 arenatype = 0; + uint32 ateamId = 0; + uint32 arenaRating = 0; + uint32 matchmakerRating = 0; + uint32 previousOpponents = 0; + switch (arenaslot) { case 0: @@ -621,13 +652,17 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) arenatype = ARENA_TYPE_5v5; break; default: + LOG_ERROR("network", "Unknown arena slot {} at HandleBattlemasterJoinArena()", arenaslot); return; } // get template for all arenas Battleground* bgt = sBattlegroundMgr->GetBattlegroundTemplate(BATTLEGROUND_AA); if (!bgt) + { + LOG_ERROR("network", "Battleground: template bg (all arenas) not found"); return; + } // arenas disabled if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, nullptr)) @@ -637,32 +672,12 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) } BattlegroundTypeId bgTypeId = bgt->GetBgTypeID(); - BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype); // expected bracket entry PvPDifficultyEntry const* bracketEntry = GetBattlegroundBracketByLevel(bgt->GetMapId(), _player->getLevel()); if (!bracketEntry) return; - // pussywizard: if trying to queue for already queued - // just remove from queue and it will requeue! - uint32 qSlot = _player->GetBattlegroundQueueIndex(bgQueueTypeId); - if (qSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) - { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - - if (bgQueue.IsPlayerInvitedToRatedArena(_player->GetGUID())) - { - WorldPacket data; - sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED); - SendPacket(&data); - return; - } - - bgQueue.RemovePlayer(_player->GetGUID(), false, qSlot); - _player->RemoveBattlegroundQueueId(bgQueueTypeId); - } - // must have free queue slot // pussywizard: allow being queued only in one arena queue, and it even cannot be together with bg queues if (_player->InBattlegroundQueue()) @@ -684,6 +699,9 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) return; } + BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(bgTypeId, arenatype); + BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); + // check if player can queue: if (!asGroup) { @@ -705,16 +723,25 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) return; } - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, bracketEntry, false, false, 0, 0, 0); - uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); + // check if already in queue + if (_player->GetBattlegroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES) + //player is already in this queue + return; + // check if has free queue slots + if (!_player->HasFreeBattlegroundQueueId()) + return; + + GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, nullptr, bgTypeId, bracketEntry, arenatype, isRated != 0, false, arenaRating, matchmakerRating, ateamId, previousOpponents); + uint32 avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); uint32 queueSlot = _player->AddBattlegroundQueueId(bgQueueTypeId); WorldPacket data; sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bgt, queueSlot, STATUS_WAIT_QUEUE, avgWaitTime, 0, arenatype, TEAM_NEUTRAL); SendPacket(&data); + LOG_DEBUG("bg.battleground", "Battleground: player joined queue for arena, skirmish, bg queue type {} bg type {}: {}, NAME {}", bgQueueTypeId, bgTypeId, _player->GetGUID().ToString(), _player->GetName()); + sScriptMgr->OnPlayerJoinArena(_player); } // check if group can queue: @@ -725,34 +752,6 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) if (!grp || grp->GetLeaderGUID() != _player->GetGUID()) return; - // pussywizard: for party members - remove queues for which leader is not queued to! - std::set leaderQueueTypeIds; - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - leaderQueueTypeIds.insert((uint32)_player->GetBattlegroundQueueTypeId(i)); - for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next()) - if (Player* member = itr->GetSource()) - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) - if (BattlegroundQueueTypeId mqtid = member->GetBattlegroundQueueTypeId(i)) - if (leaderQueueTypeIds.count((uint32)mqtid) == 0) - { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(mqtid); - - if (bgQueue.IsPlayerInvitedToRatedArena(member->GetGUID())) - { - WorldPacket data; - sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_BATTLEGROUND_JOIN_FAILED); - SendPacket(&data); - return; - } - - bgQueue.RemovePlayer(member->GetGUID(), false, i); - member->RemoveBattlegroundQueueId(mqtid); - } - - uint32 ateamId = 0; - uint32 arenaRating = 0; - uint32 matchmakerRating = 0; - // additional checks for rated arenas if (isRated) { @@ -775,15 +774,40 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) matchmakerRating = at->GetAverageMMR(grp); if (arenaRating <= 0) arenaRating = 1; + + previousOpponents = at->GetPreviousOpponents(); } err = grp->CanJoinBattlegroundQueue(bgt, bgQueueTypeId, arenatype, arenatype, (bool)isRated, arenaslot); + // Check queue group members + if (err) + { + grp->DoForAllMembers([&bgQueue, &err](Player* member) + { + if (bgQueue.IsPlayerInvitedToRatedArena(member->GetGUID())) + { + err = ERR_BATTLEGROUND_JOIN_FAILED; + } + }); + } + uint32 avgWaitTime = 0; if (err > 0) { - BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); - GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bracketEntry, isRated, false, arenaRating, matchmakerRating, ateamId); + LOG_DEBUG("bg.battleground", "Battleground: arena join as group start"); + + if (isRated) + { + LOG_DEBUG("bg.battleground", "Battleground: arena team id {}, leader {} queued with matchmaker rating {} for type {}", _player->GetArenaTeamId(arenaslot), _player->GetName(), matchmakerRating, arenatype); + bgt->SetRated(true); + } + else + { + bgt->SetRated(false); + } + + GroupQueueInfo* ginfo = bgQueue.AddGroup(_player, grp, bgTypeId, bracketEntry, arenatype, isRated != 0, false, arenaRating, matchmakerRating, ateamId, previousOpponents); avgWaitTime = bgQueue.GetAverageQueueWaitTime(ginfo); } @@ -810,13 +834,13 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData) sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, err); member->GetSession()->SendPacket(&data); + LOG_DEBUG("bg.battleground", "Battleground: player joined queue for arena as group bg queue type {} bg type {}: {}, NAME {}", bgQueueTypeId, bgTypeId, member->GetGUID().ToString(), member->GetName()); + sScriptMgr->OnPlayerJoinArena(member); } - - // pussywizard: schedule update for rated arena - if (ateamId) - sBattlegroundMgr->ScheduleArenaQueueUpdate(ateamId, bgQueueTypeId, bracketEntry->GetBracketId()); } + + sBattlegroundMgr->ScheduleQueueUpdate(matchmakerRating, arenatype, bgQueueTypeId, bgTypeId, bracketEntry->GetBracketId()); } void WorldSession::HandleReportPvPAFK(WorldPacket& recvData) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index e4d6c4886..540273a2a 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -170,7 +170,7 @@ void WorldSession::HandleMoveWorldportAck() if (uint32 inviteInstanceId = _player->GetPendingSpectatorInviteInstanceId()) { - if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId)) + if (Battleground* tbg = sBattlegroundMgr->GetBattleground(inviteInstanceId, BATTLEGROUND_TYPE_NONE)) tbg->RemoveToBeTeleported(_player->GetGUID()); _player->SetPendingSpectatorInviteInstanceId(0); } @@ -904,14 +904,14 @@ void WorldSession::ComputeNewClockDelta() std::vector latencies; std::vector clockDeltasAfterFiltering; - for (auto pair : _timeSyncClockDeltaQueue.content()) + for (auto& pair : _timeSyncClockDeltaQueue.content()) latencies.push_back(pair.second); uint32 latencyMedian = median(latencies); uint32 latencyStandardDeviation = standard_deviation(latencies); uint32 sampleSizeAfterFiltering = 0; - for (auto pair : _timeSyncClockDeltaQueue.content()) + for (auto& pair : _timeSyncClockDeltaQueue.content()) { if (pair.second <= latencyMedian + latencyStandardDeviation) { clockDeltasAfterFiltering.push_back(pair.first); diff --git a/src/server/game/Scripting/ScriptDefines/BGScript.cpp b/src/server/game/Scripting/ScriptDefines/BGScript.cpp index 602cb342a..3e06e2ce3 100644 --- a/src/server/game/Scripting/ScriptDefines/BGScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/BGScript.cpp @@ -66,58 +66,41 @@ void ScriptMgr::OnBattlegroundRemovePlayerAtLeave(Battleground* bg, Player* play }); } -void ScriptMgr::OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* grp, PvPDifficultyEntry const* bracketEntry, bool isPremade) +void ScriptMgr::OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, + uint8 arenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId, uint32 opponentsArenaTeamId) { ExecuteScript([&](BGScript* script) { - script->OnAddGroup(queue, ginfo, index, leader, grp, bracketEntry, isPremade); + script->OnAddGroup(queue, ginfo, index, leader, group, bgTypeId, bracketEntry, + arenaType, isRated, isPremade, arenaRating, matchmakerRating, arenaTeamId, opponentsArenaTeamId); }); } -bool ScriptMgr::CanFillPlayersToBG(BattlegroundQueue* queue, Battleground* bg, const int32 aliFree, const int32 hordeFree, BattlegroundBracketId bracket_id) +bool ScriptMgr::CanFillPlayersToBG(BattlegroundQueue* queue, Battleground* bg, BattlegroundBracketId bracket_id) { auto ret = IsValidBoolScript([&](BGScript* script) { - return !script->CanFillPlayersToBG(queue, bg, aliFree, hordeFree, bracket_id); + return !script->CanFillPlayersToBG(queue, bg, bracket_id); }); - if (ret && *ret) - { - return false; - } - - return true; + return ReturnValidBool(ret); } -bool ScriptMgr::CanFillPlayersToBGWithSpecific(BattlegroundQueue* queue, Battleground* bg, const int32 aliFree, const int32 hordeFree, - BattlegroundBracketId thisBracketId, BattlegroundQueue* specificQueue, BattlegroundBracketId specificBracketId) +bool ScriptMgr::IsCheckNormalMatch(BattlegroundQueue* queue, Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers) { auto ret = IsValidBoolScript([&](BGScript* script) { - return !script->CanFillPlayersToBGWithSpecific(queue, bg, aliFree, hordeFree, thisBracketId, specificQueue, specificBracketId); + return script->IsCheckNormalMatch(queue, bgTemplate, bracket_id, minPlayers, maxPlayers); }); - if (ret && *ret) - { - return false; - } - - return true; + return ReturnValidBool(ret, true); } -void ScriptMgr::OnCheckNormalMatch(BattlegroundQueue* queue, uint32& Coef, Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32& minPlayers, uint32& maxPlayers) +void ScriptMgr::OnQueueUpdate(BattlegroundQueue* queue, uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating) { ExecuteScript([&](BGScript* script) { - script->OnCheckNormalMatch(queue, Coef, bgTemplate, bracket_id, minPlayers, maxPlayers); - }); -} - -void ScriptMgr::OnQueueUpdate(BattlegroundQueue* queue, BattlegroundBracketId bracket_id, bool isRated, uint32 arenaRatedTeamId) -{ - ExecuteScript([&](BGScript* script) - { - script->OnQueueUpdate(queue, bracket_id, isRated, arenaRatedTeamId); + script->OnQueueUpdate(queue, diff, bgTypeId, bracket_id, arenaType, isRated, arenaRating); }); } @@ -128,12 +111,7 @@ bool ScriptMgr::CanSendMessageBGQueue(BattlegroundQueue* queue, Player* leader, return !script->CanSendMessageBGQueue(queue, leader, bg, bracketEntry); }); - if (ret && *ret) - { - return false; - } - - return true; + return ReturnValidBool(ret); } bool ScriptMgr::OnBeforeSendJoinMessageArenaQueue(BattlegroundQueue* queue, Player* leader, GroupQueueInfo* ginfo, PvPDifficultyEntry const* bracketEntry, bool isRated) @@ -143,12 +121,7 @@ bool ScriptMgr::OnBeforeSendJoinMessageArenaQueue(BattlegroundQueue* queue, Play return !script->OnBeforeSendJoinMessageArenaQueue(queue, leader, ginfo, bracketEntry, isRated); }); - if (ret && *ret) - { - return false; - } - - return true; + return ReturnValidBool(ret); } bool ScriptMgr::OnBeforeSendExitMessageArenaQueue(BattlegroundQueue* queue, GroupQueueInfo* ginfo) @@ -158,12 +131,7 @@ bool ScriptMgr::OnBeforeSendExitMessageArenaQueue(BattlegroundQueue* queue, Grou return !script->OnBeforeSendExitMessageArenaQueue(queue, ginfo); }); - if (ret && *ret) - { - return false; - } - - return true; + return ReturnValidBool(ret); } void ScriptMgr::OnBattlegroundEnd(Battleground* bg, TeamId winnerTeam) diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index a1860b269..8400cd7f9 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -1566,16 +1566,14 @@ public: // Remove player at leave BG virtual void OnBattlegroundRemovePlayerAtLeave(Battleground* /*bg*/, Player* /*player*/) { } - virtual void OnQueueUpdate(BattlegroundQueue* /*queue*/, BattlegroundBracketId /*bracket_id*/, bool /*isRated*/, uint32 /*arenaRatedTeamId*/) { } + virtual void OnQueueUpdate(BattlegroundQueue* /*queue*/, uint32 /* diff */, BattlegroundTypeId /* bgTypeId */, BattlegroundBracketId /* bracket_id */, uint8 /* arenaType */, bool /* isRated */, uint32 /* arenaRating */) { } - virtual void OnAddGroup(BattlegroundQueue* /*queue*/, GroupQueueInfo* /*ginfo*/, uint32& /*index*/, Player* /*leader*/, Group* /*grp*/, PvPDifficultyEntry const* /*bracketEntry*/, bool /*isPremade*/) { } + virtual void OnAddGroup(BattlegroundQueue* /*queue*/, GroupQueueInfo* /*ginfo*/, uint32& /*index*/, Player* /*leader*/, Group* /*group*/, BattlegroundTypeId /* bgTypeId */, PvPDifficultyEntry const* /* bracketEntry */, + uint8 /* arenaType */, bool /* isRated */, bool /* isPremade */, uint32 /* arenaRating */, uint32 /* matchmakerRating */, uint32 /* arenaTeamId */, uint32 /* opponentsArenaTeamId */) { } - [[nodiscard]] virtual bool CanFillPlayersToBG(BattlegroundQueue* /*queue*/, Battleground* /*bg*/, const int32 /*aliFree*/, const int32 /*hordeFree*/, BattlegroundBracketId /*bracket_id*/) { return true; } + [[nodiscard]] virtual bool CanFillPlayersToBG(BattlegroundQueue* /*queue*/, Battleground* /*bg*/, BattlegroundBracketId /*bracket_id*/) { return true; } - [[nodiscard]] virtual bool CanFillPlayersToBGWithSpecific(BattlegroundQueue* /*queue*/, Battleground* /*bg*/, const int32 /*aliFree*/, const int32 /*hordeFree*/, - BattlegroundBracketId /*thisBracketId*/, BattlegroundQueue* /*specificQueue*/, BattlegroundBracketId /*specificBracketId*/) { return true; } - - virtual void OnCheckNormalMatch(BattlegroundQueue* /*queue*/, uint32& /*Coef*/, Battleground* /*bgTemplate*/, BattlegroundBracketId /*bracket_id*/, uint32& /*minPlayers*/, uint32& /*maxPlayers*/) { } + [[nodiscard]] virtual bool IsCheckNormalMatch(BattlegroundQueue* /*queue*/, Battleground* /*bgTemplate*/, BattlegroundBracketId /*bracket_id*/, uint32 /*minPlayers*/, uint32 /*maxPlayers*/) { return false; }; [[nodiscard]] virtual bool CanSendMessageBGQueue(BattlegroundQueue* /*queue*/, Player* /*leader*/, Battleground* /*bg*/, PvPDifficultyEntry const* /*bracketEntry*/) { return true; } @@ -2402,12 +2400,11 @@ public: /* BGScript */ void OnBattlegroundAddPlayer(Battleground* bg, Player* player); void OnBattlegroundBeforeAddPlayer(Battleground* bg, Player* player); void OnBattlegroundRemovePlayerAtLeave(Battleground* bg, Player* player); - void OnQueueUpdate(BattlegroundQueue* queue, BattlegroundBracketId bracket_id, bool isRated, uint32 arenaRatedTeamId); - void OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* grp, PvPDifficultyEntry const* bracketEntry, bool isPremade); - bool CanFillPlayersToBG(BattlegroundQueue* queue, Battleground* bg, const int32 aliFree, const int32 hordeFree, BattlegroundBracketId bracket_id); - bool CanFillPlayersToBGWithSpecific(BattlegroundQueue* queue, Battleground* bg, const int32 aliFree, const int32 hordeFree, - BattlegroundBracketId thisBracketId, BattlegroundQueue* specificQueue, BattlegroundBracketId specificBracketId); - void OnCheckNormalMatch(BattlegroundQueue* queue, uint32& Coef, Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32& minPlayers, uint32& maxPlayers); + void OnQueueUpdate(BattlegroundQueue* queue, uint32 diff, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating); + void OnAddGroup(BattlegroundQueue* queue, GroupQueueInfo* ginfo, uint32& index, Player* leader, Group* group, BattlegroundTypeId bgTypeId, PvPDifficultyEntry const* bracketEntry, + uint8 arenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 matchmakerRating, uint32 arenaTeamId, uint32 opponentsArenaTeamId); + bool CanFillPlayersToBG(BattlegroundQueue* queue, Battleground* bg, BattlegroundBracketId bracket_id); + bool IsCheckNormalMatch(BattlegroundQueue* queue, Battleground* bgTemplate, BattlegroundBracketId bracket_id, uint32 minPlayers, uint32 maxPlayers); bool CanSendMessageBGQueue(BattlegroundQueue* queue, Player* leader, Battleground* bg, PvPDifficultyEntry const* bracketEntry); bool OnBeforeSendJoinMessageArenaQueue(BattlegroundQueue* queue, Player* leader, GroupQueueInfo* ginfo, PvPDifficultyEntry const* bracketEntry, bool isRated); bool OnBeforeSendExitMessageArenaQueue(BattlegroundQueue* queue, GroupQueueInfo* ginfo); diff --git a/src/server/game/Scripting/ScriptMgrMacros.h b/src/server/game/Scripting/ScriptMgrMacros.h index 82fef5168..a26c6d71b 100644 --- a/src/server/game/Scripting/ScriptMgrMacros.h +++ b/src/server/game/Scripting/ScriptMgrMacros.h @@ -64,4 +64,9 @@ inline void ExecuteScript(std::function executeHook) } } +inline bool ReturnValidBool(Optional ret, bool need = false) +{ + return ret && *ret ? need : !need; +} + #endif // _SCRIPT_MGR_MACRO_H_ diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index e54c94c92..344bfeb5d 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -587,8 +587,6 @@ void WorldSession::LogoutPlayer(bool save) for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) if (BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i)) { - _player->RemoveBattlegroundQueueId(bgQueueTypeId); - sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(_player->GetGUID(), false, i); // track if player logs out after invited to join BG if (_player->IsInvitedForBattlegroundInstance()) { @@ -599,8 +597,12 @@ void WorldSession::LogoutPlayer(bool save) stmt->SetData(1, BG_DESERTION_TYPE_INVITE_LOGOUT); CharacterDatabase.Execute(stmt); } + sScriptMgr->OnBattlegroundDesertion(_player, BG_DESERTION_TYPE_INVITE_LOGOUT); } + + _player->RemoveBattlegroundQueueId(bgQueueTypeId); + sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(_player->GetGUID(), true); } ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 41d73701a..05d3045e4 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -307,6 +307,7 @@ enum WorldIntConfigs CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS, CONFIG_ARENA_MAX_RATING_DIFFERENCE, CONFIG_ARENA_RATING_DISCARD_TIMER, + CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, CONFIG_ARENA_GAMES_REQUIRED, CONFIG_ARENA_SEASON_ID, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index f6d491ff6..4263c34ee 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1163,25 +1163,26 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN] = 150; } - m_int_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfigMgr->GetOption ("Arena.MaxRatingDifference", 150); - m_int_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfigMgr->GetOption ("Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILLISECONDS); + m_int_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfigMgr->GetOption("Arena.MaxRatingDifference", 150); + m_int_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfigMgr->GetOption("Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILLISECONDS); + m_int_configs[CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER] = sConfigMgr->GetOption("Arena.PreviousOpponentsDiscardTimer", 2 * MINUTE * IN_MILLISECONDS); m_bool_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfigMgr->GetOption("Arena.AutoDistributePoints", false); - m_int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetOption ("Arena.AutoDistributeInterval", 7); // pussywizard: spoiled by implementing constant day and hour, always 7 now - m_int_configs[CONFIG_ARENA_GAMES_REQUIRED] = sConfigMgr->GetOption ("Arena.GamesRequired", 10); - m_int_configs[CONFIG_ARENA_SEASON_ID] = sConfigMgr->GetOption ("Arena.ArenaSeason.ID", 1); - m_int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetOption ("Arena.ArenaStartRating", 0); - m_int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption ("Arena.ArenaStartPersonalRating", 1000); - m_int_configs[CONFIG_ARENA_START_MATCHMAKER_RATING] = sConfigMgr->GetOption ("Arena.ArenaStartMatchmakerRating", 1500); + m_int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetOption("Arena.AutoDistributeInterval", 7); // pussywizard: spoiled by implementing constant day and hour, always 7 now + m_int_configs[CONFIG_ARENA_GAMES_REQUIRED] = sConfigMgr->GetOption("Arena.GamesRequired", 10); + m_int_configs[CONFIG_ARENA_SEASON_ID] = sConfigMgr->GetOption("Arena.ArenaSeason.ID", 1); + m_int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetOption("Arena.ArenaStartRating", 0); + m_int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption("Arena.ArenaStartPersonalRating", 1000); + m_int_configs[CONFIG_ARENA_START_MATCHMAKER_RATING] = sConfigMgr->GetOption("Arena.ArenaStartMatchmakerRating", 1500); m_bool_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfigMgr->GetOption("Arena.ArenaSeason.InProgress", true); m_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_1] = sConfigMgr->GetOption("Arena.ArenaWinRatingModifier1", 48.0f); m_float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_2] = sConfigMgr->GetOption("Arena.ArenaWinRatingModifier2", 24.0f); m_float_configs[CONFIG_ARENA_LOSE_RATING_MODIFIER] = sConfigMgr->GetOption("Arena.ArenaLoseRatingModifier", 24.0f); m_float_configs[CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER] = sConfigMgr->GetOption("Arena.ArenaMatchmakerRatingModifier", 24.0f); - m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE] = sConfigMgr->GetOption ("Arena.QueueAnnouncer.Enable", false); - m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetOption ("Arena.QueueAnnouncer.PlayerOnly", false); + m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE] = sConfigMgr->GetOption("Arena.QueueAnnouncer.Enable", false); + m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetOption("Arena.QueueAnnouncer.PlayerOnly", false); m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetOption("OffhandCheckAtSpellUnlearn", true); - m_int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetOption("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS); + m_int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetOption("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS); if (int32 clientCacheId = sConfigMgr->GetOption("ClientCacheVersion", 0)) { @@ -2046,7 +2047,7 @@ void World::SetInitialWorldSettings() ///- Initialize Battlegrounds LOG_INFO("server.loading", "Starting Battleground System"); - sBattlegroundMgr->CreateInitialBattlegrounds(); + sBattlegroundMgr->LoadBattlegroundTemplates(); sBattlegroundMgr->InitAutomaticArenaPointDistribution(); ///- Initialize outdoor pvp diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index b8a792627..b50068b6d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -412,7 +412,7 @@ public: return false; } - Battleground* bg = sBattlegroundMgr->CreateNewBattleground(randomizedArenaBgTypeId, 80, 80, ArenaType(hcnt >= 2 ? hcnt : 2), false); + Battleground* bg = sBattlegroundMgr->CreateNewBattleground(randomizedArenaBgTypeId, GetBattlegroundBracketById(bgt->GetMapId(), bgt->GetBracketId()), ArenaType(hcnt >= 2 ? hcnt : 2), false); if (!bg) { handler->PSendSysMessage("Couldn't create arena map!"); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 8cb93cf28..a705a82e2 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -215,7 +215,7 @@ public: static bool HandleReloadBattlegroundTemplate(ChatHandler* handler) { LOG_INFO("server.loading", "Re-Loading Battleground Templates..."); - sBattlegroundMgr->CreateInitialBattlegrounds(); + sBattlegroundMgr->LoadBattlegroundTemplates(); handler->SendGlobalGMSysMessage("DB table `battleground_template` reloaded."); return true; } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 902bb72c5..28ab446c2 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2923,6 +2923,14 @@ Arena.MaxRatingDifference = 150 Arena.RatingDiscardTimer = 600000 +# +# Arena.PreviousOpponentsDiscardTimer +# Description: Time (in milliseconds) after which the previous opponents will be ignored. +# Default: 120000 - (Enabled, 2 minutes - Blizzlike) +# 0 - (Disabled) + +Arena.PreviousOpponentsDiscardTimer = 120000 + # # Arena.AutoDistributePoints # Description: Automatically distribute arena points.