diff options
Diffstat (limited to 'src/game/BattleGroundMgr.cpp')
| -rw-r--r-- | src/game/BattleGroundMgr.cpp | 1995 | 
1 files changed, 1135 insertions, 860 deletions
diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 54d2538068d..f0f801c3060 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,6 +19,7 @@   */  #include "Common.h" +#include "SharedDefines.h"  #include "Player.h"  #include "BattleGroundMgr.h"  #include "BattleGroundAV.h" @@ -29,16 +30,21 @@  #include "BattleGroundBE.h"  #include "BattleGroundAA.h"  #include "BattleGroundRL.h" -#include "SharedDefines.h" -#include "Policies/SingletonImp.h" +#include "BattleGroundSA.h" +#include "BattleGroundDS.h" +#include "BattleGroundRV.h"  #include "MapManager.h"  #include "Map.h"  #include "MapInstanced.h"  #include "ObjectMgr.h"  #include "ProgressBar.h" -#include "World.h"  #include "Chat.h"  #include "ArenaTeam.h" +#include "World.h" +#include "WorldPacket.h" +#include "ProgressBar.h" + +#include "Policies/SingletonImp.h"  INSTANTIATE_SINGLETON_1( BattleGroundMgr ); @@ -48,96 +54,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++) +    for(uint32 i = 0; i < BG_TEAMS_COUNT; 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++) -    { -        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 +160,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 +195,228 @@ 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 -    for(group_itr=m_QueuedGroups[queue_id].begin(); group_itr != m_QueuedGroups[queue_id].end(); ++group_itr) +    // 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); + +    // 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(); +        } +    } -    // check for iterator correctness -    if (group_itr != m_QueuedGroups[queue_id].end() && itr != m_QueuedPlayers[queue_id].end()) +    // remove group queue info if needed +    if (group->Players.empty())      { -        // used when player left the queue, NOT used when porting to bg -        if (decreaseInvitedCount) +        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))          { -            // if invited to bg, and should decrease invited count, then do it -            if(group->IsInvitedToBGInstanceGUID) -            { -                BattleGround* bg = sBattleGroundMgr.GetBattleGround(group->IsInvitedToBGInstanceGUID); -                if (bg) -                    bg->DecreaseInvitedCount(group->Team); -                if (bg && !bg->GetPlayersSize() && !bg->GetInvitedCount(ALLIANCE) && !bg->GetInvitedCount(HORDE)) -                { -                    // no more players on battleground, set delete it -                    bg->SetDeleteThis(); -                } -            } -            // update the join queue, maybe now the player's group fits in a queue! -            // not yet implemented (should store bgTypeId in group queue info?) +            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);          } -        // remove player queue info -        m_QueuedPlayers[queue_id].erase(itr); -        // remove group queue info if needed -        if(group->Players.empty()) +        // then actually delete, this may delete the group as well! +        RemovePlayer(group->Players.begin()->first, decreaseInvitedCount); +    } +} + +//Announce world message +void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue) +{ +    if(ginfo->ArenaType) //if Arena +    { +        if (sWorld.getConfig(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE) && ginfo->IsRated)          { -            m_QueuedGroups[queue_id].erase(group_itr); -            delete group; +            BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId); +            if (!bg) +                return; + +            char const* bgName = bg->GetName(); +            if (isAddedToQueue) +                sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating); +            else +                sWorld.SendWorldText(LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT, bgName, ginfo->ArenaType, ginfo->ArenaType, ginfo->ArenaTeamRating);          } -        // 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; +            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)) +            { +                ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, +                    bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers); +            } +            // System message +            else              { -                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); +                sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, +                    bgName, q_min_level, q_min_level + 10, qAlliance, MinPlayers, qHorde, MinPlayers);              } -            // then actually delete, this may delete the group as well! -            RemovePlayer(group->Players.begin()->first,decreaseInvitedCount);          }      }  } @@ -290,30 +424,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 +474,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 +483,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))              { -                // enough players are selected -                return true; +                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);              } -            // 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));          } +        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()) +            { +                if (hordeFree <= diffAli + 1) +                    break; +                m_SelectionPools[BG_TEAM_ALLIANCE].KickGroup(diffAli - diffHorde); +            } +        } +        //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::max(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; +        } +    } +    // 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); +            }          } -        return true;      } -    // failed to build a selection pool matching the given values +    //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 +777,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 +813,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) -        { -            // 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) +        // 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)) )          { -            // 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 +        } + +        //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]; -            sLog.outDebug("Battleground: One-faction arena created."); -            // init stats -            if(sBattleGroundMgr.isArenaTesting()) +        //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); -                    break; -                case ARENA_TYPE_5v5: -                    bg2->SetMaxPlayersPerTeam(5); -                    bg2->SetMaxPlayers(10); +                    m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*itr_team[BG_TEAM_ALLIANCE]), MaxPlayersPerTeam);                      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 +1010,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 +1040,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 +1059,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,76 +1093,98 @@ 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)              { @@ -1026,10 +1199,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 +1214,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 +1246,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 +1268,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 +1283,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,16 +1296,16 @@ 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(bg->m_ArenaTeamRatingChanges[i]);              *data << uint32(3999);  // huge thanks for TOM_RUS for this!              sLog.outDebug("rating change: %d", bg->m_ArenaTeamRatingChanges[i]);          } @@ -1132,16 +1313,16 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)          {              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 +1336,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 +1344,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 +1376,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 +1396,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 +1424,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 +1436,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) +    { +        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)      { -        switch(bg->GetArenaType()) +        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 +1554,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 +1605,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 +1638,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 +1663,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; - -        if(fields[1].GetUInt32()) -            MinPlayersPerTeam = fields[1].GetUInt32(); - -        if(fields[2].GetUInt32()) -            MaxPlayersPerTeam = fields[2].GetUInt32(); - -        if(fields[3].GetUInt32()) -            MinLvl = fields[3].GetUInt32(); +        BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); -        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 +1711,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 +1734,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,11 +1753,11 @@ 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); @@ -1555,7 +1765,7 @@ void BattleGroundMgr::InitAutomaticArenaPointDistribution()          }          else          { -            m_NextAutoDistributionTime = (*result)[0].GetUInt64(); +            m_NextAutoDistributionTime = time_t((*result)[0].GetUInt64());              delete result;          }          sLog.outDebug("Automatic Arena Point Distribution initialized."); @@ -1565,9 +1775,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 +1785,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 +1795,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 +1804,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,17 +1817,18 @@ 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)  { -    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 @@ -1635,27 +1846,25 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid          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 +1877,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 +1895,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 ); +}  | 
