aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp149
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h26
-rw-r--r--src/server/game/Entities/Player/Player.cpp34
-rw-r--r--src/server/game/Groups/Group.cpp2
-rw-r--r--src/server/game/Groups/Group.h22
-rw-r--r--src/server/game/Server/Protocol/Handlers/GroupHandler.cpp11
-rw-r--r--src/server/game/Server/Protocol/Handlers/LFGHandler.cpp2
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)