diff options
Diffstat (limited to 'src/game/BattleGroundMgr.cpp')
-rw-r--r-- | src/game/BattleGroundMgr.cpp | 2031 |
1 files changed, 1162 insertions, 869 deletions
diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 54d2538068d..371c204717d 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,12 @@ */ #include "Common.h" -#include "Player.h" +#include "ObjectMgr.h" +#include "World.h" +#include "WorldPacket.h" +#include "Policies/SingletonImp.h" + +#include "ArenaTeam.h" #include "BattleGroundMgr.h" #include "BattleGroundAV.h" #include "BattleGroundAB.h" @@ -29,16 +34,16 @@ #include "BattleGroundBE.h" #include "BattleGroundAA.h" #include "BattleGroundRL.h" -#include "SharedDefines.h" -#include "Policies/SingletonImp.h" -#include "MapManager.h" +#include "BattleGroundSA.h" +#include "BattleGroundDS.h" +#include "BattleGroundRV.h" +#include "Chat.h" #include "Map.h" #include "MapInstanced.h" -#include "ObjectMgr.h" +#include "MapManager.h" +#include "Player.h" #include "ProgressBar.h" -#include "World.h" -#include "Chat.h" -#include "ArenaTeam.h" +#include "SharedDefines.h" INSTANTIATE_SINGLETON_1( BattleGroundMgr ); @@ -48,96 +53,103 @@ INSTANTIATE_SINGLETON_1( BattleGroundMgr ); BattleGroundQueue::BattleGroundQueue() { - //queues are empty, we don't have to call clear() -/* for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) - { - //m_QueuedPlayers[i].Horde = 0; - //m_QueuedPlayers[i].Alliance = 0; - //m_QueuedPlayers[i].AverageTime = 0; - }*/ -} - -BattleGroundQueue::~BattleGroundQueue() -{ - for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) { - m_QueuedPlayers[i].clear(); - for(QueuedGroupsList::iterator itr = m_QueuedGroups[i].begin(); itr!= m_QueuedGroups[i].end(); ++itr) + for(uint32 j = 0; j < MAX_BATTLEGROUND_QUEUES; j++) { - delete (*itr); + m_SumOfWaitTimes[i][j] = 0; + m_WaitTimeLastPlayer[i][j] = 0; + for(uint32 k = 0; k < COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; k++) + m_WaitTimes[i][j][k] = 0; } - m_QueuedGroups[i].clear(); } } -// initialize eligible groups from the given source matching the given specifications -void BattleGroundQueue::EligibleGroups::Init(BattleGroundQueue::QueuedGroupsList *source, uint32 BgTypeId, uint32 side, uint32 MaxPlayers, uint8 ArenaType, bool IsRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +BattleGroundQueue::~BattleGroundQueue() { - // clear from prev initialization - clear(); - BattleGroundQueue::QueuedGroupsList::iterator itr, next; - // iterate through the source - for(itr = source->begin(); itr!= source->end(); itr = next) + m_QueuedPlayers.clear(); + for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) { - next = itr; - ++next; - if( (*itr)->BgTypeId == BgTypeId && // bg type must match - (*itr)->ArenaType == ArenaType && // arena type must match - (*itr)->IsRated == IsRated && // israted must match - (*itr)->IsInvitedToBGInstanceGUID == 0 && // leave out already invited groups - (*itr)->Team == side && // match side - (*itr)->Players.size() <= MaxPlayers && // the group must fit in the bg - ( !excludeTeam || (*itr)->ArenaTeamId != excludeTeam ) && // if excludeTeam is specified, leave out those arena team ids - ( !IsRated || (*itr)->Players.size() == MaxPlayers ) && // if rated, then pass only if the player count is exact NEEDS TESTING! (but now this should never happen) - ( !DisregardTime || (*itr)->JoinTime <= DisregardTime // pass if disregard time is greater than join time - || (*itr)->ArenaTeamRating == 0 // pass if no rating info - || ( (*itr)->ArenaTeamRating >= MinRating // pass if matches the rating range - && (*itr)->ArenaTeamRating <= MaxRating ) ) ) + for(uint32 j = 0; j < BG_QUEUE_GROUP_TYPES_COUNT; j++) { - // the group matches the conditions - // using push_back for proper selecting when inviting - push_back((*itr)); + for(GroupsQueueType::iterator itr = m_QueuedGroups[i][j].begin(); itr!= m_QueuedGroups[i][j].end(); ++itr) + delete (*itr); + m_QueuedGroups[i][j].clear(); } } } +/*********************************************************/ +/*** BATTLEGROUND QUEUE SELECTION POOLS ***/ +/*********************************************************/ + // selection pool initialization, used to clean up from prev selection -void BattleGroundQueue::SelectionPool::Init(EligibleGroups * curr) +void BattleGroundQueue::SelectionPool::Init() { - m_CurrEligGroups = curr; SelectedGroups.clear(); PlayerCount = 0; } // remove group info from selection pool -void BattleGroundQueue::SelectionPool::RemoveGroup(GroupQueueInfo *ginfo) +// returns true when we need to try to add new group to selection pool +// returns false when selection pool is ok or when we kicked smaller group than we need to kick +// sometimes it can be called on empty selection pool +bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size) { - // find what to remove - for(std::list<GroupQueueInfo *>::iterator itr = SelectedGroups.begin(); itr != SelectedGroups.end(); ++itr) + //find maxgroup or LAST group with size == size and kick it + bool found = false; + GroupsQueueType::iterator groupToKick = SelectedGroups.begin(); + for (GroupsQueueType::iterator itr = groupToKick; itr != SelectedGroups.end(); ++itr) { - if((*itr)==ginfo) + if (abs((int32)((*itr)->Players.size() - size)) <= 1) { - SelectedGroups.erase(itr); - // decrease selected players count - PlayerCount -= ginfo->Players.size(); - return; + groupToKick = itr; + found = true; } + else if (!found && (*itr)->Players.size() >= (*groupToKick)->Players.size()) + groupToKick = itr; } + //if pool is empty, do nothing + if (GetPlayerCount()) + { + //update player count + GroupQueueInfo* ginfo = (*groupToKick); + SelectedGroups.erase(groupToKick); + PlayerCount -= ginfo->Players.size(); + //return false if we kicked smaller group or there are enough players in selection pool + if (ginfo->Players.size() <= size + 1) + return false; + } + return true; } -// add group to selection +// add group to selection pool // used when building selection pools -void BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo * ginfo) +// returns true if we can invite more players, or when we added group to selection pool +// returns false when selection pool is full +bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount) { - SelectedGroups.push_back(ginfo); - // increase selected players count - PlayerCount+=ginfo->Players.size(); + //if group is larger than desired count - don't allow to add it to pool + if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) + { + SelectedGroups.push_back(ginfo); + // increase selected players count + PlayerCount += ginfo->Players.size(); + return true; + } + if (PlayerCount < desiredCount) + return true; + return false; } +/*********************************************************/ +/*** BATTLEGROUND QUEUES ***/ +/*********************************************************/ + // add group to bg queue with the given leader and bg specifications -GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, uint8 ArenaType, bool isRated, uint32 arenaRating, uint32 arenateamid) +GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId BgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid) { - uint32 queue_id = leader->GetBattleGroundQueueIdFromLevel(); + BGQueueIdBasedOnLevel queue_id = leader->GetBattleGroundQueueIdFromLevel(BgTypeId); // create new ginfo // cannot use the method like in addplayer, because that could modify an in-queue group's stats @@ -147,28 +159,34 @@ GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, uint32 BgTypeId, ui ginfo->ArenaType = ArenaType; ginfo->ArenaTeamId = arenateamid; ginfo->IsRated = isRated; - ginfo->IsInvitedToBGInstanceGUID = 0; // maybe this should be modifiable by function arguments to enable selection of running instances? + ginfo->IsInvitedToBGInstanceGUID = 0; ginfo->JoinTime = getMSTime(); + ginfo->RemoveInviteTime = 0; ginfo->Team = leader->GetTeam(); ginfo->ArenaTeamRating = arenaRating; - ginfo->OpponentsTeamRating = 0; //initialize it to 0 + ginfo->OpponentsTeamRating = 0; ginfo->Players.clear(); - m_QueuedGroups[queue_id].push_back(ginfo); + //compute index (if group is premade or joined a rated match) to queues + uint32 index = 0; + if (!isRated && !isPremade) + index += BG_TEAMS_COUNT; + if (ginfo->Team == HORDE) + index++; + sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, queue_id : %u, index : %u", BgTypeId, queue_id, index); + + m_QueuedGroups[queue_id][index].push_back(ginfo); // return ginfo, because it is needed to add players to this group info return ginfo; } +//add player to playermap void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) { - uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(); - //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak - PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()]; - info.InviteTime = 0; - info.LastInviteTime = 0; + PlayerQueueInfo& info = m_QueuedPlayers[plr->GetGUID()]; info.LastOnlineTime = getMSTime(); info.GroupInfo = ginfo; @@ -176,113 +194,245 @@ void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) ginfo->Players[plr->GetGUID()] = &info; } -void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) +void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id) { - Player *plr = objmgr.GetPlayer(guid); + uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) + { + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } - uint32 queue_id = 0; - QueuedPlayersMap::iterator itr; - GroupQueueInfo * group; - QueuedGroupsList::iterator group_itr; - bool IsSet = false; - if(plr) + //store pointer to arrayindex of player that was added first + uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][queue_id]); + //remove his time from sum + m_SumOfWaitTimes[team_index][queue_id] -= m_WaitTimes[team_index][queue_id][(*lastPlayerAddedPointer)]; + //set average time to new + m_WaitTimes[team_index][queue_id][(*lastPlayerAddedPointer)] = timeInQueue; + //add new time to sum + m_SumOfWaitTimes[team_index][queue_id] += timeInQueue; + //set index of last player added to next one + (*lastPlayerAddedPointer)++; + (*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME; +} + +uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id) +{ + uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! + if (!ginfo->ArenaType) { - queue_id = plr->GetBattleGroundQueueIdFromLevel(); + if (ginfo->Team == HORDE) + team_index = BG_TEAM_HORDE; + } + else + { + if (ginfo->IsRated) + team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE + } + //check if there is enought values(we always add values > 0) + if (m_WaitTimes[team_index][queue_id][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME - 1] ) + return (m_SumOfWaitTimes[team_index][queue_id] / COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME); + else + //if there aren't enough values return 0 - not available + return 0; +} + +//remove player from queue and from group info, if group info is empty then remove it too +void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount) +{ + //Player *plr = objmgr.GetPlayer(guid); + + int32 queue_id = -1; // signed for proper for-loop finish + QueuedPlayersMap::iterator itr; - itr = m_QueuedPlayers[queue_id].find(guid); - if(itr != m_QueuedPlayers[queue_id].end()) - IsSet = true; + //remove player from map, if he's there + itr = m_QueuedPlayers.find(guid); + if (itr == m_QueuedPlayers.end()) + { + sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid)); + return; } - if(!IsSet) + GroupQueueInfo* group = itr->second.GroupInfo; + GroupsQueueType::iterator group_itr, group_itr_tmp; + // mostly people with the highest levels are in battlegrounds, thats why + // we count from MAX_BATTLEGROUND_QUEUES - 1 to 0 + // variable index removes useless searching in other team's queue + uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE; + + for (int32 queue_id_tmp = MAX_BATTLEGROUND_QUEUES - 1; queue_id_tmp >= 0 && queue_id == -1; --queue_id_tmp) { - // either player is offline, or he levelled up to another queue category - // sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); - for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + //we must check premade and normal team's queue - because when players from premade are joining bg, + //they leave groupinfo so we can't use its players size to find out index + for (uint32 j = index; j < BG_QUEUE_GROUP_TYPES_COUNT; j += BG_QUEUE_NORMAL_ALLIANCE) { - itr = m_QueuedPlayers[i].find(guid); - if(itr != m_QueuedPlayers[i].end()) + for(group_itr_tmp = m_QueuedGroups[queue_id_tmp][j].begin(); group_itr_tmp != m_QueuedGroups[queue_id_tmp][j].end(); ++group_itr_tmp) { - queue_id = i; - IsSet = true; - break; + if ((*group_itr_tmp) == group) + { + queue_id = queue_id_tmp; + group_itr = group_itr_tmp; + //we must store index to be able to erase iterator + index = j; + break; + } } } } - - // couldn't find the player in bg queue, return - if(!IsSet) + //player can't be in queue without group, but just in case + if (queue_id == -1) { - sLog.outError("Battleground: couldn't find player to remove."); + sLog.outError("BattleGroundQueue: ERROR Cannot find groupinfo for player GUID: %u", GUID_LOPART(guid)); return; } + sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from queue_id %u", GUID_LOPART(guid), (uint32)queue_id); - group = itr->second.GroupInfo; + // ALL variables are correctly set + // We can ignore leveling up in queue - it should not cause crash + // remove player from group + // if only one player there, remove group + + // remove player queue info from group queue info + std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid); + if (pitr != group->Players.end()) + group->Players.erase(pitr); - for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr) + // if invited to bg, and should decrease invited count, then do it + if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID) { - if(group == (GroupQueueInfo*)(*group_itr)) - break; + BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID, group->BgTypeId); + if (bg) + bg->DecreaseInvitedCount(group->Team); } - // variables are set (what about leveling up when in queue????) - // remove player from group - // if only player there, remove group + // remove player queue info + m_QueuedPlayers.erase(itr); - // remove player queue info from group queue info - std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid); + //if we left BG queue(not porting) OR if arena team left queue for rated match + if ((decreaseInvitedCount && !group->ArenaType) || (group->ArenaType && group->IsRated && group->Players.empty())) + AnnounceWorld(group, guid, false); - if(pitr != group->Players.end()) - group->Players.erase(pitr); + //if player leaves queue and he is invited to rated arena match, then he have to loose + if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount) + { + ArenaTeam * at = objmgr.GetArenaTeamById(group->ArenaTeamId); + if (at) + { + sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(guid), group->OpponentsTeamRating); + Player *plr = objmgr.GetPlayer(guid); + if (plr) + at->MemberLost(plr, group->OpponentsTeamRating); + else + at->OfflineMemberLost(guid, group->OpponentsTeamRating); + at->SaveToDB(); + } + } + + // remove group queue info if needed + if (group->Players.empty()) + { + m_QueuedGroups[queue_id][index].erase(group_itr); + delete group; + } + // if group wasn't empty, so it wasn't deleted, and player have left a rated + // queue -> everyone from the group should leave too + // don't remove recursively if already invited to bg! + else if (!group->IsInvitedToBGInstanceGUID && group->IsRated) + { + // remove next player, this is recursive + // first send removal information + if (Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + { + BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(group->BgTypeId, group->ArenaType); + uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); + plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to + // queue->removeplayer, it causes bugs + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); + plr2->GetSession()->SendPacket(&data); + } + // then actually delete, this may delete the group as well! + RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); + } +} - // check for iterator correctness - if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) +//Announce world message +void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue) +{ + if(ginfo->ArenaType) //if Arena { - // used when player left the queue, NOT used when porting to bg - if (decreaseInvitedCount) + if (sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE) && ginfo->IsRated) { - // if invited to bg, and should decrease invited count, then do it - if(group->IsInvitedToBGInstanceGUID) + BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId); + if (!bg) + return; + + char const* bgName = bg->GetName(); + if (isAddedToQueue) { - BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); - if (bg) - bg->DecreaseInvitedCount(group->Team); - if (bg && !bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE)) + if (sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY)) { - // no more players on battleground, set delete it - bg->SetDeleteThis(); + if(Player *plr = objmgr.GetPlayer(playerGUID)) + ChatHandler(plr).PSendSysMessage(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); } + else + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); + } + else + { + if (sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY)) + { + if(Player *plr = objmgr.GetPlayer(playerGUID)) + ChatHandler(plr).PSendSysMessage(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); + } + else + sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); } - // update the join queue, maybe now the player's group fits in a queue! - // not yet implemented (should store bgTypeId in group queue info?) - } - // remove player queue info - m_QueuedPlayers[queue_id].erase(itr); - // remove group queue info if needed - if(group->Players.empty()) - { - m_QueuedGroups[queue_id].erase(group_itr); - delete group; } - // NEEDS TESTING! - // group wasn't empty, so it wasn't deleted, and player have left a rated queue -> everyone from the group should leave too - // don't remove recursively if already invited to bg! - else if(!group->IsInvitedToBGInstanceGUID && decreaseInvitedCount && group->IsRated) + } + else //if BG + { + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) { - // remove next player, this is recursive - // first send removal information - if(Player *plr2 = objmgr.GetPlayer(group->Players.begin()->first)) + Player *plr = objmgr.GetPlayer(playerGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId); + if (!bg || !plr) + return; + + BGQueueIdBasedOnLevel queue_id = plr->GetBattleGroundQueueIdFromLevel(bg->GetTypeID()); + char const* bgName = bg->GetName(); + uint32 MinPlayers = bg->GetMinPlayersPerTeam(); + uint32 qHorde = 0; + uint32 qAlliance = 0; + uint32 q_min_level = (queue_id + 1) * 10; + uint32 q_max_level = std::max(q_min_level + 10, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + GroupsQueueType::const_iterator itr; + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qAlliance += (*itr)->Players.size(); + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].end(); ++itr) + if (!(*itr)->IsInvitedToBGInstanceGUID) + qHorde += (*itr)->Players.size(); + + // Show queue status to player only (when joining queue) + if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) { - BattleGround * bg = sBattleGroundMgr.GetBattleGroundTemplate(group->BgTypeId); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(group->BgTypeId,group->ArenaType); - uint32 queueSlot = plr2->GetBattleGroundQueueIndex(bgQueueTypeId); - plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr2->GetTeam(), queueSlot, STATUS_NONE, 0, 0); - plr2->GetSession()->SendPacket(&data); + ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, + bgName, q_min_level, q_max_level, qAlliance, MinPlayers, qHorde, MinPlayers); + } + // System message + else + { + sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, + bgName, q_min_level, q_max_level, qAlliance, MinPlayers, qHorde, MinPlayers); } - // then actually delete, this may delete the group as well! - RemovePlayer(group->Players.begin()->first,decreaseInvitedCount); } } } @@ -290,30 +440,48 @@ void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side) { // set side if needed - if(side) + if (side) ginfo->Team = side; - if(!ginfo->IsInvitedToBGInstanceGUID) + if (!ginfo->IsInvitedToBGInstanceGUID) { // not yet invited // set invitation ginfo->IsInvitedToBGInstanceGUID = bg->GetInstanceID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + BattleGroundTypeId bgTypeId = bg->GetTypeID(); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType()); + BGQueueIdBasedOnLevel queue_id = bg->GetQueueId(); + + // set ArenaTeamId for rated matches + if (bg->isArena() && bg->isRated()) + bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId); + + ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; + // loop through the players for(std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr) { - // set status - itr->second->InviteTime = getMSTime(); - itr->second->LastInviteTime = getMSTime(); - // get the player Player* plr = objmgr.GetPlayer(itr->first); - // if offline, skip him - if(!plr) + // if offline, skip him, this should not happen - player is removed from queue when he logs out + if (!plr) continue; // invite the player - sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(),ginfo->Team); + PlayerInvitedToBGUpdateAverageWaitTime(ginfo, queue_id); + //sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team); + + // set invited player counters + bg->IncreaseInvitedCount(ginfo->Team); + + plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID); + + // create remind invite events + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME)); + // create automatic remove events + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime); + plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); WorldPacket data; @@ -322,7 +490,7 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); // send status packet - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, side?side:plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); plr->GetSession()->SendPacket(&data); } return true; @@ -331,190 +499,292 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b return false; } -// used to recursively select groups from eligible groups -bool BattleGroundQueue::SelectionPool::Build(uint32 MinPlayers, uint32 MaxPlayers, EligibleGroups::iterator startitr) +/* +This function is inviting players to already running battlegrounds +Invitation type is based on config file +large groups are disadvantageous, because they will be kicked first if invitation type = 1 +*/ +void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id) { - // start from the specified start iterator - for(EligibleGroups::iterator itr1 = startitr; itr1 != m_CurrEligGroups->end(); ++itr1) + int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE); + int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE); + + //iterator for iterating through bg queue + GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin(); + //count of groups in queue - used to stop cycles + uint32 aliCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].size(); + //index to queue which group is current + uint32 aliIndex = 0; + for (; aliIndex < aliCount && m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*Ali_itr), aliFree); aliIndex++) + ++Ali_itr; + //the same thing for horde + GroupsQueueType::const_iterator Horde_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); + uint32 hordeCount = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].size(); + uint32 hordeIndex = 0; + for (; hordeIndex < hordeCount && m_SelectionPools[BG_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.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0) + 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[BG_TEAM_ALLIANCE].GetPlayerCount()); + int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + while( abs(diffAli - diffHorde) > 1 && (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() > 0 || m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() > 0) ) { - // if it fits in, select it - if(GetPlayerCount() + (*itr1)->Players.size() <= MaxPlayers) + //each cycle execution we need to kick at least 1 group + if (diffAli < diffHorde) { - EligibleGroups::iterator next = itr1; - ++next; - AddGroup((*itr1)); - if(GetPlayerCount() >= MinPlayers) + //kick alliance group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffHorde - diffAli)) + { + for (; aliIndex < aliCount && m_SelectionPools[BG_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[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + if (aliFree <= diffHorde + 1) + break; + m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffHorde - diffAli); + } + } + else + { + //kick horde group, add to pool new group if needed + if (m_SelectionPools[BG_TEAM_HORDE].KickGroup(diffAli - diffHorde)) + { + for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), (hordeFree >= diffAli) ? hordeFree - diffAli : 0); hordeIndex++) + ++Horde_itr; + } + if (!m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) { - // enough players are selected - return true; + if (hordeFree <= diffAli + 1) + break; + m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); } - // try building from the rest of the elig. groups - // if that succeeds, return true - if(Build(MinPlayers,MaxPlayers,next)) - return true; - // the rest didn't succeed, so this group cannot be included - RemoveGroup((*itr1)); } + //count diffs after small update + diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()); + diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); } - // build didn't succeed - return false; } -// this function is responsible for the selection of queued groups when trying to create new battlegrounds -bool BattleGroundQueue::BuildSelectionPool(uint32 bgTypeId, uint32 queue_id, uint32 MinPlayers, uint32 MaxPlayers, SelectionPoolBuildMode mode, uint8 ArenaType, bool isRated, uint32 MinRating, uint32 MaxRating, uint32 DisregardTime, uint32 excludeTeam) +// this method checks if premade versus premade battleground is possible +// then after 30 mins (default) in queue it moves premade group to normal queue +// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players +bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam) { - uint32 side; - switch(mode) - { - case NORMAL_ALLIANCE: - case ONESIDE_ALLIANCE_TEAM1: - case ONESIDE_ALLIANCE_TEAM2: - side = ALLIANCE; - break; - case NORMAL_HORDE: - case ONESIDE_HORDE_TEAM1: - case ONESIDE_HORDE_TEAM2: - side = HORDE; - break; - default: - //unknown mode, return false - sLog.outDebug("Battleground: unknown selection pool build mode, returning..."); - return false; - break; - } - - // initiate the groups eligible to create the bg - m_EligibleGroups.Init(&(m_QueuedGroups[queue_id]), bgTypeId, side, MaxPlayers, ArenaType, isRated, MinRating, MaxRating, DisregardTime, excludeTeam); - // init the selected groups (clear) - // and set m_CurrEligGroups pointer - // we set it this way to only have one EligibleGroups object to save some memory - m_SelectionPools[mode].Init(&m_EligibleGroups); - // build succeeded - if(m_SelectionPools[mode].Build(MinPlayers,MaxPlayers,m_EligibleGroups.begin())) - { - // the selection pool is set, return - sLog.outDebug("Battleground-debug: pool build succeeded, return true"); - sLog.outDebug("Battleground-debug: Player size for mode %u is %u", mode, m_SelectionPools[mode].GetPlayerCount()); - for(std::list<GroupQueueInfo* >::iterator itr = m_SelectionPools[mode].SelectedGroups.begin(); itr != m_SelectionPools[mode].SelectedGroups.end(); ++itr) + //check match + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && !m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty()) + { + //start premade match + //if groups aren't invited + GroupsQueueType::const_iterator ali_group, horde_group; + for( ali_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++ali_group) + if (!(*ali_group)->IsInvitedToBGInstanceGUID) + break; + for( horde_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group) + if (!(*horde_group)->IsInvitedToBGInstanceGUID) + break; + + if (ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end()) { - sLog.outDebug("Battleground-debug: queued group in selection with %u players",(*itr)->Players.size()); - for(std::map<uint64, PlayerQueueInfo * >::iterator itr2 = (*itr)->Players.begin(); itr2 != (*itr)->Players.end(); ++itr2) - sLog.outDebug("Battleground-debug: player in above group GUID %u", (uint32)(itr2->first)); + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam); + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*horde_group), MaxPlayersPerTeam); + //add groups/players from normal queue to size of bigger group + uint32 maxPlayers = std::min(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount(), m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()); + GroupsQueueType::const_iterator itr; + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[queue_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; } - return true; } - // failed to build a selection pool matching the given values + // 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 = getMSTime() - sWorld.getConfig(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH); + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].empty()) + { + GroupsQueueType::iterator itr = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].begin(); + if (!(*itr)->IsInvitedToBGInstanceGUID && ((*itr)->JoinTime < time_before || (*itr)->Players.size() < MinPlayersPerTeam)) + { + //we must insert group to normal queue and erase pointer from premade queue + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); + } + } + } + //selection pools are not set return false; } -// used to remove the Enter Battle window if the battle has already, but someone still has it -// (this can happen in arenas mainly, since the preparation is shorter than the timer for the bgqueueremove event -void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) +// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam +bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers) { - uint32 queue_id = bg->GetQueueType(); - uint32 bgInstanceId = bg->GetInstanceID(); - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - QueuedGroupsList::iterator itr, next; - for(itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); itr = next) - { - // must do this way, because the groupinfo will be deleted when all playerinfos are removed - GroupQueueInfo * ginfo = (*itr); - next = itr; - ++next; - // if group was invited to this bg instance, then remove all references - if(ginfo->IsInvitedToBGInstanceGUID == bgInstanceId) + GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + { + itr_team[i] = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); + for(; itr_team[i] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++(itr_team[i])) { - // after removing this much playerinfos, the ginfo will be deleted, so we'll use a for loop - uint32 to_remove = ginfo->Players.size(); - uint32 team = ginfo->Team; - for(int i = 0; i < to_remove; ++i) + if (!(*(itr_team[i]))->IsInvitedToBGInstanceGUID) { - // always remove the first one in the group - std::map<uint64, PlayerQueueInfo * >::iterator itr2 = ginfo->Players.begin(); - if(itr2 == ginfo->Players.end()) - { - sLog.outError("Empty Players in ginfo, this should never happen!"); - return; - } + m_SelectionPools[i].AddGroup(*(itr_team[i]), maxPlayers); + if (m_SelectionPools[i].GetPlayerCount() >= minPlayers) + break; + } + } + } + //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 = BG_TEAM_ALLIANCE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + j = BG_TEAM_HORDE; + if( sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) != 0 + && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_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[queue_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) % BG_TEAMS_COUNT].GetPlayerCount())) + break; + } + // do not allow to start bg with more than 2 players more on 1 faction + if (abs((int32)(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() - m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount())) > 2) + return false; + } + //allow 1v0 if debug bg + if (sBattleGroundMgr.isTesting() && bg_template->isBattleGround() && (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() || m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())) + return true; + //return true if there are enough players in selection pools - enable to work .debug bg command correctly + return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; +} - // get the player - Player * plr = objmgr.GetPlayer(itr2->first); - if(!plr) - { - sLog.outError("Player offline when trying to remove from GroupQueueInfo, this should never happen."); - continue; - } +// this method will check if we can invite players to same faction skirmish match +bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam) +{ + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam) + return false; + uint32 teamIndex = BG_TEAM_ALLIANCE; + uint32 otherTeam = BG_TEAM_HORDE; + uint32 otherTeamId = HORDE; + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam ) + { + teamIndex = BG_TEAM_HORDE; + otherTeam = BG_TEAM_ALLIANCE; + otherTeamId = 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[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); + for(; itr_team != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) + if (ginfo == *itr_team) + break; + if (itr_team == m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end()) + return false; + GroupsQueueType::iterator itr_team2 = itr_team; + ++itr_team2; + //invite players to other selection pool + for(; itr_team2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + 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; - // get the queueslot - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue - { - plr->RemoveBattleGroundQueueId(bgQueueTypeId); - // remove player from queue, this might delete the ginfo as well! don't use that pointer after this! - RemovePlayer(itr2->first, true); - // this is probably unneeded, since this player was already invited -> does not fit when initing eligible groups - // but updating the queue can't hurt - Update(bgQueueTypeId, bg->GetQueueType()); - // send info to client - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, team, queueSlot, STATUS_NONE, 0, 0); - plr->GetSession()->SendPacket(&data); - } + //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)->Team = otherTeamId; + //add team to other queue + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr); + //remove team from old queue + GroupsQueueType::iterator itr2 = itr_team; + ++itr2; + for(; itr2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2) + { + if (*itr2 == *itr) + { + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); + break; } } } + return true; } /* this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue it must be called after fully adding the members of a group to ensure group joining -should be called after removeplayer functions in some cases +should be called from BattleGround::RemovePlayer function in some cases */ -void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype, bool isRated, uint32 arenaRating) +void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated, uint32 arenaRating) { - if (queue_id >= MAX_BATTLEGROUND_QUEUES) - { - //this is error, that caused crashes (not in , but now it shouldn't) - sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash"); - return; - } - - //if no players in queue ... do nothing - if (m_QueuedGroups[queue_id].empty()) + //if no players in queue - do nothing + if( m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].empty() && + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].empty() ) return; - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bgTypeId, arenatype); - - //battleground with free slot for player should be always the last in this queue + //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 queue_id_based_on_level BGFreeSlotQueueType::iterator itr, next; for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { next = itr; ++next; - // battleground is running, so if: - // DO NOT allow queue manager to invite new player to running arena - if ((*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + // DO NOT allow queue manager to invite new player to arena + if( (*itr)->isBattleGround() && (*itr)->GetTypeID() == bgTypeId && (*itr)->GetQueueId() == queue_id && + (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE ) { - //we must check both teams BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) // and iterator is invalid - for(QueuedGroupsList::iterator itr = m_QueuedGroups[queue_id].begin(); itr != m_QueuedGroups[queue_id].end(); ++itr) - { - // did the group join for this bg type? - if((*itr)->BgTypeId != bgTypeId) - continue; - // if so, check if fits in - if(bg->GetFreeSlotsForTeam((*itr)->Team) >= (*itr)->Players.size()) - { - // if group fits in, invite it - InviteGroupToBG((*itr),bg,(*itr)->Team); - } - } + // clear selection pools + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); + + // call a function that does the job for us + FillPlayersToBG(bg, queue_id); + + // now everything is set, invite players + for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->Team); + for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg, (*citr)->Team); if (!bg->HasFreeSlots()) { - //remove BG from BGFreeSlotQueue + // remove BG from BGFreeSlotQueue bg->RemoveFromBGFreeSlotQueue(); } } @@ -523,25 +793,29 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype // finished iterating through the bgs with free slots, maybe we need to create a new bg BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if(!bg_template) + if (!bg_template) { sLog.outError("Battleground: Update: bg template not found for %u", bgTypeId); return; } - // get the min. players per team, properly for larger arenas as well. (must have full teams for arena matches!) uint32 MinPlayersPerTeam = bg_template->GetMinPlayersPerTeam(); uint32 MaxPlayersPerTeam = bg_template->GetMaxPlayersPerTeam(); - if(bg_template->isArena()) + if (sBattleGroundMgr.isTesting()) + MinPlayersPerTeam = 1; + if (bg_template->isArena()) { - if(sBattleGroundMgr.isArenaTesting()) + if (sBattleGroundMgr.isArenaTesting()) { MaxPlayersPerTeam = 1; MinPlayersPerTeam = 1; } else { - switch(arenatype) + //this switch can be much shorter + MaxPlayersPerTeam = arenaType; + MinPlayersPerTeam = arenaType; + /*switch(arenaType) { case ARENA_TYPE_2v2: MaxPlayersPerTeam = 2; @@ -555,293 +829,192 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype MaxPlayersPerTeam = 5; MinPlayersPerTeam = 5; break; - } + }*/ } } - // found out the minimum and maximum ratings the newly added team should battle against - // arenaRating is the rating of the latest joined team - uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference(); - // if no rating is specified, set maxrating to 0 - uint32 arenaMaxRating = (arenaRating == 0)? 0 : arenaRating + sBattleGroundMgr.GetMaxRatingDifference(); - uint32 discardTime = 0; - // 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 - if(sBattleGroundMgr.GetMaxRatingDifference() && getMSTime() >= sBattleGroundMgr.GetRatingDiscardTimer()) - discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); - - // try to build the selection pools - bool bAllyOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_ALLIANCE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bAllyOK) - sLog.outDebug("Battleground: ally pool successfully built"); - else - sLog.outDebug("Battleground: ally pool wasn't created"); - bool bHordeOK = BuildSelectionPool(bgTypeId, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam, NORMAL_HORDE, arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bHordeOK) - sLog.outDebug("Battleground: horde pool successfully built"); - else - sLog.outDebug("Battleground: horde pool wasn't created"); + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); - // if selection pools are ready, create the new bg - if (bAllyOK && bHordeOK) + if (bg_template->isBattleGround()) { - BattleGround * bg2 = 0; - // special handling for arenas - if(bg_template->isArena()) + //check if there is premade against premade match + if (CheckPremadeMatch(queue_id, MinPlayersPerTeam, MaxPlayersPerTeam)) { - // Find a random arena, that can be created - uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; - uint32 arena_num = urand(0,2); - if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + //create new battleground + BattleGround * bg2 = NULL; + if (!(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, 0, false))) { - sLog.outError("Battleground: couldn't create any arena instance!"); + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); return; } - - // set the MaxPlayersPerTeam values based on arenatype - // setting the min player values isn't needed, since we won't be using that value later on. - if(sBattleGroundMgr.isArenaTesting()) - { - bg2->SetMaxPlayersPerTeam(1); - bg2->SetMaxPlayers(2); - } - else - { - switch(arenatype) - { - case ARENA_TYPE_2v2: - bg2->SetMaxPlayersPerTeam(2); - bg2->SetMaxPlayers(4); - break; - case ARENA_TYPE_3v3: - bg2->SetMaxPlayersPerTeam(3); - bg2->SetMaxPlayers(6); - break; - case ARENA_TYPE_5v5: - bg2->SetMaxPlayersPerTeam(5); - bg2->SetMaxPlayers(10); - break; - default: - break; - } - } - } - else - { - // create new battleground - bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId); - } - - if(!bg2) - { - sLog.outError("Battleground: couldn't create bg %u",bgTypeId); - return; - } - - // start the joining of the bg - bg2->SetStatus(STATUS_WAIT_JOIN); - bg2->SetQueueType(queue_id); - // initialize arena / rating info - bg2->SetArenaType(arenatype); - // set rating - bg2->SetRated(isRated); - - std::list<GroupQueueInfo* >::iterator itr; - - // Send amount of invites based on the difference between the sizes of the two faction's queues - uint32 QUEUED_HORDE = m_SelectionPools[NORMAL_HORDE].SelectedGroups.size(); - uint32 QUEUED_ALLIANCE = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.size(); - uint16 maxbginvites = 0; - - if(QUEUED_ALLIANCE <= QUEUED_HORDE) - maxbginvites = QUEUED_ALLIANCE; - else - maxbginvites = QUEUED_HORDE; - - // invite groups from horde selection pool - uint16 invitecounter = 0; - for(itr = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_HORDE].SelectedGroups.end(); ++itr) - { - if (invitecounter >= maxbginvites) - return; - InviteGroupToBG((*itr),bg2,HORDE); - ++invitecounter; - } - - // invite groups from ally selection pool - invitecounter = 0; - for(itr = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); itr != m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.end(); ++itr) - { - if (invitecounter >= maxbginvites) - return; - InviteGroupToBG((*itr),bg2,ALLIANCE); - ++invitecounter; - } - - if (isRated) - { - std::list<GroupQueueInfo* >::iterator itr_alliance = m_SelectionPools[NORMAL_ALLIANCE].SelectedGroups.begin(); - std::list<GroupQueueInfo* >::iterator itr_horde = m_SelectionPools[NORMAL_HORDE].SelectedGroups.begin(); - (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; - sLog.outDebug("setting opposite team rating for team %u to %u", (*itr_alliance)->ArenaTeamId, (*itr_alliance)->OpponentsTeamRating); - (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; - sLog.outDebug("setting opposite team rating for team %u to %u", (*itr_horde)->ArenaTeamId, (*itr_horde)->OpponentsTeamRating); + //invite those selection pools + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg2, (*citr)->Team); + //start bg + bg2->StartBattleGround(); + //clear structures + m_SelectionPools[BG_TEAM_ALLIANCE].Init(); + m_SelectionPools[BG_TEAM_HORDE].Init(); } - - // start the battleground - bg2->StartBattleGround(); } - // there weren't enough players for a "normal" match - // if arena, enable horde versus horde or alliance versus alliance teams here - - else if(bg_template->isArena()) + // now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena) + if (!isRated) { - bool bOneSideHordeTeam1 = false, bOneSideHordeTeam2 = false; - bool bOneSideAllyTeam1 = false, bOneSideAllyTeam2 = false; - bOneSideHordeTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bOneSideHordeTeam1) + // if there are enough players in pools, start new battleground or non rated arena + if (CheckNormalMatch(bg_template, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam) + || (bg_template->isArena() && CheckSkirmishForSameFaction(queue_id, MinPlayersPerTeam)) ) { - // one team has been selected, find out if other can be selected too - std::list<GroupQueueInfo* >::iterator itr; - // temporarily change the team side to enable building the next pool excluding the already selected groups - for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=ALLIANCE; - - bOneSideHordeTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_HORDE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime, (*(m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); - - // change back the team to horde - for(itr = m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_HORDE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=HORDE; - - if(!bOneSideHordeTeam2) - bOneSideHordeTeam1 = false; - } - if(!bOneSideHordeTeam1) - { - // check for one sided ally - bOneSideAllyTeam1 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM1,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime); - if(bOneSideAllyTeam1) + // we successfully created a pool + BattleGround * bg2 = NULL; + if (!(bg2 = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, false))) { - // one team has been selected, find out if other can be selected too - std::list<GroupQueueInfo* >::iterator itr; - // temporarily change the team side to enable building the next pool excluding the already selected groups - for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=HORDE; - - bOneSideAllyTeam2 = BuildSelectionPool(bgTypeId, queue_id,MaxPlayersPerTeam,MaxPlayersPerTeam,ONESIDE_ALLIANCE_TEAM2,arenatype, isRated, arenaMinRating, arenaMaxRating, discardTime,(*(m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin()))->ArenaTeamId); - - // change back the team to ally - for(itr = m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.begin(); itr != m_SelectionPools[ONESIDE_ALLIANCE_TEAM1].SelectedGroups.end(); ++itr) - (*itr)->Team=ALLIANCE; + sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId); + return; } - if(!bOneSideAllyTeam2) - bOneSideAllyTeam1 = false; + // invite those selection pools + for(uint32 i = 0; i < BG_TEAMS_COUNT; i++) + for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr) + InviteGroupToBG((*citr), bg2, (*citr)->Team); + // start bg + bg2->StartBattleGround(); } - // 1-sided BuildSelectionPool() will work, because the MinPlayersPerTeam == MaxPlayersPerTeam in every arena!!!! - if( (bOneSideHordeTeam1 && bOneSideHordeTeam2) || - (bOneSideAllyTeam1 && bOneSideAllyTeam2) ) + } + else if (bg_template->isArena()) + { + // 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 ) { - // which side has enough players? - uint32 side = 0; - SelectionPoolBuildMode mode1, mode2; - // find out what pools are we using - if(bOneSideAllyTeam1 && bOneSideAllyTeam2) + GroupQueueInfo* front1 = NULL; + GroupQueueInfo* front2 = NULL; + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].empty()) { - side = ALLIANCE; - mode1 = ONESIDE_ALLIANCE_TEAM1; - mode2 = ONESIDE_ALLIANCE_TEAM2; + front1 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].front(); + arenaRating = front1->ArenaTeamRating; } - else + if (!m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].empty()) { - side = HORDE; - mode1 = ONESIDE_HORDE_TEAM1; - mode2 = ONESIDE_HORDE_TEAM2; + front2 = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].front(); + arenaRating = front2->ArenaTeamRating; } - - // create random arena - uint8 arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; - uint32 arena_num = urand(0,2); - BattleGround* bg2 = NULL; - if( !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[arena_num%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+1)%3])) && - !(bg2 = sBattleGroundMgr.CreateNewBattleGround(arenas[(arena_num+2)%3])) ) + if (front1 && front2) { - sLog.outError("Could not create arena."); - return; + if (front1->JoinTime < front2->JoinTime) + arenaRating = front1->ArenaTeamRating; } + else if (!front1 && !front2) + return; //queues are empty + } - sLog.outDebug("Battleground: One-faction arena created."); - // init stats - if(sBattleGroundMgr.isArenaTesting()) + //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 + uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer(); + + // we need to find 2 teams which will play next game + + GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT]; + + //optimalization : --- we dont need to use selection_pools - each update we select max 2 groups + + for(uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++) + { + // take the group that joined first + itr_team[i] = m_QueuedGroups[queue_id][i].begin(); + for(; itr_team[i] != m_QueuedGroups[queue_id][i].end(); ++(itr_team[i])) { - bg2->SetMaxPlayersPerTeam(1); - bg2->SetMaxPlayers(2); + // if group match conditions, then add it to pool + if( !(*itr_team[i])->IsInvitedToBGInstanceGUID + && (((*itr_team[i])->ArenaTeamRating >= arenaMinRating && (*itr_team[i])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[i])->JoinTime < discardTime) ) + { + m_SelectionPools[i].AddGroup((*itr_team[i]), MaxPlayersPerTeam); + // break for cycle to be able to start selecting another group from same faction queue + break; + } } - else + } + // now we are done if we have 2 groups - ali vs horde! + // if we don't have, we must try to continue search in same queue + // tmp variables are correctly set + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in HORDE queue + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + itr_team[BG_TEAM_ALLIANCE] = itr_team[BG_TEAM_HORDE]; + ++itr_team[BG_TEAM_ALLIANCE]; + for(; itr_team[BG_TEAM_ALLIANCE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++(itr_team[BG_TEAM_ALLIANCE])) { - switch(arenatype) + if( !(*itr_team[BG_TEAM_ALLIANCE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_ALLIANCE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_ALLIANCE])->JoinTime < discardTime) ) { - case ARENA_TYPE_2v2: - bg2->SetMaxPlayersPerTeam(2); - bg2->SetMaxPlayers(4); - break; - case ARENA_TYPE_3v3: - bg2->SetMaxPlayersPerTeam(3); - bg2->SetMaxPlayers(6); + m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam); break; - case ARENA_TYPE_5v5: - bg2->SetMaxPlayersPerTeam(5); - bg2->SetMaxPlayers(10); - break; - default: + } + } + } + // this code isn't much userfriendly - but it is supposed to continue search for mathing group in ALLIANCE queue + if (m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == 0 && m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount()) + { + itr_team[BG_TEAM_HORDE] = itr_team[BG_TEAM_ALLIANCE]; + ++itr_team[BG_TEAM_HORDE]; + for(; itr_team[BG_TEAM_HORDE] != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end(); ++(itr_team[BG_TEAM_HORDE])) + { + if( !(*itr_team[BG_TEAM_HORDE])->IsInvitedToBGInstanceGUID + && (((*itr_team[BG_TEAM_HORDE])->ArenaTeamRating >= arenaMinRating && (*itr_team[BG_TEAM_HORDE])->ArenaTeamRating <= arenaMaxRating) + || (*itr_team[BG_TEAM_HORDE])->JoinTime < discardTime) ) + { + m_SelectionPools[BG_TEAM_HORDE].AddGroup((*itr_team[BG_TEAM_HORDE]), MaxPlayersPerTeam); break; } } + } - bg2->SetRated(isRated); - - // assigned team of the other group - uint32 other_side; - if(side == ALLIANCE) - other_side = HORDE; - else - other_side = ALLIANCE; - - // start the joining of the bg - bg2->SetStatus(STATUS_WAIT_JOIN); - bg2->SetQueueType(queue_id); - // initialize arena / rating info - bg2->SetArenaType(arenatype); - - std::list<GroupQueueInfo* >::iterator itr; - - // invite players from the first group as horde players (actually green team) - for(itr = m_SelectionPools[mode1].SelectedGroups.begin(); itr != m_SelectionPools[mode1].SelectedGroups.end(); ++itr) + //if we have 2 teams, then start new arena and invite players! + if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount()) + { + BattleGround* arena = NULL; + if (!(arena = sBattleGroundMgr.CreateNewBattleGround(bgTypeId, queue_id, arenaType, true))) { - InviteGroupToBG((*itr),bg2,HORDE); + sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!"); + return; } - // invite players from the second group as ally players (actually gold team) - for(itr = m_SelectionPools[mode2].SelectedGroups.begin(); itr != m_SelectionPools[mode2].SelectedGroups.end(); ++itr) + (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating); + (*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating; + sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamId, (*(itr_team[BG_TEAM_HORDE]))->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 ((*(itr_team[BG_TEAM_ALLIANCE]))->Team != ALLIANCE) { - InviteGroupToBG((*itr),bg2,ALLIANCE); + // add to alliance queue + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].push_front(*(itr_team[BG_TEAM_ALLIANCE])); + // erase from horde queue + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].erase(itr_team[BG_TEAM_ALLIANCE]); + itr_team[BG_TEAM_ALLIANCE] = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].begin(); } - - if (isRated) + if ((*(itr_team[BG_TEAM_HORDE]))->Team != HORDE) { - std::list<GroupQueueInfo* >::iterator itr_alliance = m_SelectionPools[mode1].SelectedGroups.begin(); - std::list<GroupQueueInfo* >::iterator itr_horde = m_SelectionPools[mode2].SelectedGroups.begin(); - (*itr_alliance)->OpponentsTeamRating = (*itr_horde)->ArenaTeamRating; - (*itr_horde)->OpponentsTeamRating = (*itr_alliance)->ArenaTeamRating; + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].push_front(*(itr_team[BG_TEAM_HORDE])); + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]); + itr_team[BG_TEAM_HORDE] = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); } - bg2->StartBattleGround(); + InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE); + InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE); + + sLog.outDebug("Starting rated arena match!"); + + arena->StartBattleGround(); } } } @@ -853,36 +1026,29 @@ void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id, uint8 arenatype bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* plr = objmgr.GetPlayer( m_PlayerGuid ); - // player logged off (we should do nothing, he is correctly removed from queue in another procedure) if (!plr) return true; - // Player can be in another BG queue and must be removed in normal way in any case - // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG) - // if (plr->GetBattleGroundId() > 0) - // return true; - - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //if battleground ended and its instance deleted - do nothing if (!bg) return true; - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); + if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue or in battleground { - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + // check if player is invited to this bg + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if( qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID + && qItr->second.GroupInfo->RemoveInviteTime == m_RemoveTime ) { - // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; - BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); - if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) - { - WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, qItr->second.GroupInfo->Team, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); - plr->GetSession()->SendPacket(&data); - } + WorldPacket data; + //we must send remaining time in queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, qItr->second.GroupInfo->ArenaType); + plr->GetSession()->SendPacket(&data); } } return true; //event will be deleted @@ -890,10 +1056,18 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) void BGQueueInviteEvent::Abort(uint64 /*e_time*/) { - //this should not be called - sLog.outError("Battleground invite event ABORTED!"); + //do nothing } +/* + this event has many possibilities when it is executed: + 1. player is in battleground ( he clicked enter on invitation window ) + 2. player left battleground queue and he isn't there any more + 3. player left battleground queue and he joined it again and IsInvitedToBGInstanceGUID = 0 + 4. player left queue and he joined again and he has been invited to same battleground again -> we should not remove him from queue yet + 5. player is invited to bg and he didn't choose what to do and timer expired - only in this condition we should call queue::RemovePlayer + we must remove player in the 5. case even if battleground object doesn't exist! +*/ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { Player* plr = objmgr.GetPlayer( m_PlayerGuid ); @@ -901,40 +1075,33 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) // player logged off (we should do nothing, he is correctly removed from queue in another procedure) return true; - BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); - if (!bg) - return true; - - sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId); + //battleground can be deleted already when we are removing queue info + //bg pointer can be NULL! so use it carefully! - uint32 bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId); - if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId); + if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue, or in Battleground { - // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems - BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].find(m_PlayerGuid); - if (qMapItr != sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()].end() && qMapItr->second.GroupInfo && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + // check if player is in queue for this BG and if we are removing his invite event + BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].m_QueuedPlayers; + BattleGroundQueue::QueuedPlayersMap::iterator qMapItr = qpMap.find(m_PlayerGuid); + if( qMapItr != qpMap.end() && qMapItr->second.GroupInfo + && qMapItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID + && qMapItr->second.GroupInfo->RemoveInviteTime == m_RemoveTime ) { - if (qMapItr->second.GroupInfo->IsRated) - { - ArenaTeam * at = objmgr.GetArenaTeamById(qMapItr->second.GroupInfo->ArenaTeamId); - if (at) - { - sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u", GUID_LOPART(plr->GetGUID()), qMapItr->second.GroupInfo->OpponentsTeamRating); - at->MemberLost(plr, qMapItr->second.GroupInfo->OpponentsTeamRating); - at->SaveToDB(); - } - } - plr->RemoveBattleGroundQueueId(bgQueueTypeId); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true); - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgQueueTypeId, bg->GetQueueType()); + sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID); + + plr->RemoveBattleGroundQueueId(m_BgQueueTypeId); + sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].RemovePlayer(m_PlayerGuid, true); + //update queues if battleground isn't ended + if (bg) + sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].Update(m_BgTypeId, bg->GetQueueId()); + WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, m_PlayersTeam, queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } } - else - sLog.outDebug("Battleground: Player was already removed from queue"); //event will be deleted return true; @@ -942,82 +1109,104 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) { - //this should not be called - sLog.outError("Battleground remove event ABORTED!"); + //do nothing } /*********************************************************/ /*** BATTLEGROUND MANAGER ***/ /*********************************************************/ -BattleGroundMgr::BattleGroundMgr() +BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false) { - m_BattleGrounds.clear(); - m_AutoDistributePoints = (bool)sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS); - m_MaxRatingDifference = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); - m_RatingDiscardTimer = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); - m_PrematureFinishTimer = sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); - m_NextRatingDiscardUpdate = m_RatingDiscardTimer; - m_AutoDistributionTimeChecker = 0; - m_ArenaTesting = false; + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) + m_BattleGrounds[i].clear(); + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); + m_Testing=false; } BattleGroundMgr::~BattleGroundMgr() { - BattleGroundSet::iterator itr, next; - for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + DeleteAllBattleGrounds(); +} + +void BattleGroundMgr::DeleteAllBattleGrounds() +{ + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - next = itr; - ++next; - BattleGround * bg = itr->second; - m_BattleGrounds.erase(itr); - delete bg; + for(BattleGroundSet::iterator itr = m_BattleGrounds[i].begin(); itr != m_BattleGrounds[i].end();) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr++); + if (!m_ClientBattleGroundIds[i][bg->GetQueueId()].empty()) + m_ClientBattleGroundIds[i][bg->GetQueueId()].erase(bg->GetClientInstanceID()); + delete bg; + } + } + + // destroy template battlegrounds that listed only in queues (other already terminated) + for(uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId) + { + // ~BattleGround call unregistring BG from queue + while(!BGFreeSlotQueue[bgTypeId].empty()) + delete BGFreeSlotQueue[bgTypeId].front(); } - m_BattleGrounds.clear(); } // used to update running battlegrounds, and delete finished ones -void BattleGroundMgr::Update(time_t diff) +void BattleGroundMgr::Update(uint32 diff) { BattleGroundSet::iterator itr, next; - for(itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); itr = next) + for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - next = itr; - ++next; - itr->second->Update(diff); - // use the SetDeleteThis variable - // direct deletion caused crashes - if(itr->second->m_SetDeleteThis) + itr = m_BattleGrounds[i].begin(); + // skip updating battleground template + if (itr != m_BattleGrounds[i].end()) + ++itr; + for(; itr != m_BattleGrounds[i].end(); itr = next) { - BattleGround * bg = itr->second; - m_BattleGrounds.erase(itr); - delete bg; + next = itr; + ++next; + itr->second->Update(diff); + // use the SetDeleteThis variable + // direct deletion caused crashes + if (itr->second->m_SetDeleteThis) + { + BattleGround * bg = itr->second; + m_BattleGrounds[i].erase(itr); + if (!m_ClientBattleGroundIds[i][bg->GetQueueId()].empty()) + m_ClientBattleGroundIds[i][bg->GetQueueId()].erase(bg->GetClientInstanceID()); + delete bg; + } } } // if rating difference counts, maybe force-update queues - if(m_MaxRatingDifference) + if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) { // it's time to force update - if(m_NextRatingDiscardUpdate < diff) + if (m_NextRatingDiscardUpdate < diff) { // forced update for level 70 rated arenas - m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA,6,ARENA_TYPE_2v2,true,0); - m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA,6,ARENA_TYPE_3v3,true,0); - m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA,6,ARENA_TYPE_5v5,true,0); - m_NextRatingDiscardUpdate = m_RatingDiscardTimer; + sLog.outDebug("BattleGroundMgr: UPDATING ARENA QUEUES"); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_2v2, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_2v2].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_2v2, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_3v3, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_3v3].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_3v3, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_79, ARENA_TYPE_5v5, true, 0); + m_BattleGroundQueues[BATTLEGROUND_QUEUE_5v5].Update(BATTLEGROUND_AA, QUEUE_ID_MAX_LEVEL_80, ARENA_TYPE_5v5, true, 0); + m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); } else m_NextRatingDiscardUpdate -= diff; } - if(m_AutoDistributePoints) + if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { - if(m_AutoDistributionTimeChecker < diff) + if (m_AutoDistributionTimeChecker < diff) { if(time(NULL) > m_NextAutoDistributionTime) { DistributeArenaPoints(); m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); - CharacterDatabase.PExecute("UPDATE saved_variables SET NextArenaPointDistributionTime = '"I64FMTD"'", m_NextAutoDistributionTime); + CharacterDatabase.PExecute("UPDATE saved_variables SET NextArenaPointDistributionTime = '"UI64FMTD"'", m_NextAutoDistributionTime); } m_AutoDistributionTimeChecker = 600000; // check 10 minutes } @@ -1026,10 +1215,11 @@ void BattleGroundMgr::Update(time_t diff) } } -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype, uint8 israted) +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype) { // we can be in 3 queues in same time... - if(StatusID == 0) + + if (StatusID == 0 || !bg) { data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3); *data << uint32(QueueSlot); // queue id (0...2) @@ -1040,10 +1230,11 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time // uint64 in client - *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); - *data << uint32(0); // unknown + *data << uint64( uint64(arenatype) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint32(bg->GetClientInstanceID()); // alliance/horde for BG and skirmish/rated for Arenas - *data << uint8(bg->isArena() ? ( israted ? israted : bg->isRated() ) : bg->GetTeamIndexByTeamId(team)); + // following displays the minimap-icon 0 = faction icon 1 = arenaicon + *data << uint8(bg->isRated()); /* *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! switch(bg->GetTypeID()) // value depends on bg id { @@ -1071,12 +1262,21 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro case BATTLEGROUND_RL: *data << uint8(8); break; + case BATTLEGROUND_SA: + *data << uint8(9); + break; + case BATTLEGROUND_DS: + *data << uint8(10); + break; + case BATTLEGROUND_RV: + *data << uint8(11); + break; default: // unknown *data << uint8(0); break; } - if(bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) + if (bg->isArena() && (StatusID == STATUS_WAIT_QUEUE)) *data << uint32(BATTLEGROUND_AA); // all arenas I don't think so. else *data << uint32(bg->GetTypeID()); // BG id from DBC @@ -1084,17 +1284,14 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro *data << uint16(0x1F90); // unk value 8080 *data << uint32(bg->GetInstanceID()); // instance id - if(bg->isBattleGround()) - *data << uint8(bg->GetTeamIndexByTeamId(team)); // team - else - *data << uint8(israted?israted:bg->isRated()); // is rated battle + *data << uint8(bg->isArena()); // minimap-icon 0=faction 1=arena */ *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? + *data << uint32(Time2); // time in queue, updated every minute!, milliseconds break; case STATUS_WAIT_JOIN: // status_invite *data << uint32(bg->GetMapId()); // map id @@ -1102,7 +1299,7 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro break; case STATUS_IN_PROGRESS: // status_in_progress *data << uint32(bg->GetMapId()); // map id - *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds + *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(0x1); // unk sometimes 0x0! break; @@ -1115,33 +1312,34 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { uint8 type = (bg->isArena() ? 1 : 0); - // last check on 2.4.1 + // last check on 3.0.3 data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); - *data << uint8(type); // seems to be type (battleground=0/arena=1) + *data << uint8(type); // type (battleground=0/arena=1) if(type) // arena { // it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H for(int i = 1; i >= 0; --i) { - *data << uint32(3000-bg->m_ArenaTeamRatingChanges[i]); // rating change: showed value - 3000 - *data << uint32(3999); // huge thanks for TOM_RUS for this! + *data << uint32(bg->m_ArenaTeamRatingChanges[i]); + *data << uint32(3999); // huge thanks for TOM_RUS for this! + *data << uint32(0); // added again in 3.1 sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]); } for(int i = 1; i >= 0; --i) { uint32 at_id = bg->m_ArenaTeamIds[i]; ArenaTeam * at = objmgr.GetArenaTeamById(at_id); - if(at) + if (at) *data << at->GetName(); - else//*/ + else *data << (uint8)0; } } - if(bg->GetWinner() == 2) + if (bg->GetStatus() != STATUS_WAIT_LEAVE) { - *data << uint8(0); // bg in progress + *data << uint8(0); // bg not ended } else { @@ -1155,10 +1353,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) { *data << (uint64)itr->first; *data << (int32)itr->second->KillingBlows; - Player *plr = objmgr.GetPlayer(itr->first); - uint32 team = bg->GetPlayerTeam(itr->first); - if(!team && plr) team = plr->GetTeam(); - if(type == 0) + if (type == 0) { *data << (int32)itr->second->HonorableKills; *data << (int32)itr->second->Deaths; @@ -1166,18 +1361,12 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } else { - // that part probably wrong - if(plr) - { - if(team == HORDE) - *data << uint8(0); - else if(team == ALLIANCE) - { - *data << uint8(1); - } - else - *data << uint8(0); - } + Player *plr = objmgr.GetPlayer(itr->first); + uint32 team = bg->GetPlayerTeam(itr->first); + if (!team && plr) + team = plr->GetTeam(); + if (( bg->GetWinner()==0 && team == ALLIANCE ) || ( bg->GetWinner()==1 && team==HORDE )) + *data << uint8(1); else *data << uint8(0); } @@ -1204,13 +1393,16 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended break; case BATTLEGROUND_EY: - *data << (uint32)0x00000001; // count of next fields + *data << (uint32)0x00000001; // count of next fields *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures break; case BATTLEGROUND_NA: case BATTLEGROUND_BE: case BATTLEGROUND_AA: case BATTLEGROUND_RL: + case BATTLEGROUND_SA: // wotlk + case BATTLEGROUND_DS: // wotlk + case BATTLEGROUND_RV: // wotlk *data << (int32)0; // 0 break; default: @@ -1221,7 +1413,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) } } -void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId) +void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId) { /*bgTypeId is: 0 - Your group has joined a battleground queue, but you are not eligible @@ -1249,10 +1441,10 @@ void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) *data << uint32(soundid); } -void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr) +void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid) { data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); - *data << uint64(plr->GetGUID()); + *data << uint64(guid); } void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr) @@ -1261,62 +1453,97 @@ void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Pla *data << uint64(plr->GetGUID()); } -void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID, uint32 team) +BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId) { - // set invited player counters: - BattleGround* bg = GetBattleGround(bgInstanceGUID); - if(!bg) - return; - bg->IncreaseInvitedCount(team); + //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 NULL; - plr->SetInviteForBattleGroundQueueType(BGQueueTypeId(bg->GetTypeID(),bg->GetArenaType()), bgInstanceGUID); + if (bg->isArena()) + return GetBattleGround(instanceId, bgTypeId); - // set the arena teams for rated matches - if(bg->isArena() && bg->isRated()) + for(BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr) { - switch(bg->GetArenaType()) + if (itr->second->GetClientInstanceID() == instanceId) + return itr->second; + } + return NULL; +} + +BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId) +{ + //search if needed + BattleGroundSet::iterator itr; + if (bgTypeId == BATTLEGROUND_TYPE_NONE) + { + for(uint32 i = BATTLEGROUND_AV; i < MAX_BATTLEGROUND_TYPE_ID; i++) { - case ARENA_TYPE_2v2: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(0)); - break; - case ARENA_TYPE_3v3: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(1)); - break; - case ARENA_TYPE_5v5: - bg->SetArenaTeamIdForTeam(team, plr->GetArenaTeamId(2)); - break; - default: - break; + itr = m_BattleGrounds[i].find(InstanceID); + if (itr != m_BattleGrounds[i].end()) + return itr->second; } + return NULL; } + itr = m_BattleGrounds[bgTypeId].find(InstanceID); + return ( (itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL ); +} - // create invite events: - //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events - BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); - plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2)); - BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, team); - plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); +BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId) +{ + //map is sorted and we can be sure that lowest instance id has only BG template + return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second; } -BattleGround * BattleGroundMgr::GetBattleGroundTemplate(uint32 bgTypeId) +uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id) { - return BGFreeSlotQueue[bgTypeId].empty() ? NULL : BGFreeSlotQueue[bgTypeId].back(); + 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<uint32> - but that would only make code unreadable + uint32 lastId = 0; + for(std::set<uint32>::iterator itr = m_ClientBattleGroundIds[bgTypeId][queue_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][queue_id].end();) + { + if( (++lastId) != *itr) //if there is a gap between the ids, we will break.. + break; + lastId = *itr; + } + m_ClientBattleGroundIds[bgTypeId][queue_id].insert(lastId + 1); + return lastId + 1; } // create a new battleground that will really be used to play -BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) +BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated) { - BattleGround *bg = NULL; - // get the template BG BattleGround *bg_template = GetBattleGroundTemplate(bgTypeId); - - if(!bg_template) + if (!bg_template) { sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); - return 0; + return NULL; + } + + //for arenas there is random map used + if (bg_template->isArena()) + { + BattleGroundTypeId arenas[] = {BATTLEGROUND_NA, BATTLEGROUND_BE, BATTLEGROUND_RL}; + uint32 arena_num = urand(0,2); + bgTypeId = arenas[arena_num]; + bg_template = GetBattleGroundTemplate(bgTypeId); + if (!bg_template) + { + sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId); + return NULL; + } } + BattleGround *bg = NULL; // create a copy of the BG template switch(bgTypeId) { @@ -1344,42 +1571,47 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(uint32 bgTypeId) case BATTLEGROUND_RL: bg = new BattleGroundRL(*(BattleGroundRL*)bg_template); break; + case BATTLEGROUND_SA: + bg = new BattleGroundSA(*(BattleGroundSA*)bg_template); + break; + case BATTLEGROUND_DS: + bg = new BattleGroundDS(*(BattleGroundDS*)bg_template); + break; + case BATTLEGROUND_RV: + bg = new BattleGroundRV(*(BattleGroundRV*)bg_template); + break; default: - //bg = new BattleGround; + //error, but it is handled few lines above return 0; - break; // placeholder for non implemented BG } // generate a new instance id bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id + bg->SetClientInstanceID(CreateClientVisibleInstanceId(bgTypeId, queue_id)); // reset the new bg (set status to status_wait_queue from status_none) bg->Reset(); - /* will be setup in BG::Update() when the first player is ported in - if(!(bg->SetupBattleGround())) - { - sLog.outError("BattleGround: CreateNewBattleGround: SetupBattleGround failed for bg %u", bgTypeId); - delete bg; - return 0; - } - */ + // start the joining of the bg + bg->SetStatus(STATUS_WAIT_JOIN); + bg->SetQueueId(queue_id); + bg->SetArenaType(arenaType); + bg->SetRated(isRated); // add BG to free slot queue bg->AddToBGFreeSlotQueue(); // add bg to update list - AddBattleGround(bg->GetInstanceID(), bg); + AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); return bg; } // used to create the BG templates -uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) +uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) { // Create the BG BattleGround *bg = NULL; - switch(bgTypeId) { case BATTLEGROUND_AV: bg = new BattleGroundAV; break; @@ -1390,35 +1622,27 @@ uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPer case BATTLEGROUND_AA: bg = new BattleGroundAA; break; case BATTLEGROUND_EY: bg = new BattleGroundEY; break; case BATTLEGROUND_RL: bg = new BattleGroundRL; break; + case BATTLEGROUND_SA: bg = new BattleGroundSA; break; + case BATTLEGROUND_DS: bg = new BattleGroundDS; break; + case BATTLEGROUND_RV: bg = new BattleGroundRV; break; default:bg = new BattleGround; break; // placeholder for non implemented BG } bg->SetMapId(MapID); - - bg->Reset(); - - BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); - //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed - if (bl) - { - bg->SetArenaorBGType(bl->type == TYPE_ARENA); - } - bg->SetTypeID(bgTypeId); - bg->SetInstanceID(0); // template bg, instance id is 0 + bg->SetInstanceID(0); + bg->SetArenaorBGType(IsArena); bg->SetMinPlayersPerTeam(MinPlayersPerTeam); bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); - bg->SetMinPlayers(MinPlayersPerTeam*2); - bg->SetMaxPlayers(MaxPlayersPerTeam*2); + bg->SetMinPlayers(MinPlayersPerTeam * 2); + bg->SetMaxPlayers(MaxPlayersPerTeam * 2); bg->SetName(BattleGroundName); bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); bg->SetLevelRange(LevelMin, LevelMax); - //add BattleGround instance to FreeSlotQueue (.back() will return the template!) - bg->AddToBGFreeSlotQueue(); - - // do NOT add to update list, since this is a template battleground! + // add bg to update list + AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg); // return some not-null value, bgTypeId is good enough for me return bgTypeId; @@ -1431,13 +1655,14 @@ void BattleGroundMgr::CreateInitialBattleGrounds() uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; BattlemasterListEntry const *bl; WorldSafeLocsEntry const *start; + bool IsArena; uint32 count = 0; // 0 1 2 3 4 5 6 7 8 QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template"); - if(!result) + if (!result) { barGoLink bar(1); @@ -1455,44 +1680,46 @@ void BattleGroundMgr::CreateInitialBattleGrounds() Field *fields = result->Fetch(); bar.step(); - uint32 bgTypeID = fields[0].GetUInt32(); + uint32 bgTypeID_ = fields[0].GetUInt32(); - // can be overwrited by values from DB - bl = sBattlemasterListStore.LookupEntry(bgTypeID); - if(!bl) + // can be overwrite by values from DB + bl = sBattlemasterListStore.LookupEntry(bgTypeID_); + if (!bl) { - sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.",bgTypeID); + sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_); continue; } - MaxPlayersPerTeam = bl->maxplayersperteam; - MinPlayersPerTeam = bl->maxplayersperteam/2; - MinLvl = bl->minlvl; - MaxLvl = bl->maxlvl; + BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); - if(fields[1].GetUInt32()) - MinPlayersPerTeam = fields[1].GetUInt32(); - - if(fields[2].GetUInt32()) - MaxPlayersPerTeam = fields[2].GetUInt32(); - - if(fields[3].GetUInt32()) - MinLvl = fields[3].GetUInt32(); - - if(fields[4].GetUInt32()) - MaxLvl = fields[4].GetUInt32(); + IsArena = (bl->type == TYPE_ARENA); + MinPlayersPerTeam = fields[1].GetUInt32(); + MaxPlayersPerTeam = fields[2].GetUInt32(); + MinLvl = fields[3].GetUInt32(); + MaxLvl = fields[4].GetUInt32(); + //check values from DB + if (MaxPlayersPerTeam == 0 || MinPlayersPerTeam == 0 || MinPlayersPerTeam > MaxPlayersPerTeam) + { + MaxPlayersPerTeam = bl->maxplayersperteam; + MinPlayersPerTeam = bl->maxplayersperteam / 2; + } + if (MinLvl == 0 || MaxLvl == 0 || MinLvl > MaxLvl) + { + MinLvl = bl->minlvl; + MaxLvl = bl->maxlvl; + } start1 = fields[5].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start1); - if(start) + if (start) { AStartLoc[0] = start->x; AStartLoc[1] = start->y; AStartLoc[2] = start->z; AStartLoc[3] = fields[6].GetFloat(); } - else if(bgTypeID == BATTLEGROUND_AA) + else if (bgTypeID == BATTLEGROUND_AA) { AStartLoc[0] = 0; AStartLoc[1] = 0; @@ -1501,21 +1728,21 @@ void BattleGroundMgr::CreateInitialBattleGrounds() } else { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.",bgTypeID,start1); + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTypeID, start1); continue; } start2 = fields[7].GetUInt32(); start = sWorldSafeLocsStore.LookupEntry(start2); - if(start) + if (start) { HStartLoc[0] = start->x; HStartLoc[1] = start->y; HStartLoc[2] = start->z; HStartLoc[3] = fields[8].GetFloat(); } - else if(bgTypeID == BATTLEGROUND_AA) + else if (bgTypeID == BATTLEGROUND_AA) { HStartLoc[0] = 0; HStartLoc[1] = 0; @@ -1524,12 +1751,12 @@ void BattleGroundMgr::CreateInitialBattleGrounds() } else { - sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.",bgTypeID,start2); + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTypeID, start2); continue; } //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); - if(!CreateBattleGround(bgTypeID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) + if (!CreateBattleGround(bgTypeID, IsArena, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) continue; ++count; @@ -1543,19 +1770,19 @@ void BattleGroundMgr::CreateInitialBattleGrounds() void BattleGroundMgr::InitAutomaticArenaPointDistribution() { - if(m_AutoDistributePoints) + if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS)) { sLog.outDebug("Initializing Automatic Arena Point Distribution"); QueryResult * result = CharacterDatabase.Query("SELECT NextArenaPointDistributionTime FROM saved_variables"); - if(!result) + if (!result) { sLog.outDebug("Battleground: Next arena point distribution time not found in SavedVariables, reseting it now."); m_NextAutoDistributionTime = time(NULL) + BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY * sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS); - CharacterDatabase.PExecute("INSERT INTO saved_variables (NextArenaPointDistributionTime) VALUES ('"I64FMTD"')", m_NextAutoDistributionTime); + CharacterDatabase.PExecute("INSERT INTO saved_variables (NextArenaPointDistributionTime) VALUES ('"UI64FMTD"')", m_NextAutoDistributionTime); } else { - m_NextAutoDistributionTime = (*result)[0].GetUInt64(); + m_NextAutoDistributionTime = time_t((*result)[0].GetUInt64()); delete result; } sLog.outDebug("Automatic Arena Point Distribution initialized."); @@ -1565,9 +1792,9 @@ void BattleGroundMgr::InitAutomaticArenaPointDistribution() void BattleGroundMgr::DistributeArenaPoints() { // used to distribute arena points based on last week's stats - sWorld.SendGlobalText("Flushing Arena points based on team ratings, this may take a few minutes. Please stand by...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START); - sWorld.SendGlobalText("Distributing arena points to players...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START); //temporary structure for storing maximum points to add values for all players std::map<uint32, uint32> PlayerPoints; @@ -1575,7 +1802,7 @@ void BattleGroundMgr::DistributeArenaPoints() //at first update all points for all team members for(ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr) { - if(ArenaTeam * at = team_itr->second) + if (ArenaTeam * at = team_itr->second) { at->UpdateArenaPointsHelper(PlayerPoints); } @@ -1585,7 +1812,7 @@ void BattleGroundMgr::DistributeArenaPoints() for (std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr) { //update to database - CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE `guid` = '%u'", plr_itr->second, plr_itr->first); + CharacterDatabase.PExecute("UPDATE characters SET arena_pending_points = '%u' WHERE guid = '%u'", plr_itr->second, plr_itr->first); //add points if player is online Player* pl = objmgr.GetPlayer(plr_itr->first); if (pl) @@ -1594,12 +1821,12 @@ void BattleGroundMgr::DistributeArenaPoints() PlayerPoints.clear(); - sWorld.SendGlobalText("Finished setting arena points for online players.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END); - sWorld.SendGlobalText("Modifying played count, arena points etc. for loaded arena teams, sending updated stats to online players...", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START); for(ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr) { - if(ArenaTeam * at = titr->second) + if (ArenaTeam * at = titr->second) { at->FinishWeek(); // set played this week etc values to 0 in memory, too at->SaveToDB(); // save changes @@ -1607,55 +1834,55 @@ void BattleGroundMgr::DistributeArenaPoints() } } - sWorld.SendGlobalText("Modification done.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); - sWorld.SendGlobalText("Done flushing Arena points.", NULL); + sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END); } -void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId) +void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId, uint8 fromWhere) { - uint32 PlayerLevel = 10; + if (!plr) + return; - if(plr) - PlayerLevel = plr->getLevel(); + uint32 PlayerLevel = 10; + PlayerLevel = plr->getLevel(); data->Initialize(SMSG_BATTLEFIELD_LIST); *data << uint64(guid); // battlemaster guid + *data << uint8(fromWhere); // from where you joined *data << uint32(bgTypeId); // battleground id if(bgTypeId == BATTLEGROUND_AA) // arena { - *data << uint8(5); // unk - *data << uint32(0); // unk + *data << uint8(4); // unk + *data << uint32(0); // unk (count?) } else // battleground { - *data << uint8(0x00); // unk + *data << uint8(0x00); // unk, different for each bg type size_t count_pos = data->wpos(); uint32 count = 0; *data << uint32(0x00); // number of bg instances - for(std::map<uint32, BattleGround*>::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(bgTypeId); + for(std::set<uint32>::iterator itr = m_ClientBattleGroundIds[bgTypeId][queue_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][queue_id].end();++itr) { - if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel())) - { - *data << uint32(itr->second->GetInstanceID()); - ++count; - } + *data << uint32(*itr); + ++count; } data->put<uint32>( count_pos , count); } } -void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId) +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId) { - BattleGround *bg = GetBattleGround(instanceId); - if(bg) + BattleGround *bg = GetBattleGround(instanceId, bgTypeId); + if (bg) { uint32 mapid = bg->GetMapId(); float x, y, z, O; uint32 team = pl->GetBGTeam(); - if(team==0) + if (team==0) team = pl->GetTeam(); bg->GetTeamStartLoc(team, x, y, z, O); @@ -1668,24 +1895,17 @@ void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId) } } -void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid) +void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid) { WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds - if(time_ == uint32(-1)) + if (time_ == uint32(-1)) time_ = 0; data << guid << time_; pl->GetSession()->SendPacket(&data); } -void BattleGroundMgr::RemoveBattleGround(uint32 instanceID) -{ - BattleGroundSet::iterator itr = m_BattleGrounds.find(instanceID); - if(itr!=m_BattleGrounds.end()) - m_BattleGrounds.erase(itr); -} - -bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const +bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId) { return ( bgTypeId == BATTLEGROUND_AA || bgTypeId == BATTLEGROUND_BE || @@ -1693,96 +1913,169 @@ bool BattleGroundMgr::IsArenaType(uint32 bgTypeId) const bgTypeId == BATTLEGROUND_RL ); } -bool BattleGroundMgr::IsBattleGroundType(uint32 bgTypeId) const -{ - return !IsArenaType(bgTypeId); -} - -uint32 BattleGroundMgr::BGQueueTypeId(uint32 bgTypeId, uint8 arenaType) const +BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType) { switch(bgTypeId) { - case BATTLEGROUND_WS: - return BATTLEGROUND_QUEUE_WS; - case BATTLEGROUND_AB: - return BATTLEGROUND_QUEUE_AB; - case BATTLEGROUND_AV: - return BATTLEGROUND_QUEUE_AV; - case BATTLEGROUND_EY: - return BATTLEGROUND_QUEUE_EY; - case BATTLEGROUND_AA: - case BATTLEGROUND_NA: - case BATTLEGROUND_RL: - case BATTLEGROUND_BE: - switch(arenaType) - { - case ARENA_TYPE_2v2: - return BATTLEGROUND_QUEUE_2v2; - case ARENA_TYPE_3v3: - return BATTLEGROUND_QUEUE_3v3; - case ARENA_TYPE_5v5: - return BATTLEGROUND_QUEUE_5v5; + case BATTLEGROUND_WS: + return BATTLEGROUND_QUEUE_WS; + case BATTLEGROUND_AB: + return BATTLEGROUND_QUEUE_AB; + case BATTLEGROUND_AV: + return BATTLEGROUND_QUEUE_AV; + case BATTLEGROUND_EY: + return BATTLEGROUND_QUEUE_EY; + case BATTLEGROUND_SA: + return BATTLEGROUND_QUEUE_SA; + case BATTLEGROUND_AA: + case BATTLEGROUND_NA: + case BATTLEGROUND_RL: + case BATTLEGROUND_BE: + case BATTLEGROUND_DS: + case BATTLEGROUND_RV: + switch(arenaType) + { + case ARENA_TYPE_2v2: + return BATTLEGROUND_QUEUE_2v2; + case ARENA_TYPE_3v3: + return BATTLEGROUND_QUEUE_3v3; + case ARENA_TYPE_5v5: + return BATTLEGROUND_QUEUE_5v5; + default: + return BATTLEGROUND_QUEUE_NONE; + } default: - return 0; - } - default: - return 0; + return BATTLEGROUND_QUEUE_NONE; } } -uint32 BattleGroundMgr::BGTemplateId(uint32 bgQueueTypeId) const +BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { - case BATTLEGROUND_QUEUE_WS: - return BATTLEGROUND_WS; - case BATTLEGROUND_QUEUE_AB: - return BATTLEGROUND_AB; - case BATTLEGROUND_QUEUE_AV: - return BATTLEGROUND_AV; - case BATTLEGROUND_QUEUE_EY: - return BATTLEGROUND_EY; - case BATTLEGROUND_QUEUE_2v2: - case BATTLEGROUND_QUEUE_3v3: - case BATTLEGROUND_QUEUE_5v5: - return BATTLEGROUND_AA; - default: - return 0; + case BATTLEGROUND_QUEUE_WS: + return BATTLEGROUND_WS; + case BATTLEGROUND_QUEUE_AB: + return BATTLEGROUND_AB; + case BATTLEGROUND_QUEUE_AV: + return BATTLEGROUND_AV; + case BATTLEGROUND_QUEUE_EY: + return BATTLEGROUND_EY; + case BATTLEGROUND_QUEUE_SA: + return BATTLEGROUND_SA; + case BATTLEGROUND_QUEUE_2v2: + case BATTLEGROUND_QUEUE_3v3: + case BATTLEGROUND_QUEUE_5v5: + return BATTLEGROUND_AA; + default: + return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing) } } -uint8 BattleGroundMgr::BGArenaType(uint32 bgQueueTypeId) const +uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId) { switch(bgQueueTypeId) { - case BATTLEGROUND_QUEUE_2v2: - return ARENA_TYPE_2v2; - case BATTLEGROUND_QUEUE_3v3: - return ARENA_TYPE_3v3; - case BATTLEGROUND_QUEUE_5v5: - return ARENA_TYPE_5v5; - default: - return 0; + case BATTLEGROUND_QUEUE_2v2: + return ARENA_TYPE_2v2; + case BATTLEGROUND_QUEUE_3v3: + return ARENA_TYPE_3v3; + case BATTLEGROUND_QUEUE_5v5: + return ARENA_TYPE_5v5; + default: + return 0; } } +void BattleGroundMgr::ToggleTesting() +{ + m_Testing = !m_Testing; + if (m_Testing) + sWorld.SendWorldText(LANG_DEBUG_BG_ON); + else + sWorld.SendWorldText(LANG_DEBUG_BG_OFF); +} + void BattleGroundMgr::ToggleArenaTesting() { m_ArenaTesting = !m_ArenaTesting; - if(m_ArenaTesting) - sWorld.SendGlobalText("Arenas are set to 1v1 for debugging. So, don't join as group.", NULL); + if (m_ArenaTesting) + sWorld.SendWorldText(LANG_DEBUG_ARENA_ON); else - sWorld.SendGlobalText("Arenas are set to normal playercount.", NULL); + sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); } void BattleGroundMgr::SetHolidayWeekends(uint32 mask) { - for(uint32 bgtype = 1; bgtype <= 8; ++bgtype) + for(uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype) { - if(BattleGround * bg = GetBattleGroundTemplate(bgtype)) + if(BattleGround * bg = GetBattleGroundTemplate(BattleGroundTypeId(bgtype))) { bg->SetHoliday(mask & (1 << bgtype)); } } } +uint32 BattleGroundMgr::GetMaxRatingDifference() const +{ + // this is for stupid people who can't use brain and set max rating difference to 0 + uint32 diff = sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE); + if (diff == 0) + diff = 5000; + return diff; +} + +uint32 BattleGroundMgr::GetRatingDiscardTimer() const +{ + return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER); +} + +uint32 BattleGroundMgr::GetPrematureFinishTime() const +{ + return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER); +} + +void BattleGroundMgr::LoadBattleMastersEntry() +{ + mBattleMastersMap.clear(); // need for reload case + + QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" ); + + uint32 count = 0; + + if (!result) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + ++count; + bar.step(); + + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 bgTypeId = fields[1].GetUInt32(); + if (!sBattlemasterListStore.LookupEntry(bgTypeId)) + { + sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId); + continue; + } + + mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId); + + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u battlemaster entries", count ); +} |