diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/DungeonFinding/LFGMgr.cpp | 149 | ||||
-rw-r--r-- | src/server/game/DungeonFinding/LFGMgr.h | 26 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 34 | ||||
-rw-r--r-- | src/server/game/Groups/Group.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Groups/Group.h | 22 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/GroupHandler.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/LFGHandler.cpp | 2 |
7 files changed, 230 insertions, 16 deletions
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index e57d15c093a..dd14af14b08 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -57,6 +57,10 @@ LFGMgr::~LFGMgr() delete it->second; m_Proposals.clear(); + for (LfgPlayerBootMap::iterator it = m_Boots.begin(); it != m_Boots.end(); ++it) + delete it->second; + m_Boots.clear(); + for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end(); ++it) delete it->second; m_RoleChecks.clear(); @@ -1093,6 +1097,115 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t } /// <summary> +/// Initialize a boot kick vote +/// </summary> +/// <param name="Group *">Group</param> +/// <param name="uint32">Player low guid who inits the vote kick</param> +/// <param name="uint32">Player low guid to be kicked </param> +/// <param name="std::string">kick reason</param> +void LFGMgr::InitBoot(Group *grp, uint32 iLowGuid, uint32 vLowguid, std::string reason) +{ + if (!grp) + return; + + LfgPlayerBoot *pBoot = new LfgPlayerBoot(); + pBoot->inProgress = true; + pBoot->cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; + pBoot->reason = reason; + pBoot->victimLowGuid = vLowguid; + pBoot->votedNeeded = GROUP_LFG_KICK_VOTES_NEEDED; + PlayerSet players; + + uint32 pLowGuid = 0; + for (GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + if (Player *plrg = itr->getSource()) + { + pLowGuid = plrg->GetGUIDLow(); + if (pLowGuid == vLowguid) + pBoot->votes[pLowGuid] = 0; // Victim auto vote NO + else if (pLowGuid == iLowGuid) + pBoot->votes[pLowGuid] = 1; // Kicker auto vote YES + else + { + pBoot->votes[pLowGuid] = -1; // Other members need to vote + players.insert(plrg); + } + } + } + + for (PlayerSet::const_iterator it = players.begin(); it != players.end(); ++it) + SendLfgBootPlayer(*it, pBoot); + + grp->SetLfgKickActive(true); + m_Boots[grp->GetLowGUID()] = pBoot; +} + +/// <summary> +/// Update Boot info with player answer +/// </summary> +/// <param name="Player *">Player guid</param> +/// <param name="uint8">Player answer</param> +void LFGMgr::UpdateBoot(Player *plr, uint8 accept) +{ + Group *grp = plr ? plr->GetGroup() : NULL; + if (!grp) + return; + + uint32 bootId = grp->GetLowGUID(); + uint32 lowGuid = plr->GetGUIDLow(); + + LfgPlayerBootMap::iterator itBoot = m_Boots.find(bootId); + if (itBoot == m_Boots.end()) + return; + + LfgPlayerBoot *pBoot = itBoot->second; + if (!pBoot) + return; + + if (pBoot->votes[lowGuid] != -1) // Cheat check: Player can't vote twice + return; + + pBoot->votes[lowGuid] = accept; + + uint8 votesNum = 0; + uint8 agreeNum = 0; + for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) + { + if (itVotes->second != -1) + { + ++votesNum; + if (itVotes->second == 1) + ++agreeNum; + } + } + + if (agreeNum == pBoot->votedNeeded || // Vote passed + votesNum == pBoot->votes.size() || // All voted but not passed + (pBoot->votes.size() - votesNum + agreeNum) < pBoot->votedNeeded) // Vote didnt passed + { + // Send update info to all players + pBoot->inProgress = false; + for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) + if (Player *plrg = sObjectMgr.GetPlayer(itVotes->first)) + if (plrg->GetGUIDLow() != pBoot->victimLowGuid) + SendLfgBootPlayer(plrg, pBoot); + + if (agreeNum == pBoot->votedNeeded) // Vote passed - Kick player + { + Player::RemoveFromGroup(grp, MAKE_NEW_GUID(pBoot->victimLowGuid, 0, HIGHGUID_PLAYER)); + if (Player *victim = sObjectMgr.GetPlayer(pBoot->victimLowGuid)) + victim->TeleportToBGEntryPoint(); + OfferContinue(grp); + grp->SetLfgKicks(grp->GetLfgKicks() + 1); + } + grp->SetLfgKickActive(false); + delete pBoot; + m_Boots.erase(itBoot); + } +} + +/// <summary> /// Teleports the player in or out the dungeon /// </summary> /// <param name="Player *">Player</param> @@ -1306,6 +1419,42 @@ void LFGMgr::SendLfgPartyInfo(Player *plr) } /// <summary> +/// Build and Send LFG boot player info +/// </summary> +/// <param name="Player *">Player</param> +/// <param name="LfgPlayerBoot *">Boot info</param> +void LFGMgr::SendLfgBootPlayer(Player *plr, LfgPlayerBoot *pBoot) +{ + sLog.outDebug("SMSG_LFG_BOOT_PLAYER"); + + int8 playerVote = pBoot->votes[plr->GetGUIDLow()]; + uint8 votesNum = 0; + uint8 agreeNum = 0; + uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000); + for (LfgAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it) + { + if (it->second != -1) + { + ++votesNum; + if (it->second == 1) + ++agreeNum; + } + } + + WorldPacket data(SMSG_LFG_BOOT_PLAYER, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length()); + data << uint8(pBoot->inProgress); // Vote in progress + data << uint8(playerVote != -1); // Did Vote + data << uint8(playerVote == 1); // Agree + data << uint64(MAKE_NEW_GUID(pBoot->victimLowGuid, 0, HIGHGUID_PLAYER)); // Victim GUID + data << uint32(votesNum); // Total Votes + data << uint32(agreeNum); // Agree Count + data << uint32(secsleft); // Time Left + data << uint32(pBoot->votedNeeded); // Needed Votes + data << pBoot->reason.c_str(); // Kick reason + plr->GetSession()->SendPacket(&data); +} + +/// <summary> /// Build and Send LFG player reward /// </summary> /// <param name="Player *">Player</param> diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 683fc83414c..cf5490a900f 100644 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -27,8 +27,8 @@ enum LFGenum { LFG_TIME_ROLECHECK = 2*MINUTE, + LFG_TIME_BOOT = 2*MINUTE, LFG_TIME_PROPOSAL = 2*MINUTE, - LFG_VOTES_NEEDED = 3, LFG_TANKS_NEEDED = 1, LFG_HEALERS_NEEDED = 1, LFG_DPS_NEEDED = 3, @@ -158,13 +158,6 @@ enum LfgRewardEnums LFG_REWARD_DATA_SIZE = 10, }; -enum LfgDungeonStatus -{ - LFG_STATUS_SAVED = 0, - LFG_STATUS_NOT_SAVED = 1, - LFG_STATUS_COMPLETE = 2, -}; - const uint32 RewardDungeonData[LFG_REWARD_DATA_SIZE+1][5] = { // XP, money, item, item display, count {310, 3500, 51999, 56915, 1}, // Classic 15-23 @@ -273,6 +266,17 @@ struct LfgRoleCheck uint32 leader; }; +// Stores information of a current vote to kick someone from a group +struct LfgPlayerBoot +{ + time_t cancelTime; // Time left to vote + bool inProgress; // Vote in progress + LfgAnswerMap votes; // Player votes (-1 not answer | 0 Not agree | 1 agree) + uint32 victimLowGuid; // Player guid to be kicked (can't vote) + uint8 votedNeeded; // Votes needed to kick the player + std::string reason; // kick reason +}; + typedef std::set<Player*> PlayerSet; typedef std::set<LfgLockStatus*> LfgLockStatusSet; typedef std::vector<LfgReward*> LfgRewardList; @@ -282,6 +286,7 @@ typedef std::map<uint32, LfgLockStatusSet*> LfgLockStatusMap; typedef std::map<uint64, LfgQueueInfo*> LfgQueueInfoMap; typedef std::map<uint32, LfgRoleCheck*> LfgRoleCheckMap; typedef std::map<uint32, LfgProposal*> LfgProposalMap; +typedef std::map<uint32, LfgPlayerBoot*> LfgPlayerBootMap; typedef std::list<Player *> LfgPlayerList; class LFGMgr @@ -297,14 +302,17 @@ class LFGMgr void OfferContinue(Group *grp); void TeleportPlayer(Player *plr, bool out); void UpdateProposal(uint32 proposalId, uint32 lowGuid, uint8 accept); + void UpdateBoot(Player *plr, uint8 accept); void UpdateRoleCheck(Group *grp, Player *plr = NULL); void Update(uint32 diff); void SendLfgPlayerInfo(Player *plr); void SendLfgPartyInfo(Player *plr); bool isRandomDungeon(uint32 dungeonId); + void InitBoot(Group *grp, uint32 plowGuid, uint32 vlowGuid, std::string reason); private: + void SendLfgBootPlayer(Player *plr, LfgPlayerBoot *pBoot); void SendUpdateProposal(Player *plr, uint32 proposalId, LfgProposal *pProp); void SendLfgPlayerReward(Player *plr); @@ -312,6 +320,7 @@ class LFGMgr void BuildAvailableRandomDungeonList(WorldPacket &data, Player *plr); void BuildPlayerLockDungeonBlock(WorldPacket &data, LfgLockStatusSet *lockSet); void BuildPartyLockDungeonBlock(WorldPacket &data, LfgLockStatusMap *lockMap); + void BuildBootPlayerBlock(WorldPacket &data, LfgPlayerBoot *pBoot, uint32 lowGuid); void AddToQueue(uint64 guid, LfgRolesMap *roles, LfgDungeonSet *dungeons); bool RemoveFromQueue(uint64 guid); @@ -335,6 +344,7 @@ class LFGMgr LfgGuidList m_currentQueue; // Ordered list. Used to find groups LfgGuidList m_newToQueue; // New groups to add to queue; LfgProposalMap m_Proposals; // Current Proposals + LfgPlayerBootMap m_Boots; // Current player kicks LfgRoleCheckMap m_RoleChecks; // Current Role checks uint32 m_QueueTimer; // used to check interval of update uint32 m_lfgProposalId; // used as internal counter for proposals diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9ba259f8c88..5d88d93800e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -22029,11 +22029,37 @@ PartyResult Player::CanUninviteFromGroup() const if (!grp) return ERR_NOT_IN_GROUP; - if (!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID())) - return ERR_NOT_LEADER; + if (grp->isLFGGroup()) + { + if (grp->GetLfgKicks() == GROUP_MAX_LFG_KICKS) + return ERR_PARTY_LFG_BOOT_LIMIT; - if (InBattleground()) - return ERR_INVITE_RESTRICTED; + if (grp->isLfgKickActive()) + return ERR_PARTY_LFG_BOOT_IN_PROGRESS; + + if (grp->GetMembersCount() <= GROUP_LFG_KICK_VOTES_NEEDED) + return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS; + + if (grp->isLfgDungeonComplete()) + return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE; + + if (grp->isRollLootActive()) + return ERR_PARTY_LFG_BOOT_LOOT_ROLLS; + + /* Missing support for these types + return ERR_PARTY_LFG_BOOT_IN_COMBAT; // also have a cooldown (some secs after combat finish + return ERR_PARTY_LFG_BOOT_COOLDOWN_S; + return ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S; + */ + } + else + { + if (!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID())) + return ERR_NOT_LEADER; + + if (InBattleground()) + return ERR_INVITE_RESTRICTED; + } return ERR_PARTY_RESULT_OK; } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 74a26f6a7b8..7c35e5fc5ed 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -51,6 +51,8 @@ Group::Group() m_LfgQueued = false; m_LfgStatus = LFG_STATUS_NOT_SAVED; m_LfgDungeonEntry = 0; + m_Lfgkicks = 0; + m_LfgkicksActive = false; for (uint8 i = 0; i < TARGETICONCOUNT; ++i) m_targetIcons[i] = 0; diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 78e766edc92..f29e5d5c234 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -35,6 +35,8 @@ #define MAXRAIDSIZE 40 #define MAX_RAID_SUBGROUPS MAXRAIDSIZE/MAXGROUPSIZE #define TARGETICONCOUNT 8 +#define GROUP_MAX_LFG_KICKS 3 +#define GROUP_LFG_KICK_VOTES_NEEDED 3 enum RollVote { @@ -46,6 +48,13 @@ enum RollVote NOT_VALID = 5 }; +enum LfgDungeonStatus +{ + LFG_STATUS_SAVED = 0, + LFG_STATUS_NOT_SAVED = 1, + LFG_STATUS_COMPLETE = 2, +}; + enum GroupMemberOnlineStatus { MEMBER_STATUS_OFFLINE = 0x0000, @@ -192,10 +201,12 @@ class Group void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; } void Disband(bool hideDestroy=false); + // Dungeon Finder void SetLfgQueued(bool queued) { m_LfgQueued = queued; } bool isLfgQueued() { return m_LfgQueued; } void SetLfgStatus(uint8 status) { m_LfgStatus = status; } uint8 GetLfgStatus() { return m_LfgStatus; } + bool isLfgDungeonComplete() const { return m_LfgStatus == LFG_STATUS_COMPLETE; } void SetLfgDungeonEntry(uint32 dungeonEntry) { m_LfgDungeonEntry = dungeonEntry; } uint32 GetLfgDungeonEntry(bool id = true) { @@ -204,8 +215,11 @@ class Group else return m_LfgDungeonEntry; } - - void SetLfgRoles(uint64 guid, const uint8 roles) + bool isLfgKickActive() const { return m_LfgkicksActive; } + void SetLfgKickActive(bool active) { m_LfgkicksActive = active; } + uint8 GetLfgKicks() const { return m_Lfgkicks; } + void SetLfgKicks(uint8 kicks) { m_Lfgkicks = kicks; } + void SetLfgRoles(uint64 guid, const uint8 roles) { member_witerator slot = _getMemberWSlot(guid); if (slot == m_memberSlots.end()) @@ -214,6 +228,7 @@ class Group slot->roles = roles; SendUpdate(); } + // properties accessories bool IsFull() const { return (m_groupType == GROUPTYPE_NORMAL) ? (m_memberSlots.size() >= MAXGROUPSIZE) : (m_memberSlots.size() >= MAXRAIDSIZE); } bool isLFGGroup() const { return m_groupType & GROUPTYPE_LFG; } @@ -350,6 +365,7 @@ class Group /*** LOOT SYSTEM ***/ /*********************************************************/ + bool isRollLootActive() const { return !RollId.empty(); } void SendLootStartRoll(uint32 CountDown, uint32 mapid, const Roll &r); void SendLootRoll(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); void SendLootRollWon(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); @@ -488,5 +504,7 @@ class Group bool m_LfgQueued; uint8 m_LfgStatus; uint32 m_LfgDungeonEntry; + uint8 m_Lfgkicks; + bool m_LfgkicksActive; }; #endif diff --git a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp index 7b8fae749e1..838ebb40f4f 100644 --- a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp @@ -32,6 +32,7 @@ #include "Util.h" #include "SpellAuras.h" #include "Vehicle.h" +#include "LFGMgr.h" class Aura; @@ -289,7 +290,10 @@ void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data) if (grp->IsMember(guid)) { - Player::RemoveFromGroup(grp,guid); + if (grp->isLFGGroup()) + sLFGMgr.InitBoot(grp, GUID_LOPART(GetPlayer()->GetGUID()), GUID_LOPART(guid), reason); + else + Player::RemoveFromGroup(grp,guid); return; } @@ -331,7 +335,10 @@ void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data) if (uint64 guid = grp->GetMemberGUID(membername)) { - Player::RemoveFromGroup(grp,guid); + if (grp->isLFGGroup()) + sLFGMgr.InitBoot(grp, GUID_LOPART(GetPlayer()->GetGUID()), GUID_LOPART(guid), ""); + else + Player::RemoveFromGroup(grp,guid); return; } diff --git a/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp b/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp index b2a91b010fc..85a280152b5 100644 --- a/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/LFGHandler.cpp @@ -150,6 +150,8 @@ void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket &recv_data) uint8 agree; // Agree to kick player recv_data >> agree; + if (agree < 2) + sLFGMgr.UpdateBoot(GetPlayer(), agree); } void WorldSession::HandleLfgTeleportOpcode(WorldPacket &recv_data) |