/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "BattlegroundMgr.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "BattlegroundAB.h" #include "BattlegroundAV.h" #include "BattlegroundBE.h" #include "BattlegroundDS.h" #include "BattlegroundEY.h" #include "BattlegroundIC.h" #include "BattlegroundNA.h" #include "BattlegroundQueue.h" #include "BattlegroundRL.h" #include "BattlegroundRV.h" #include "BattlegroundSA.h" #include "BattlegroundWS.h" #include "Chat.h" #include "Common.h" #include "DisableMgr.h" #include "Formulas.h" #include "GameEventMgr.h" #include "GameGraveyard.h" #include "GameTime.h" #include "Map.h" #include "MapMgr.h" #include "MiscPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" #include "ScriptMgr.h" #include "SharedDefines.h" #include "World.h" #include "WorldPacket.h" #include "WorldState.h" #include "WorldStateDefines.h" #include bool BattlegroundTemplate::IsArena() const { return BattlemasterEntry->type == MAP_ARENA; } /*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ BattlegroundMgr::BattlegroundMgr() : m_ArenaTesting(false), m_Testing(false), m_NextAutoDistributionTime(0), m_AutoDistributionTimeChecker(0), m_NextPeriodicQueueUpdateTime(5 * IN_MILLISECONDS) { } BattlegroundMgr::~BattlegroundMgr() { DeleteAllBattlegrounds(); } BattlegroundMgr* BattlegroundMgr::instance() { static BattlegroundMgr instance; return &instance; } void BattlegroundMgr::DeleteAllBattlegrounds() { for (auto& [_, data] : bgDataStore) { while (!data._Battlegrounds.empty()) delete data._Battlegrounds.begin()->second; 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 (auto& [_, bgData] : bgDataStore) { 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 = 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 (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_QueueUpdateScheduler.empty()) { std::vector scheduled; std::swap(scheduled, m_QueueUpdateScheduler); for (uint8 i = 0; i < scheduled.size(); i++) { 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, bgTypeId, bracket_id, arenaType, arenaMMRating > 0, arenaMMRating); m_BattlegroundQueues[bgQueueTypeId].BattlegroundQueueAnnouncerUpdate(diff, bgQueueTypeId, bracket_id); } } // periodic queue update if (m_NextPeriodicQueueUpdateTime < 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, BATTLEGROUND_AA, BattlegroundBracketId(bracket), BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId(qtype)), true, 0); } } 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].BattlegroundQueueAnnouncerUpdate(m_NextPeriodicQueueUpdateTime, BattlegroundQueueTypeId(qtype), BattlegroundBracketId(bracket)); } } } else m_NextPeriodicQueueUpdateTime -= diff; // arena points auto-distribution if (sWorld->getBoolConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { if (m_AutoDistributionTimeChecker < diff) { if (GameTime::GetGameTime() > m_NextAutoDistributionTime) { sArenaTeamMgr->DistributeArenaPoints(); m_NextAutoDistributionTime = GameTime::GetGameTime() + Seconds(DAY * sWorld->getIntConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS)); sWorldState->setWorldState(WORLD_STATE_CUSTOM_ARENA_DISTRIBUTION_TIME, m_NextAutoDistributionTime.count()); } m_AutoDistributionTimeChecker = 600000; // 10 minutes check } else m_AutoDistributionTimeChecker -= diff; } } void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket* data, Battleground* bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype, TeamId teamId, bool isRated, BattlegroundTypeId forceBgTypeId) { // pussywizard: //ASSERT(QueueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES); if (StatusID == STATUS_NONE || !bg) { data->Initialize(SMSG_BATTLEFIELD_STATUS, 4 + 8); *data << uint32(QueueSlot); *data << uint64(0); return; } data->Initialize(SMSG_BATTLEFIELD_STATUS, (4 + 8 + 1 + 1 + 4 + 1 + 4 + 4 + 4)); *data << uint32(QueueSlot); // The following segment is read as uint64 in client but can be appended as their original type. *data << uint8(arenatype); *data << uint8(bg->isArena() ? 0xE : 0x0); *data << uint32(forceBgTypeId != BATTLEGROUND_TYPE_NONE ? forceBgTypeId : bg->GetBgTypeID()); *data << uint16(0x1F90); // End of uint64 segment, decomposed this way for simplicity *data << uint8(bg->GetMinLevel()); *data << uint8(bg->GetMaxLevel()); *data << uint32(bg->GetClientInstanceID()); // following displays the minimap icon. 0 = faction icon, 1 = arenaicon *data << uint8(bg->isRated() || isRated); // 1 for rated match, 0 for bg or non rated match *data << uint32(StatusID); // status switch (StatusID) { case STATUS_WAIT_QUEUE: // status_in_queue *data << uint32(Time1); // average wait time, milliseconds *data << uint32(Time2); // time in queue, updated every minute!, milliseconds break; case STATUS_WAIT_JOIN: // status_invite *data << uint32(bg->GetMapId()); // map id *data << uint64(0); // 3.3.5, unknown *data << uint32(Time1); // time to remove from queue, milliseconds break; case STATUS_IN_PROGRESS: // status_in_progress *data << uint32(bg->GetMapId()); // map id *data << uint64(0); // 3.3.5, unknown *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds *data << uint32(Time2); // time from bg start, milliseconds *data << uint8(teamId == TEAM_ALLIANCE ? 1 : 0); // arenafaction (0 for horde, 1 for alliance) break; default: break; } } void BattlegroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket* data, GroupJoinBattlegroundResult result) { data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); *data << int32(result); if (result == ERR_BATTLEGROUND_JOIN_TIMED_OUT || result == ERR_BATTLEGROUND_JOIN_FAILED) *data << uint64(0); // player guid } void BattlegroundMgr::BuildPlayerLeftBattlegroundPacket(WorldPacket* data, ObjectGuid guid) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); *data << guid; } void BattlegroundMgr::BuildPlayerJoinedBattlegroundPacket(WorldPacket* data, Player* player) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8); *data << player->GetGUID(); } 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; 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) { BattlegroundDataContainer::const_iterator itr = bgDataStore.find(bgTypeId); if (itr == bgDataStore.end()) 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; } std::vector BattlegroundMgr::GetActiveBattlegrounds() { std::vector result; for (auto const& [bgType, bgData] : bgDataStore) for (auto const& [id, bg] : bgData._Battlegrounds) if (bg->GetStatus() == STATUS_WAIT_JOIN || bg->GetStatus() == STATUS_IN_PROGRESS) result.push_back(static_cast(bg)); return result; } uint32 BattlegroundMgr::CreateClientVisibleInstanceId(BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { 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, PvPDifficultyEntry const* bracketEntry, uint8 arenaType, bool isRated) { BattlegroundTypeId bgTypeId = GetRandomBG(originalBgTypeId, bracketEntry->minLevel); if (originalBgTypeId == BATTLEGROUND_AA) originalBgTypeId = bgTypeId; // 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()) { return nullptr; } bg = BattlegroundMgr::bgTypeToTemplate[bgTypeId](bg_template); bool isRandom = bgTypeId != originalBgTypeId && !bg->isArena(); bg->SetBracket(bracketEntry); bg->SetInstanceID(sMapMgr->GenerateInstanceId()); bg->SetClientInstanceID(CreateClientVisibleInstanceId(originalBgTypeId, bracketEntry->GetBracketId())); bg->Init(); bg->SetStatus(STATUS_WAIT_JOIN); // start the joining of the bg bg->SetArenaType(arenaType); bg->SetBgTypeID(originalBgTypeId); bg->SetRandomTypeID(bgTypeId); bg->SetRated(isRated); bg->SetRandom(isRandom); // Set up correct min/max player counts for scoreboards if (bg->isArena()) { uint32 maxPlayersPerTeam = ArenaTeam::GetReqPlayersForType(arenaType) / 2; sScriptMgr->OnSetArenaMaxPlayersPerTeam(arenaType, maxPlayersPerTeam); bg->SetMaxPlayersPerTeam(maxPlayersPerTeam); } return bg; } // used to create the BG templates bool BattlegroundMgr::CreateBattleground(BattlegroundTemplate const* bgTemplate) { // Create the BG Battleground* bg = GetBattlegroundTemplate(bgTemplate->Id); if (!bg) { bg = BattlegroundMgr::bgtypeToBattleground[bgTemplate->Id]; ASSERT(bg); if (bgTemplate->Id == BATTLEGROUND_RB) bg->SetRandom(true); 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::LoadBattlegroundTemplates() { uint32 oldMSTime = getMSTime(); _battlegroundMapTemplates.clear(); _battlegroundTemplates.clear(); // 0 1 2 3 4 5 6 7 8 9 10 11 QueryResult result = WorldDatabase.Query("SELECT ID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, AllianceStartO, HordeStartLoc, HordeStartO, StartMaxDist, Weight, ScriptName FROM battleground_template"); if (!result) { LOG_ERROR("bg.battleground", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); return; } do { Field* fields = result->Fetch(); BattlegroundTypeId bgTypeId = static_cast(fields[0].Get()); if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId, nullptr)) continue; // can be overwrite by values from DB BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if (!bl) { LOG_ERROR("bg.battleground", "Battleground ID {} not found in BattlemasterList.dbc. Battleground not created.", bgTypeId); continue; } 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(); bgTemplate.MaxStartDistSq = dist * dist; bgTemplate.Weight = fields[10].Get(); bgTemplate.ScriptId = sObjectMgr->GetScriptId(fields[11].Get()); bgTemplate.BattlemasterEntry = bl; if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.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 (bgTemplate.MinLevel == 0 || bgTemplate.MaxLevel == 0 || bgTemplate.MinLevel > bgTemplate.MaxLevel) { LOG_ERROR("sql.sql", "Table `battleground_template` for id {} has bad values for LevelMin ({}) and LevelMax({})", bgTemplate.Id, bgTemplate.MinLevel, bgTemplate.MaxLevel); continue; } if (bgTemplate.Id != BATTLEGROUND_AA && bgTemplate.Id != BATTLEGROUND_RB) { uint32 startId = fields[5].Get(); if (GraveyardStruct const* start = sGraveyard->GetGraveyard(startId)) { bgTemplate.StartLocation[TEAM_ALLIANCE].Relocate(start->x, start->y, start->z, fields[6].Get()); } else { 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)) { bgTemplate.StartLocation[TEAM_HORDE].Relocate(start->x, start->y, start->z, fields[8].Get()); } else { 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(&bgTemplate)) continue; _battlegroundTemplates[bgTypeId] = bgTemplate; if (bgTemplate.BattlemasterEntry->mapid[1] == -1) // in this case we have only one mapId _battlegroundMapTemplates[bgTemplate.BattlemasterEntry->mapid[0]] = &_battlegroundTemplates[bgTypeId]; } while (result->NextRow()); LOG_INFO("server.loading", ">> Loaded {} battlegrounds in {} ms", _battlegroundTemplates.size(), GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } void BattlegroundMgr::InitAutomaticArenaPointDistribution() { if (!sWorld->getBoolConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) return; Seconds wstime = Seconds(sWorldState->getWorldState(WORLD_STATE_CUSTOM_ARENA_DISTRIBUTION_TIME)); Seconds curtime = GameTime::GetGameTime(); LOG_INFO("server.loading", "Initializing Automatic Arena Point Distribution"); if (wstime < curtime) { m_NextAutoDistributionTime = curtime; // reset will be called in the next update LOG_INFO("server.loading", "Next arena point distribution time in the past, reseting it now."); } else { m_NextAutoDistributionTime = wstime; } LOG_INFO("server.loading", "Automatic Arena Point Distribution initialized."); } void BattlegroundMgr::BuildBattlegroundListPacket(WorldPacket* data, ObjectGuid guid, Player* player, BattlegroundTypeId bgTypeId, uint8 fromWhere) { if (!player) return; uint32 winner_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_FIRST); uint32 winner_arena = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_ARENA_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_ARENA_FIRST); uint32 loser_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_FIRST); winner_kills = Acore::Honor::hk_honor_at_level(player->GetLevel(), float(winner_kills)); loser_kills = Acore::Honor::hk_honor_at_level(player->GetLevel(), float(loser_kills)); data->Initialize(SMSG_BATTLEFIELD_LIST); *data << guid; // battlemaster guid *data << uint8(fromWhere); // from where you joined *data << uint32(bgTypeId); // battleground id *data << uint8(0); // unk *data << uint8(0); // unk // Rewards *data << uint8(player->GetRandomWinner()); // 3.3.3 hasWin *data << uint32(winner_kills); // 3.3.3 winHonor *data << uint32(winner_arena); // 3.3.3 winArena *data << uint32(loser_kills); // 3.3.3 lossHonor uint8 isQueueRandom = (bgTypeId == BATTLEGROUND_RB); *data << uint8(isQueueRandom); // 3.3.3 isRandom if (isQueueRandom) { // Rewards (random) *data << uint8(player->GetRandomWinner()); // 3.3.3 hasWin_Random *data << uint32(winner_kills); // 3.3.3 winHonor_Random *data << uint32(winner_arena); // 3.3.3 winArena_Random *data << uint32(loser_kills); // 3.3.3 lossHonor_Random } if (bgTypeId == BATTLEGROUND_AA) // arena *data << uint32(0); // unk (count?) else // battleground { std::size_t count_pos = data->wpos(); *data << uint32(0); // number of bg instances 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; BattlegroundBracketId bracketId = bracketEntry->GetBracketId(); BattlegroundClientIdsContainer& clientIds = it->second._ClientBattlegroundIds[bracketId]; for (auto const& itr : clientIds) { *data << uint32(itr); ++count; } data->put(count_pos, count); } } } } void BattlegroundMgr::SendToBattleground(Player* player, uint32 instanceId, BattlegroundTypeId bgTypeId) { if (Battleground* bg = GetBattleground(instanceId, bgTypeId)) { uint32 mapid = bg->GetMapId(); Position const* pos = bg->GetTeamStartPosition(player->GetBgTeamId()); LOG_DEBUG("bg.battleground", "BattlegroundMgr::SendToBattleground: Sending {} to map {}, {} (bgType {})", player->GetName(), mapid, pos->ToString(), bgTypeId); player->TeleportTo(mapid, pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation()); } else { LOG_ERROR("bg.battleground", "BattlegroundMgr::SendToBattleground: Instance {} (bgType {}) not found while trying to teleport player {}", instanceId, bgTypeId, player->GetName()); } } void BattlegroundMgr::SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, ObjectGuid guid) { WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); uint32 time_ = RESURRECTION_INTERVAL - bg->GetLastResurrectTime(); // resurrect every X seconds if (time_ == uint32(-1)) time_ = 0; data << guid << time_; player->GetSession()->SendPacket(&data); } bool BattlegroundMgr::IsArenaType(BattlegroundTypeId bgTypeId) { return bgTypeId == BATTLEGROUND_AA || bgTypeId == BATTLEGROUND_BE || bgTypeId == BATTLEGROUND_NA || bgTypeId == BATTLEGROUND_DS || bgTypeId == BATTLEGROUND_RV || bgTypeId == BATTLEGROUND_RL; } BattlegroundQueueTypeId BattlegroundMgr::BGQueueTypeId(BattlegroundTypeId bgTypeId, uint8 arenaType) { uint32 queueTypeID = BATTLEGROUND_QUEUE_NONE; if (arenaType) { if (BattlegroundMgr::ArenaTypeToQueue.find(arenaType) != BattlegroundMgr::ArenaTypeToQueue.end()) { queueTypeID = BattlegroundMgr::ArenaTypeToQueue.at(arenaType); } sScriptMgr->OnArenaTypeIDToQueueID(bgTypeId, arenaType, queueTypeID); return static_cast(queueTypeID); } if (BattlegroundMgr::bgToQueue.find(bgTypeId) != BattlegroundMgr::bgToQueue.end()) { queueTypeID = BattlegroundMgr::bgToQueue.at(bgTypeId); } return static_cast(queueTypeID); } BattlegroundTypeId BattlegroundMgr::BGTemplateId(BattlegroundQueueTypeId bgQueueTypeId) { if (BattlegroundMgr::queueToBg.find(bgQueueTypeId) == BattlegroundMgr::queueToBg.end()) { return BattlegroundTypeId(0); } return BattlegroundMgr::queueToBg[bgQueueTypeId]; } uint8 BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId bgQueueTypeId) { uint8 arenaType = 0; if (BattlegroundMgr::QueueToArenaType.find(bgQueueTypeId) != BattlegroundMgr::QueueToArenaType.end()) { arenaType = BattlegroundMgr::QueueToArenaType.at(bgQueueTypeId); } sScriptMgr->OnArenaQueueIdToArenaType(bgQueueTypeId, arenaType); return arenaType; } void BattlegroundMgr::ToggleTesting() { if (sWorld->getBoolConfig(CONFIG_DEBUG_BATTLEGROUND)) { m_Testing = true; ChatHandler(nullptr).SendWorldText(LANG_DEBUG_BG_CONF); } else { m_Testing = !m_Testing; ChatHandler(nullptr).SendWorldText(m_Testing ? LANG_DEBUG_BG_ON : LANG_DEBUG_BG_OFF); } } void BattlegroundMgr::ToggleArenaTesting() { if (sWorld->getBoolConfig(CONFIG_DEBUG_ARENA)) { m_ArenaTesting = true; ChatHandler(nullptr).SendWorldText(LANG_DEBUG_ARENA_CONF); } else { m_ArenaTesting = !m_ArenaTesting; ChatHandler(nullptr).SendWorldText(m_ArenaTesting ? LANG_DEBUG_ARENA_ON : LANG_DEBUG_ARENA_OFF); } } void BattlegroundMgr::SetHolidayWeekends(uint32 mask) { for (uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) { if (bgtype == BATTLEGROUND_RB) continue; if (Battleground* bgt = GetBattlegroundTemplate(BattlegroundTypeId(bgtype))) bgt->SetHoliday(mask & (1 << bgtype)); } } void BattlegroundMgr::ScheduleQueueUpdate(uint32 arenaMatchmakerRating, uint8 arenaType, BattlegroundQueueTypeId bgQueueTypeId, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id) { //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 { return sWorld->getIntConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); } uint32 BattlegroundMgr::GetPrematureFinishTime() const { return sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); } void BattlegroundMgr::LoadBattleMastersEntry() { uint32 oldMSTime = getMSTime(); mBattleMastersMap.clear(); // need for reload case QueryResult result = WorldDatabase.Query("SELECT entry, bg_template FROM battlemaster_entry"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 battlemaster entries. DB table `battlemaster_entry` is empty!"); LOG_INFO("server.loading", " "); return; } uint32 count = 0; do { ++count; Field* fields = result->Fetch(); uint32 entry = fields[0].Get(); if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry)) { if ((cInfo->npcflag & UNIT_NPC_FLAG_BATTLEMASTER) == 0) LOG_ERROR("sql.sql", "Creature (Entry: {}) listed in `battlemaster_entry` is not a battlemaster.", entry); } else { LOG_ERROR("sql.sql", "Creature (Entry: {}) listed in `battlemaster_entry` does not exist.", entry); continue; } uint32 bgTypeId = fields[1].Get(); if (!sBattlemasterListStore.LookupEntry(bgTypeId)) { LOG_ERROR("sql.sql", "Table `battlemaster_entry` contain entry {} for not existed battleground type {}, ignored.", entry, bgTypeId); continue; } mBattleMastersMap[entry] = BattlegroundTypeId(bgTypeId); } while (result->NextRow()); CheckBattleMasters(); LOG_INFO("server.loading", ">> Loaded {} battlemaster entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } void BattlegroundMgr::CheckBattleMasters() { CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates(); for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr) { if ((itr->second.npcflag & UNIT_NPC_FLAG_BATTLEMASTER) && mBattleMastersMap.find(itr->second.Entry) == mBattleMastersMap.end()) { LOG_ERROR("sql.sql", "CreatureTemplate (Entry: {}) has UNIT_NPC_FLAG_BATTLEMASTER but no data in `battlemaster_entry` table. Removing flag!", itr->second.Entry); const_cast(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_BATTLEMASTER; } } } HolidayIds BattlegroundMgr::BGTypeToWeekendHolidayId(BattlegroundTypeId bgTypeId) { switch (bgTypeId) { case BATTLEGROUND_AV: return HOLIDAY_CALL_TO_ARMS_AV; case BATTLEGROUND_EY: return HOLIDAY_CALL_TO_ARMS_EY; case BATTLEGROUND_WS: return HOLIDAY_CALL_TO_ARMS_WS; case BATTLEGROUND_SA: return HOLIDAY_CALL_TO_ARMS_SA; case BATTLEGROUND_AB: return HOLIDAY_CALL_TO_ARMS_AB; case BATTLEGROUND_IC: return HOLIDAY_CALL_TO_ARMS_IC; default: return HOLIDAY_NONE; } } BattlegroundTypeId BattlegroundMgr::WeekendHolidayIdToBGType(HolidayIds holiday) { switch (holiday) { case HOLIDAY_CALL_TO_ARMS_AV: return BATTLEGROUND_AV; case HOLIDAY_CALL_TO_ARMS_EY: return BATTLEGROUND_EY; case HOLIDAY_CALL_TO_ARMS_WS: return BATTLEGROUND_WS; case HOLIDAY_CALL_TO_ARMS_SA: return BATTLEGROUND_SA; case HOLIDAY_CALL_TO_ARMS_AB: return BATTLEGROUND_AB; case HOLIDAY_CALL_TO_ARMS_IC: return BATTLEGROUND_IC; default: return BATTLEGROUND_TYPE_NONE; } } bool BattlegroundMgr::IsBGWeekend(BattlegroundTypeId bgTypeId) { return IsHolidayActive(BGTypeToWeekendHolidayId(bgTypeId)); } BattlegroundTypeId BattlegroundMgr::GetRandomBG(BattlegroundTypeId bgTypeId, uint32 minLevel) { if (BattlegroundTemplate const* bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId)) { std::vector ids; ids.reserve(16); std::vector weights; weights.reserve(16); for (int32 mapId : bgTemplate->BattlemasterEntry->mapid) { if (mapId == -1) break; if (BattlegroundTemplate const* bg = GetBattlegroundTemplateByMapId(mapId)) { if (bg->MinLevel <= minLevel) { ids.push_back(bg->Id); weights.push_back(bg->Weight); } } } return *Acore::Containers::SelectRandomWeightedContainerElement(ids, weights); } 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) bgDataStore[bg->GetBgTypeID()]._Battlegrounds[bg->GetInstanceID()] = bg; sScriptMgr->OnBattlegroundCreate(bg); } void BattlegroundMgr::RemoveBattleground(BattlegroundTypeId bgTypeId, uint32 instanceId) { bgDataStore[bgTypeId]._Battlegrounds.erase(instanceId); } // init/update unordered_map // Battlegrounds std::unordered_map BattlegroundMgr::bgToQueue = { { BATTLEGROUND_AV, BATTLEGROUND_QUEUE_AV}, { BATTLEGROUND_WS, BATTLEGROUND_QUEUE_WS}, { BATTLEGROUND_AB, BATTLEGROUND_QUEUE_AB}, { BATTLEGROUND_EY, BATTLEGROUND_QUEUE_EY}, { BATTLEGROUND_SA, BATTLEGROUND_QUEUE_SA}, { BATTLEGROUND_IC, BATTLEGROUND_QUEUE_IC}, { BATTLEGROUND_RB, BATTLEGROUND_QUEUE_RB}, // Arena Battlegrounds { BATTLEGROUND_NA, BattlegroundQueueTypeId(0)}, // Nagrand Arena { BATTLEGROUND_BE, BattlegroundQueueTypeId(0)}, // Blade's Edge Arena { BATTLEGROUND_AA, BattlegroundQueueTypeId(0)}, // All Arena { BATTLEGROUND_RL, BattlegroundQueueTypeId(0)}, // Ruins of Lordaernon { BATTLEGROUND_DS, BattlegroundQueueTypeId(0)}, // Dalaran Sewer { BATTLEGROUND_RV, BattlegroundQueueTypeId(0)}, // Ring of Valor }; std::unordered_map BattlegroundMgr::queueToBg = { { BATTLEGROUND_QUEUE_NONE, BATTLEGROUND_TYPE_NONE }, { BATTLEGROUND_QUEUE_AV, BATTLEGROUND_AV }, { BATTLEGROUND_QUEUE_WS, BATTLEGROUND_WS }, { BATTLEGROUND_QUEUE_AB, BATTLEGROUND_AB }, { BATTLEGROUND_QUEUE_EY, BATTLEGROUND_EY }, { BATTLEGROUND_QUEUE_SA, BATTLEGROUND_SA }, { BATTLEGROUND_QUEUE_IC, BATTLEGROUND_IC }, { BATTLEGROUND_QUEUE_RB, BATTLEGROUND_RB }, { BATTLEGROUND_QUEUE_2v2, BATTLEGROUND_AA }, { BATTLEGROUND_QUEUE_3v3, BATTLEGROUND_AA }, { BATTLEGROUND_QUEUE_5v5, BATTLEGROUND_AA }, }; std::unordered_map BattlegroundMgr::bgtypeToBattleground = { { BATTLEGROUND_AV, new BattlegroundAV }, { BATTLEGROUND_WS, new BattlegroundWS }, { BATTLEGROUND_AB, new BattlegroundAB }, { BATTLEGROUND_NA, new BattlegroundNA }, { BATTLEGROUND_BE, new BattlegroundBE }, { BATTLEGROUND_EY, new BattlegroundEY }, { BATTLEGROUND_RL, new BattlegroundRL }, { BATTLEGROUND_SA, new BattlegroundSA }, { BATTLEGROUND_DS, new BattlegroundDS }, { BATTLEGROUND_RV, new BattlegroundRV }, { BATTLEGROUND_IC, new BattlegroundIC }, { BATTLEGROUND_AA, new Battleground }, { BATTLEGROUND_RB, new Battleground }, }; std::unordered_map BattlegroundMgr::bgTypeToTemplate = { { BATTLEGROUND_AV, [](Battleground * bg_t) -> Battleground* { return new BattlegroundAV(*(BattlegroundAV*)bg_t); } }, { BATTLEGROUND_WS, [](Battleground * bg_t) -> Battleground* { return new BattlegroundWS(*(BattlegroundWS*)bg_t); } }, { BATTLEGROUND_AB, [](Battleground * bg_t) -> Battleground* { return new BattlegroundAB(*(BattlegroundAB*)bg_t); } }, { BATTLEGROUND_NA, [](Battleground * bg_t) -> Battleground* { return new BattlegroundNA(*(BattlegroundNA*)bg_t); } }, { BATTLEGROUND_BE, [](Battleground * bg_t) -> Battleground* { return new BattlegroundBE(*(BattlegroundBE*)bg_t); } }, { BATTLEGROUND_EY, [](Battleground * bg_t) -> Battleground* { return new BattlegroundEY(*(BattlegroundEY*)bg_t); } }, { BATTLEGROUND_RL, [](Battleground * bg_t) -> Battleground* { return new BattlegroundRL(*(BattlegroundRL*)bg_t); } }, { BATTLEGROUND_SA, [](Battleground * bg_t) -> Battleground* { return new BattlegroundSA(*(BattlegroundSA*)bg_t); } }, { BATTLEGROUND_DS, [](Battleground * bg_t) -> Battleground* { return new BattlegroundDS(*(BattlegroundDS*)bg_t); } }, { BATTLEGROUND_RV, [](Battleground * bg_t) -> Battleground* { return new BattlegroundRV(*(BattlegroundRV*)bg_t); } }, { BATTLEGROUND_IC, [](Battleground * bg_t) -> Battleground* { return new BattlegroundIC(*(BattlegroundIC*)bg_t); } }, { BATTLEGROUND_RB, [](Battleground * bg_t) -> Battleground* { return new Battleground(*bg_t); }, }, { BATTLEGROUND_AA, [](Battleground * bg_t) -> Battleground* { return new Battleground(*bg_t); }, }, }; std::unordered_map BattlegroundMgr::getBgFromMap = {}; std::unordered_map BattlegroundMgr::getBgFromTypeID = { { BATTLEGROUND_RB, [](WorldPacket * data, Battleground::BattlegroundScoreMap::const_iterator itr2, Battleground * bg) { if (BattlegroundMgr::getBgFromMap.find(bg->GetMapId()) == BattlegroundMgr::getBgFromMap.end()) // this should not happen { *data << uint32(0); } else { BattlegroundMgr::getBgFromMap[bg->GetMapId()](data, itr2); } } } }; std::unordered_map BattlegroundMgr::ArenaTypeToQueue = { { ARENA_TYPE_2v2, BATTLEGROUND_QUEUE_2v2 }, { ARENA_TYPE_3v3, BATTLEGROUND_QUEUE_3v3 }, { ARENA_TYPE_5v5, BATTLEGROUND_QUEUE_5v5 } }; std::unordered_map BattlegroundMgr::QueueToArenaType = { { BATTLEGROUND_QUEUE_2v2, ARENA_TYPE_2v2 }, { BATTLEGROUND_QUEUE_3v3, ARENA_TYPE_3v3 }, { BATTLEGROUND_QUEUE_5v5, ARENA_TYPE_5v5 } };