diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 379a52087a..e5ad5a637f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1744,8 +1744,8 @@ class TC_GAME_API Player : public Unit, public GridObject void SendUpdateToOutOfRangeGroupMembers(); void SetInGuild(uint32 guildId); - void SetRank(uint8 rankId) { SetUInt32Value(PLAYER_GUILDRANK, rankId); } - uint8 GetRank() const { return uint8(GetUInt32Value(PLAYER_GUILDRANK)); } + void SetGuildRank(uint8 rankId) { SetUInt32Value(PLAYER_GUILDRANK, rankId); } + uint8 GetGuildRank() const { return uint8(GetUInt32Value(PLAYER_GUILDRANK)); } void SetGuildLevel(uint32 level) { SetUInt32Value(PLAYER_GUILDLEVEL, level); } uint32 GetGuildLevel() { return GetUInt32Value(PLAYER_GUILDLEVEL); } void SetGuildIdInvited(uint32 GuildId) { m_GuildIdInvited = GuildId; } diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 9b2bfe317c..2f7d72e780 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -736,8 +736,8 @@ void Guild::Member::ChangeRank(CharacterDatabaseTransaction& trans, uint8 newRan m_rankId = newRank; // Update rank information in player's field, if he is online. - if (Player* player = FindPlayer()) - player->SetRank(newRank); + if (Player* player = FindConnectedPlayer()) + player->SetGuildRank(newRank); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK); stmt->setUInt8 (0, newRank); @@ -1819,24 +1819,45 @@ void Guild::HandleSetEmblem(WorldSession* session, EmblemInfo const& emblemInfo) } } -void Guild::HandleSetNewGuildMaster(WorldSession* session, std::string const& name) +void Guild::HandleSetNewGuildMaster(WorldSession* session, std::string const& name, bool isSelfPromote) { Player* player = session->GetPlayer(); - // Only the guild master can throne a new guild master - if (!_IsLeader(player)) - SendCommandResult(session, GUILD_COMMAND_CHANGE_LEADER, ERR_GUILD_PERMISSIONS); - // Old GM must be a guild member - else if (Member* oldGuildMaster = GetMember(player->GetGUID())) + + Member* oldGuildMaster = GetMember(GetLeaderGUID()); + ASSERT(oldGuildMaster); + + Member* newGuildMaster; + + if (isSelfPromote) { - // Same for the new one - if (Member* newGuildMaster = GetMember(name)) + newGuildMaster = GetMember(player->GetGUID()); + if (!newGuildMaster) + return; + + if (!newGuildMaster->IsRankNotLower(GR_MEMBER) || uint32(oldGuildMaster->GetInactiveDays()) < GUILD_MASTER_DETHRONE_INACTIVE_DAYS) { - _SetLeaderGUID(newGuildMaster); - CharacterDatabaseTransaction trans(nullptr); - oldGuildMaster->ChangeRank(trans, GR_INITIATE); - _BroadcastEvent(GE_LEADER_CHANGED, ObjectGuid::Empty, player->GetName().c_str(), name.c_str()); + SendCommandResult(session, GUILD_COMMAND_CHANGE_LEADER, ERR_GUILD_PERMISSIONS); + return; } } + else + { + if (!_IsLeader(player)) + { + SendCommandResult(session, GUILD_COMMAND_CHANGE_LEADER, ERR_GUILD_PERMISSIONS); + return; + } + + newGuildMaster = GetMember(name); + if (!newGuildMaster) + return; + } + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + _SetLeader(trans, newGuildMaster); + oldGuildMaster->ChangeRank(trans, GR_INITIATE); + _BroadcastEvent(GE_LEADER_CHANGED, ObjectGuid::Empty, player->GetName().c_str(), newGuildMaster->GetName().c_str()); + CharacterDatabase.CommitTransaction(trans); } void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string const& name, std::string const& icon) @@ -2814,7 +2835,6 @@ bool Guild::Validate() Member* pLeader = GetMember(m_leaderGuid); if (!pLeader) { - CharacterDatabaseTransaction trans(nullptr); DeleteMember(trans, m_leaderGuid); // If no more members left, disband guild if (m_members.empty()) @@ -2824,7 +2844,7 @@ bool Guild::Validate() } } else if (!pLeader->IsRank(GR_GUILDMASTER)) - _SetLeaderGUID(pLeader); + _SetLeader(trans, pLeader); // Check config if multiple guildmasters are allowed if (!sConfigMgr->GetBoolDefault("Guild.AllowMultipleGuildMaster", 0)) @@ -2988,7 +3008,7 @@ bool Guild::AddMember(CharacterDatabaseTransaction& trans, ObjectGuid guid, uint m_members[lowguid] = member; player->SetInGuild(m_id); player->SetGuildIdInvited(0); - player->SetRank(rankId); + player->SetGuildRank(rankId); player->SetGuildLevel(GetLevel()); member->UpdateProfessionData(); SendLoginInfo(player->GetSession()); @@ -3088,11 +3108,7 @@ void Guild::DeleteMember(CharacterDatabaseTransaction& trans, ObjectGuid guid, b return; } - _SetLeaderGUID(newLeader); - - // If player not online data in data field will be loaded from guild tabs no need to update it !! - if (Player* newLeaderPlayer = newLeader->FindPlayer()) - newLeaderPlayer->SetRank(GR_GUILDMASTER); + _SetLeader(trans, newLeader); // If leader does not exist (at guild loading with deleted leader) do not send broadcasts if (oldLeader) @@ -3123,13 +3139,11 @@ void Guild::DeleteMember(CharacterDatabaseTransaction& trans, ObjectGuid guid, b if (player) { player->SetInGuild(0); - player->SetRank(0); + player->SetGuildRank(0); player->SetGuildLevel(0); - for (uint32 i = 0; i < sGuildPerkSpellsStore.GetNumRows(); ++i) - if (GuildPerkSpellsEntry const* entry = sGuildPerkSpellsStore.LookupEntry(i)) - if (entry->GuildLevel <= GetLevel()) - player->RemoveSpell(entry->SpellID, false, false); + for (GuildPerkSpellsEntry const* entry : sGuildPerkSpellsStore) + player->RemoveSpell(entry->SpellID, false, false); // Update GuildId in group if (Group* group = player->GetGroup()) @@ -3330,12 +3344,15 @@ bool Guild::_ModifyBankMoney(CharacterDatabaseTransaction& trans, uint64 amount, return true; } -void Guild::_SetLeaderGUID(Member* pLeader) +void Guild::_SetLeader(CharacterDatabaseTransaction& trans, Member* pLeader) { if (!pLeader) return; - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + bool isInTransaction = bool(trans); + if (!isInTransaction) + trans = CharacterDatabase.BeginTransaction(); + m_leaderGuid = pLeader->GetGUID(); pLeader->ChangeRank(trans, GR_GUILDMASTER); @@ -3344,7 +3361,8 @@ void Guild::_SetLeaderGUID(Member* pLeader) stmt->setUInt32(1, m_id); trans->Append(stmt); - CharacterDatabase.CommitTransaction(trans); + if (!isInTransaction) + CharacterDatabase.CommitTransaction(trans); } void Guild::_SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay) diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index 85406963d9..dd19772b32 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -44,6 +44,7 @@ enum GuildMisc GUILD_BANK_MAX_TABS = 8, // send by client for money log also GUILD_BANK_MAX_SLOTS = 98, GUILD_BANK_MONEY_LOGS_TAB = 100, // used for money log in DB + GUILD_MASTER_DETHRONE_INACTIVE_DAYS = 90, GUILD_RANKS_MIN_COUNT = 2, GUILD_RANKS_MAX_COUNT = 10, GUILD_RANK_NONE = 0xFF, @@ -794,7 +795,7 @@ public: void HandleSetMOTD(WorldSession* session, std::string const& motd); void HandleSetInfo(WorldSession* session, std::string const& info); void HandleSetEmblem(WorldSession* session, EmblemInfo const& emblemInfo); - void HandleSetNewGuildMaster(WorldSession* session, std::string const& name); + void HandleSetNewGuildMaster(WorldSession* session, std::string const& name, bool isSelfPromote); void HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string const& name, std::string const& icon); void HandleSetMemberNote(WorldSession* session, std::string const& note, ObjectGuid guid, bool isPublic); void HandleSetRankInfo(WorldSession* session, uint8 rankId, std::string const& name, uint32 rights, uint32 moneyPerDay, GuildBankRightsAndSlotsVec const& rightsAndSlots); @@ -988,7 +989,7 @@ private: bool _IsLeader(Player* player) const; void _DeleteBankItems(CharacterDatabaseTransaction& trans, bool removeItemsFromDB = false); bool _ModifyBankMoney(CharacterDatabaseTransaction& trans, uint64 amount, bool add); - void _SetLeaderGUID(Member* pLeader); + void _SetLeader(CharacterDatabaseTransaction& trans, Member* pLeader); void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay); void _SetRankBankTabRightsAndSlots(uint8 rankId, GuildBankRightsAndSlots rightsAndSlots, bool saveToDB = true); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index cda3a08e5b..079b9527fe 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -892,7 +892,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) { Field* fields = resultGuild->Fetch(); pCurrChar->SetInGuild(fields[0].GetUInt32()); - pCurrChar->SetRank(fields[1].GetUInt8()); + pCurrChar->SetGuildRank(fields[1].GetUInt8()); if (Guild* guild = sGuildMgr->GetGuildById(pCurrChar->GetGuildId())) { pCurrChar->SetGuildLevel(guild->GetLevel()); @@ -906,7 +906,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) else if (pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership { pCurrChar->SetInGuild(0); - pCurrChar->SetRank(0); + pCurrChar->SetGuildRank(0); pCurrChar->SetGuildLevel(0); } diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index 965983002e..ee798867af 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -701,14 +701,16 @@ void WorldSession::HandleGuildNewsUpdateStickyOpcode(WorldPacket& recvPacket) guild->HandleNewsSetSticky(this, newsId, sticky); } -void WorldSession::HandleGuildSetGuildMaster(WorldPacket& recvPacket) +void WorldSession::HandleGuildReplaceGuildMaster(WorldPackets::Guild::ReplaceGuildMaster& /*packet */) { - uint8 nameLength = recvPacket.ReadBits(7); - // This is related to guild master inactivity. - /*bool isDethrone = */recvPacket.ReadBit(); - std::string playerName = recvPacket.ReadString(nameLength); if (Guild* guild = GetPlayer()->GetGuild()) - guild->HandleSetNewGuildMaster(this, playerName); + guild->HandleSetNewGuildMaster(this, "", true); +} + +void WorldSession::HandleGuildSetGuildMaster(WorldPackets::Guild::SetGuildMaster& packet) +{ + if (Guild* guild = GetPlayer()->GetGuild()) + guild->HandleSetNewGuildMaster(this, packet.NewMasterName, packet.IsDethrone); } void WorldSession::HandleGuildSetAchievementTracking(WorldPacket& recvPacket) diff --git a/src/server/game/Server/Packets/GuildPackets.cpp b/src/server/game/Server/Packets/GuildPackets.cpp index a02149d42e..906d745653 100644 --- a/src/server/game/Server/Packets/GuildPackets.cpp +++ b/src/server/game/Server/Packets/GuildPackets.cpp @@ -690,3 +690,11 @@ WorldPacket const* WorldPackets::Guild::GuildRanks::Write() return &_worldPacket; } + +void WorldPackets::Guild::SetGuildMaster::Read() +{ + uint8 nameLen = _worldPacket.ReadBits(7); + + IsDethrone = _worldPacket.ReadBit(); + NewMasterName = _worldPacket.ReadString(nameLen); +} diff --git a/src/server/game/Server/Packets/GuildPackets.h b/src/server/game/Server/Packets/GuildPackets.h index d9b2999a33..4fa5814668 100644 --- a/src/server/game/Server/Packets/GuildPackets.h +++ b/src/server/game/Server/Packets/GuildPackets.h @@ -552,6 +552,25 @@ namespace WorldPackets std::vector Ranks; }; + + class ReplaceGuildMaster final : public ClientPacket + { + public: + ReplaceGuildMaster(WorldPacket&& packet) : ClientPacket(CMSG_GUILD_REPLACE_GUILD_MASTER, std::move(packet)) { } + + void Read() override { } + }; + + class SetGuildMaster final : public ClientPacket + { + public: + SetGuildMaster(WorldPacket&& packet) : ClientPacket(CMSG_GUILD_SET_GUILD_MASTER, std::move(packet)) { } + + void Read() override; + + bool IsDethrone = false; + std::string NewMasterName; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 56f92ab992..235fdb8afa 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -367,7 +367,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_GUILD_QUERY_MEMBER_RECIPES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildQueryMemberRecipe ); DEFINE_HANDLER(CMSG_GUILD_QUERY_MEMBERS_FOR_RECIPE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildQueryMembersForRecipe); DEFINE_HANDLER(CMSG_GUILD_REMOVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRemoveOpcode ); - DEFINE_HANDLER(CMSG_GUILD_REPLACE_GUILD_MASTER, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + DEFINE_HANDLER(CMSG_GUILD_REPLACE_GUILD_MASTER, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGuildReplaceGuildMaster ); DEFINE_HANDLER(CMSG_GUILD_REQUEST_CHALLENGE_UPDATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRequestChallengeUpdate); DEFINE_HANDLER(CMSG_GUILD_REQUEST_MAX_DAILY_XP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRequestMaxDailyXP ); DEFINE_HANDLER(CMSG_GUILD_REQUEST_PARTY_STATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGuildRequestPartyState ); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 70cb615f49..18fc2db071 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -131,6 +131,8 @@ namespace WorldPackets class GuildQueryMemberRecipes; class GuildChallengeUpdateRequest; class RequestGuildRewardsList; + class ReplaceGuildMaster; + class SetGuildMaster; } namespace Misc @@ -785,7 +787,8 @@ class TC_GAME_API WorldSession void HandleGuildLeaveOpcode(WorldPacket& recvPacket); void HandleGuildDisbandOpcode(WorldPacket& recvPacket); void HandleGuildSetAchievementTracking(WorldPacket& recvPacket); - void HandleGuildSetGuildMaster(WorldPacket& recvPacket); + void HandleGuildReplaceGuildMaster(WorldPackets::Guild::ReplaceGuildMaster& packet); + void HandleGuildSetGuildMaster(WorldPackets::Guild::SetGuildMaster& packet); void HandleGuildMOTDOpcode(WorldPacket& recvPacket); void HandleGuildNewsUpdateStickyOpcode(WorldPacket& recvPacket); void HandleGuildSetNoteOpcode(WorldPacket& recvPacket);