/* EQEmu: EQEmulator Copyright (C) 2001-2026 EQEmu Development Team 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 the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "guild_mgr.h" #include "common/emu_versions.h" #include "common/repositories/guild_bank_repository.h" #include "common/repositories/guild_ranks_repository.h" #include "common/servertalk.h" #include "common/strings.h" #include "zone/client.h" #include "zone/string_ids.h" #include "zone/worldserver.h" #include "zone/zonedb.h" ZoneGuildManager guild_mgr; GuildBankManager *GuildBanks; extern WorldServer worldserver; extern volatile bool is_zone_loaded; extern EntityList entity_list; void ZoneGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { LogGuilds("Sending guild refresh for [{}] to world, changes: name=[{}], motd=[{}], rank=d, relation=[{}]", guild_id, name, motd, rank, relation); auto pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; s->guild_id = guild_id; s->name_change = name; s->motd_change = motd; s->rank_change = rank; s->relation_change = relation; worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) { if(guild_id == 0) { LogGuilds("Guild lookup for char [{}] when sending char refresh", charid); CharGuildInfo gci; if(!GetCharInfo(charid, gci)) { guild_id = GUILD_NONE; } else { guild_id = gci.guild_id; } } LogGuilds("Sending char refresh for [{}] from guild [{}] to world", charid, guild_id); auto pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; s->guild_id = guild_id; s->old_guild_id = old_guild_id; s->char_id = charid; worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::SendRankUpdate(uint32 CharID) { CharGuildInfo gci; if(!GetCharInfo(CharID, gci)) { return; } auto pack = new ServerPacket(ServerOP_GuildRankUpdate, sizeof(ServerGuildRankUpdate_Struct)); auto sgrus = (ServerGuildRankUpdate_Struct *) pack->pBuffer; sgrus->guild_id = gci.guild_id; sgrus->rank = gci.rank; sgrus->banker = gci.banker + (gci.alt * 2); sgrus->no_update = true; strn0cpy(sgrus->member_name, gci.char_name.c_str(), sizeof(sgrus->member_name)); worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::SendGuildDelete(uint32 guild_id) { LogGuilds("Sending guild delete for guild [{}] to world", guild_id); auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct)); auto s = (ServerGuildID_Struct *) pack->pBuffer; s->guild_id = guild_id; worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::ListGuilds(Client *c, uint32 guild_id) const { if (m_guilds.size()) { const auto& g = m_guilds.find(guild_id); if (g == m_guilds.end()) { c->Message( Chat::White, fmt::format( "Guild ID {} does not exist or is invalid.", guild_id ).c_str() ); return; } const auto leader_name = database.GetCharNameByID(g->second->leader); c->Message( Chat::White, fmt::format( "Guild {} | {}Name: {}", g->first, ( !leader_name.empty() ? fmt::format( "Leader: {} ({}) ", leader_name, g->second->leader ) : "" ), g->second->name ).c_str() ); } else { c->Message(Chat::White, "There are no Guilds to list."); } } void ZoneGuildManager::ListGuilds(Client *c, std::string search_criteria) const { if (m_guilds.size()) { if (search_criteria.empty()) { c->Message( Chat::White, fmt::format( "Listing {} Guild{}.", m_guilds.size(), m_guilds.size() != 1 ? "s" : "" ).c_str() ); } auto found_count = 0; for (const auto& guild : m_guilds) { if ( !search_criteria.empty() && !Strings::Contains( Strings::ToLower(guild.second->name), Strings::ToLower(search_criteria) ) ) { continue; } const auto leader_name = database.GetCharNameByID(guild.second->leader); c->Message( Chat::White, fmt::format( "Guild {} | {}Name: {}", guild.first, ( !leader_name.empty() ? fmt::format( "Leader: {} ({}) ", leader_name, guild.second->leader ) : "" ), guild.second->name ).c_str() ); found_count++; } c->Message( Chat::White, fmt::format( "Found {} Guild{}{}.", found_count, found_count != 1 ? "s" : "", ( !search_criteria.empty() ? fmt::format( " matching '{}'", search_criteria ) : "" ) ).c_str() ); } else { c->Message(Chat::White, "There are no Guilds to list."); } } void ZoneGuildManager::DescribeGuild(Client* c, uint32 guild_id) const { std::map::const_iterator res; res = m_guilds.find(guild_id); if (res == m_guilds.end()) { c->Message( Chat::White, fmt::format( "Guild ID {} could not be found.", guild_id ).c_str() ); return; } const GuildInfo *info = res->second; auto membership = GuildMembersRepository::GetGuildMembershipStats(*m_db, guild_id); auto leader_name = database.GetCharNameByID(info->leader); std::string popup_text = ""; popup_text += fmt::format( "", info->name, guild_id ); popup_text += fmt::format( "", leader_name, info->leader ); popup_text += ""; popup_text += ""; popup_text += fmt::format("", info->rank_names[1].c_str(), membership.leaders); popup_text += fmt::format("", info->rank_names[2].c_str(), membership.senior_officers); popup_text += fmt::format("", info->rank_names[3].c_str(), membership.officers); popup_text += fmt::format("", info->rank_names[4].c_str(), membership.senior_members); popup_text += fmt::format("", info->rank_names[5].c_str(), membership.members); popup_text += fmt::format("", info->rank_names[6].c_str(), membership.junior_members); popup_text += fmt::format("", info->rank_names[7].c_str(), membership.initates); popup_text += fmt::format("", info->rank_names[8].c_str(), membership.recruits); popup_text += ""; popup_text += fmt::format("", membership.tribute_enabled); popup_text += fmt::format("", info->tribute.favor); popup_text += "
Name{}Guild ID{}
Leader{}Character ID{}
.
RanksQuantity
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
.
Tribute On{}
Total Tribute{}
"; auto text = new char[4096]; strn0cpy(text, popup_text.c_str(), 4096); c->SendPopupToClient( "Guild Information", text ); safe_delete(text); } //in theory, we could get a pile of unused entries in this array, but only if //we had a malicious client sending controlled packets, plus its like 10 bytes per entry. void ZoneGuildManager::RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank) { m_inviteQueue[char_id] = std::pair(guild_id, rank); } bool ZoneGuildManager::VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank) { std::map >::iterator res; res = m_inviteQueue.find(char_id); if(res == m_inviteQueue.end()) return(false); //no entry... bool valid = false; if(res->second.first == guild_id && res->second.second == rank) { valid = true; } m_inviteQueue.erase(res); return(valid); } void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { switch (pack->opcode) { case ServerOP_RefreshGuild: { if (pack->size != sizeof(ServerGuildRefresh_Struct)) { LogError("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildRefresh_Struct)); return; } ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; LogDebug( "Received guild refresh from world for guild id [{}] changes: name=[{}] motd=[{}] rank=[{}] relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change ); RefreshGuild(s->guild_id); if (s->motd_change) { //resend guild MOTD to all guild members in this zone. entity_list.SendGuildMOTD(s->guild_id); } if (s->name_change) { //until we figure out the guild update packet, we resend the whole guild list. entity_list.SendGuildList(); } if (s->rank_change) { //we need to send spawn appearance packets for all members of this guild in the zone, to everybody. entity_list.SendGuildSpawnAppearance(s->guild_id); } if (s->relation_change) { //unknown until we implement guild relations. } break; } case ServerOP_GuildCharRefresh: { if (pack->size != sizeof(ServerGuildCharRefresh_Struct)) { LogError("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildCharRefresh_Struct)); return; } ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; LogDebug("Received guild member refresh from world for char [{}] from guild [{}]", s->char_id, s->guild_id ); RefreshGuild(s->guild_id); Client *c = entity_list.GetClientByCharID(s->char_id); if (c) { //this reloads the char's guild info from the database and sends appearance updates c->RefreshGuildInfo(); } //it would be nice if we had the packet to send just a one-person update if (s->guild_id == GUILD_NONE) { if (c) { c->SendGuildMOTD(); } } else { entity_list.SendGuildMembers(s->guild_id); //even send GUILD_NONE (empty) } if (s->old_guild_id != 0 && s->old_guild_id != GUILD_NONE && s->old_guild_id != s->guild_id) { entity_list.SendGuildMembers(s->old_guild_id); } else if (c && s->guild_id != GUILD_NONE) { //char is in zone, and has changed into a new guild, send MOTD. c->SendGuildMOTD(); if (c->ClientVersion() >= EQ::versions::ClientVersion::RoF) { c->SendGuildRanks(); } } break; } case ServerOP_GuildRankUpdate: { auto sgrus = (ServerGuildRankUpdate_Struct *) pack->pBuffer; if (is_zone_loaded) { entity_list.SendGuildMemberRankAltBanker(sgrus->guild_id, sgrus->rank, sgrus->member_name, sgrus->alt, sgrus->banker); } break; } case ServerOP_DeleteGuild: { if (pack->size != sizeof(ServerGuildID_Struct)) { LogError("Received ServerOP_DeleteGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildID_Struct)); return; } if (is_zone_loaded) { ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; LogGuilds("Received guild delete from world for guild [{}]", s->guild_id); auto clients = entity_list.GetClientList(); for (auto &c: clients) { if (c.second->GuildID() == s->guild_id) { c.second->SetGuildID(GUILD_NONE); c.second->SetGuildRank(GUILD_RANK_NONE); c.second->SetGuildTributeOptIn(false); c.second->SendGuildActiveTributes(c.second->GuildID()); c.second->SendGuildDeletePacket(s->guild_id); c.second->RefreshGuildInfo(); c.second->MessageString(Chat::Guild, GUILD_DISBANDED); c.second->SendGuildList(); } } auto res = m_guilds.find(s->guild_id); if (res != m_guilds.end()) { delete res->second; m_guilds.erase(res); } } break; } case ServerOP_GuildMemberUpdate: { auto sgmus = (ServerGuildMemberUpdate_Struct *) pack->pBuffer; if (is_zone_loaded) { auto outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); auto gmus = (GuildMemberUpdate_Struct *) outapp->pBuffer; gmus->GuildID = sgmus->guild_id; gmus->ZoneID = sgmus->zone_id; gmus->InstanceID = 0; gmus->LastSeen = sgmus->last_seen; strn0cpy(gmus->MemberName, sgmus->member_name, sizeof(gmus->MemberName)); entity_list.QueueClientsGuild(outapp, sgmus->guild_id); safe_delete(outapp); } break; } case ServerOP_OnlineGuildMembersResponse: { if (is_zone_loaded) { char *Buffer = (char *) pack->pBuffer; uint32 FromID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); uint32 Count = VARSTRUCT_DECODE_TYPE(uint32, Buffer); Client *c = entity_list.GetClientByCharID(FromID); if (!c || !c->IsInAGuild()) { LogGuilds("Invalid Client or not in guild. ID=[{}]", FromID); break; } LogGuilds("Processing ServerOP_OnlineGuildMembersResponse"); auto outapp = new EQApplicationPacket( OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); GuildMemberUpdate_Struct *gmus = (GuildMemberUpdate_Struct *) outapp->pBuffer; char Name[64]; gmus->LastSeen = time(nullptr); gmus->InstanceID = 1; gmus->GuildID = c->GuildID(); for (int i = 0; i < Count; i++) { // Just make the packet once and swap out name/zone and send VARSTRUCT_DECODE_STRING(Name, Buffer); strn0cpy(gmus->MemberName, Name, sizeof(gmus->MemberName)); gmus->ZoneID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); LogGuilds("Sending OP_GuildMemberUpdate to [{}]. Name=[{}] ZoneID=[{}]", FromID, Name, gmus->ZoneID); c->QueuePacket(outapp); } safe_delete(outapp); } break; } case ServerOP_LFGuildUpdate: { if (is_zone_loaded) { char GuildName[33]; char Comments[257]; uint32 FromLevel, ToLevel, Classes, AACount, TimeZone, TimePosted, Toggle; pack->ReadString(GuildName); pack->ReadString(Comments); FromLevel = pack->ReadUInt32(); ToLevel = pack->ReadUInt32(); Classes = pack->ReadUInt32(); AACount = pack->ReadUInt32(); TimeZone = pack->ReadUInt32(); TimePosted = pack->ReadUInt32(); Toggle = pack->ReadUInt32(); uint32 GuildID = GetGuildIDByName(GuildName); if (GuildID == GUILD_NONE) { break; } auto outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_GuildToggle_Struct)); auto *gts = (LFGuild_GuildToggle_Struct *) outapp->pBuffer; gts->Command = 1; strcpy(gts->Comment, Comments); gts->FromLevel = FromLevel; gts->ToLevel = ToLevel; gts->Classes = Classes; gts->AACount = AACount; gts->TimeZone = TimeZone; gts->Toggle = Toggle; gts->TimePosted = TimePosted; gts->Name[0] = 0; entity_list.QueueClientsGuild(outapp, GuildID); safe_delete(outapp); } break; } case ServerOP_GuildPermissionUpdate: { if (is_zone_loaded) { auto *sgpus = (ServerGuildPermissionUpdate_Struct *)pack->pBuffer; auto guild = GetGuildByGuildID(sgpus->guild_id); if (!guild) { return; } if (sgpus->function_value) { guild->functions[sgpus->function_id].perm_value |= (1UL << (8 - sgpus->rank)); } else { guild->functions[sgpus->function_id].perm_value &= ~(1UL << (8 - sgpus->rank)); } auto outapp = new EQApplicationPacket(OP_GuildUpdate, sizeof(GuildPermission_Struct)); auto *guuacs = (GuildPermission_Struct *) outapp->pBuffer; guuacs->Action = GuildUpdatePermissions; guuacs->rank = sgpus->rank; guuacs->function_id = sgpus->function_id; guuacs->value = sgpus->function_value; entity_list.QueueClientsGuild(outapp, sgpus->guild_id); LogDebug( "Zone Received guild permission update from world for rank {} function id [{}] and value [{}]", guuacs->rank = sgpus->rank, guuacs->function_id = sgpus->function_id, guuacs->value = sgpus->function_value ); safe_delete(outapp); if (sgpus->function_id == GUILD_ACTION_DISPLAY_GUILD_NAME) { entity_list.SendGuildSpawnAppearance(sgpus->guild_id); } //for backwards compatibility with guild bank functionality //if the four permissions (deposit, promote, withdraw and view) exist for a rank, turn on the banker flag for pre RoF clients if (IsActionABankAction((GuildAction) sgpus->function_id) && GetGuildBankerStatus(sgpus->guild_id, sgpus->rank)) { entity_list.GuildSetPreRoFBankerFlag(sgpus->guild_id, sgpus->rank, true); } else if (IsActionABankAction((GuildAction) sgpus->function_id) && !GetGuildBankerStatus(sgpus->guild_id, sgpus->rank)) { entity_list.GuildSetPreRoFBankerFlag(sgpus->guild_id, sgpus->rank, false); } } break; } case ServerOP_GuildRankNameChange: { if (is_zone_loaded) { auto *s = (ServerGuildRankNameChange *) pack->pBuffer; LogGuilds( "Received guild rank name change from world for rank [{}] from guild [{}]", s->rank, s->guild_id ); auto guild = guild_mgr.GetGuildByGuildID(s->guild_id); if (guild) { guild->rank_names[s->rank] = s->rank_name; auto outapp = new EQApplicationPacket(OP_GuildUpdate,sizeof(GuildUpdateUCPStruct)); auto *gucp = (GuildUpdateUCPStruct *) outapp->pBuffer; gucp->payload.rank_name.rank = s->rank; strn0cpy(gucp->payload.rank_name.rank_name, s->rank_name, sizeof(gucp->payload.rank_name.rank_name)); gucp->action = GuildUpdateRanks; entity_list.QueueClientsGuild(outapp, s->guild_id); safe_delete(outapp); } } break; } case ServerOP_GuildMemberLevelUpdate: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildMemberLevel(s_in->guild_id, s_in->player_level, s_in->player_name); } break; } case ServerOP_GuildMemberPublicNote: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildMemberPublicNote(s_in->guild_id, s_in->player_name, s_in->note); } break; } case ServerOP_GuildSendGuildList: { if (is_zone_loaded) { entity_list.SendGuildList(); } break; } case ServerOP_GuildChannel: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildChannel(s_in->guild_id); } break; } case ServerOP_GuildMembersList: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildMembersList(s_in->guild_id); } break; } case ServerOP_GuildURL: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildURL(s_in->guild_id); } break; } case ServerOP_GuildMemberRemove: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildMemberRemove(s_in->guild_id, s_in->player_name); } break; } case ServerOP_GuildMemberAdd: { if (is_zone_loaded) { auto s_in = (ServerOP_GuildMessage_Struct *) pack->pBuffer; entity_list.SendGuildMemberAdd( s_in->guild_id, s_in->player_level, s_in->player_class, s_in->player_rank, 0, s_in->player_zone_id, s_in->player_name ); } break; } } } void ZoneGuildManager::SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen) { auto pack = new ServerPacket(ServerOP_GuildMemberUpdate, sizeof(ServerGuildMemberUpdate_Struct)); ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer; sgmus->guild_id = GuildID; strn0cpy(sgmus->member_name, MemberName, sizeof(sgmus->member_name)); sgmus->zone_id = ZoneID; sgmus->last_seen = LastSeen; worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID) { auto pack = new ServerPacket(ServerOP_RequestOnlineGuildMembers, sizeof(ServerRequestOnlineGuildMembers_Struct)); ServerRequestOnlineGuildMembers_Struct *srogm = (ServerRequestOnlineGuildMembers_Struct*)pack->pBuffer; srogm->FromID = FromID; srogm->GuildID = GuildID; worldserver.SendPacket(pack); safe_delete(pack); } ZoneGuildManager::~ZoneGuildManager() { ClearGuilds(); } GuildBankManager::~GuildBankManager() { } void GuildBankManager::Load(uint32 guild_id) { auto results = GuildBankRepository::GetWhere( database, fmt::format("`guild_id` = '{}' ORDER BY `area`, `slot`", guild_id) ); auto bank = std::make_shared(); bank->guild_id = guild_id; int32 deposit_slot = 0; int32 main_slot = 0; for (auto &it: results) { if (it.area == GuildBankMainArea) { it.slot = deposit_slot; bank->items.main_area.emplace(deposit_slot, it); deposit_slot++; } else { it.slot = main_slot; bank->items.deposit_area.emplace(main_slot, it); main_slot++; } } banks.push_back(bank); } bool GuildBankManager::IsLoaded(uint32 guild_id) { return GetGuildBank(guild_id) ? true : false; } void GuildBankManager::SendGuildBank(Client *c) { if (!c || !c->IsInAGuild()) { return; } if (!IsLoaded(c->GuildID())) { Load(c->GuildID()); } auto guild_bank = GetGuildBank(c->GuildID()); if (!guild_bank) { LogError("Unable to find guild bank for guild ID [{}]", c->GuildID()); return; } // RoF+ uses a bulk list packet -- This is also how the Action 0 of older clients basically works if (c->ClientVersionBit() & EQ::versions::maskRoFAndLater) { auto outapp = new EQApplicationPacket(OP_GuildBankItemList, sizeof(GuildBankItemListEntry_Struct) * 240); for (int i = 0; i < c->GetInv().GetLookup()->InventoryTypeSize.GuildBankDeposit; i++) { const EQ::ItemData *item = database.GetItem(guild_bank->items.deposit_area[i].item_id); if (item) { outapp->WriteUInt8(1); outapp->WriteUInt32(guild_bank->items.deposit_area[i].permissions); outapp->WriteString(guild_bank->items.deposit_area[i].who_for.c_str()); outapp->WriteString(guild_bank->items.deposit_area[i].donator.c_str()); outapp->WriteUInt32(item->ID); outapp->WriteUInt32(item->Icon); if (item->Stackable) { outapp->WriteUInt32(guild_bank->items.deposit_area[i].quantity); outapp->WriteUInt8(item->StackSize == guild_bank->items.deposit_area[i].quantity ? 1 : 1); } else { outapp->WriteUInt32(1); outapp->WriteUInt8(0); } outapp->WriteUInt8(item->IsEquipable(c->GetBaseRace(), c->GetBaseClass()) ? 1 : 0); outapp->WriteString(item->Name); } else { outapp->WriteUInt8(0); // empty } } for (int i = 0; i < c->GetInv().GetLookup()->InventoryTypeSize.GuildBankMain; ++i) { const EQ::ItemData *Item = database.GetItem(guild_bank->items.main_area[i].item_id); if (Item) { outapp->WriteUInt8(1); outapp->WriteUInt32(guild_bank->items.main_area[i].permissions); outapp->WriteString(guild_bank->items.main_area[i].who_for.c_str()); outapp->WriteString(guild_bank->items.main_area[i].donator.c_str()); outapp->WriteUInt32(Item->ID); outapp->WriteUInt32(Item->Icon); if (Item->Stackable) { outapp->WriteUInt32(guild_bank->items.main_area[i].quantity); outapp->WriteUInt8(Item->StackSize == guild_bank->items.main_area[i].quantity ? 1 : 1); } else { outapp->WriteUInt32(1); outapp->WriteUInt8(0); } outapp->WriteUInt8(Item->IsEquipable(c->GetBaseRace(), c->GetBaseClass()) ? 1 : 0); outapp->WriteString(Item->Name); } else { outapp->WriteUInt8(0); // empty } } outapp->size = outapp->GetWritePosition(); // truncate to used size c->FastQueuePacket(&outapp); return; } for (int i = 0; i < c->GetInv().GetLookup()->InventoryTypeSize.GuildBankDeposit; ++i) { if (guild_bank->items.deposit_area[i].item_id > 0) { const EQ::ItemData *item = database.GetItem(guild_bank->items.deposit_area[i].item_id); if (!item) { continue; } auto outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); auto gbius = (GuildBankItemUpdate_Struct *) outapp->pBuffer; if (!item->Stackable) { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, item->ID, item->Icon, 1, guild_bank->items.deposit_area[i].permissions, 0, false ); } else { if (guild_bank->items.deposit_area[i].quantity == item->StackSize) { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, item->ID, item->Icon, guild_bank->items.deposit_area[i].quantity, guild_bank->items.deposit_area[i].permissions, 1, false ); } else { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, item->ID, item->Icon, guild_bank->items.deposit_area[i].quantity, guild_bank->items.deposit_area[i].permissions, 1, false ); } } strn0cpy(gbius->item_name, item->Name, sizeof(gbius->item_name)); strn0cpy(gbius->donator, guild_bank->items.deposit_area[i].donator.c_str(), sizeof(gbius->donator)); strn0cpy(gbius->who_for, guild_bank->items.deposit_area[i].who_for.c_str(), sizeof(gbius->who_for)); c->FastQueuePacket(&outapp); } } for (int i = 0; i < c->GetInv().GetLookup()->InventoryTypeSize.GuildBankMain; ++i) { if (guild_bank->items.main_area[i].item_id > 0) { const EQ::ItemData *item = database.GetItem(guild_bank->items.main_area[i].item_id); if (!item) { continue; } bool useable = item->IsEquipable(c->GetBaseRace(), c->GetBaseClass()); auto outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); auto gbius = (GuildBankItemUpdate_Struct *) outapp->pBuffer; if (!item->Stackable) { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, item->ID, item->Icon, 1, guild_bank->items.main_area[i].permissions, 0, useable ); } else { if (guild_bank->items.main_area[i].quantity == item->StackSize) { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, item->ID, item->Icon, guild_bank->items.main_area[i].quantity, guild_bank->items.main_area[i].permissions, 1, useable ); } else { gbius->Init( GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, item->ID, item->Icon, guild_bank->items.main_area[i].quantity, guild_bank->items.main_area[i].permissions, 1, useable ); } } strn0cpy(gbius->item_name, item->Name, sizeof(gbius->item_name)); strn0cpy(gbius->donator, guild_bank->items.main_area[i].donator.c_str(), sizeof(gbius->donator)); strn0cpy(gbius->who_for, guild_bank->items.main_area[i].who_for.c_str(), sizeof(gbius->who_for)); c->FastQueuePacket(&outapp); } } } bool GuildBankManager::IsAreaFull(uint32 guild_id, uint16 area) { return NextFreeBankSlot(guild_id, area) == -1; } bool GuildBankManager::AddItem(GuildBankRepository::GuildBank &guild_bank_item, Client* c) { auto guild_bank = GetGuildBank(guild_bank_item.guild_id); if (!guild_bank) { LogError("Unable to find guild bank for guild ID [{}]", guild_bank_item.guild_id); return false; } switch (guild_bank_item.area) { case GuildBankMainArea: { for (auto &[key, item]: guild_bank->items.main_area) { if (item.item_id == 0) { item.slot = NextFreeBankSlot(guild_bank_item.guild_id, GuildBankMainArea); guild_bank_item.slot = item.slot; auto e = GuildBankRepository::InsertOne(database, guild_bank_item); if (!e.id) { return false; } guild_bank_item.id = e.id; item = guild_bank_item; break; } } break; } case GuildBankDepositArea: { for (auto &[key, item]: guild_bank->items.deposit_area) { if (item.item_id == 0) { item.slot = NextFreeBankSlot(guild_bank_item.guild_id, GuildBankDepositArea); guild_bank_item.slot = item.slot; auto e = GuildBankRepository::InsertOne(database, guild_bank_item); if (!e.id) { return false; } guild_bank_item.id = e.id; item = guild_bank_item; break; } } break; } default: { return false; } } SendGuildBankItemUpdate(guild_bank_item.guild_id, guild_bank_item.slot, guild_bank_item.area, true, c); return true; } int GuildBankManager::Promote(uint32 guild_id, int slot_id, Client* c) { if (slot_id < 0) { return -1; } auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return -1; } auto it_deposit = guild_bank->items.deposit_area.find(slot_id); if (it_deposit == std::end(guild_bank->items.deposit_area)) { return -1; } auto new_item = GuildBankRepository::NewEntity(); new_item.id = it_deposit->second.id; new_item.guild_id = it_deposit->second.guild_id; new_item.area = GuildBankMainArea; new_item.slot = NextFreeBankSlot(guild_id, GuildBankMainArea); new_item.item_id = it_deposit->second.item_id; new_item.augment_one_id = it_deposit->second.augment_one_id; new_item.augment_two_id = it_deposit->second.augment_two_id; new_item.augment_three_id = it_deposit->second.augment_three_id; new_item.augment_four_id = it_deposit->second.augment_four_id; new_item.augment_five_id = it_deposit->second.augment_five_id; new_item.augment_six_id = it_deposit->second.augment_six_id; new_item.quantity = it_deposit->second.quantity; new_item.donator = it_deposit->second.donator; new_item.who_for = it_deposit->second.who_for; new_item.permissions = it_deposit->second.permissions; GuildBankRepository::UpdateOne(database, new_item); SendGuildBankItemUpdate(it_deposit->second.guild_id, it_deposit->second.slot, it_deposit->second.area, false, c); it_deposit->second = std::move(GuildBankRepository::NewEntity()); guild_bank->items.main_area[new_item.slot] = new_item; SendGuildBankItemUpdate(new_item.guild_id, new_item.slot, new_item.area, true, c); return new_item.slot; } void GuildBankManager::SetPermissions(uint32 guild_id, uint16 slot_id, uint32 permissions, const char *member_name, Client* c) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return; } auto item = &guild_bank->items.main_area[slot_id]; item->permissions = permissions; item->who_for.clear(); if (permissions == GuildBankSingleMember) { item->who_for = member_name; } GuildBankRepository::UpdateOne(database, *item); SendGuildBankItemUpdate(item->guild_id, item->slot, item->area, true, c); } std::unique_ptr GuildBankManager::GetItem(uint32 guild_id, uint16 area, uint16 slot_id, uint32 quantity) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return nullptr; } std::unique_ptr inst; if (area == GuildBankDepositArea) { if (slot_id > guild_bank->items.deposit_area.size()) { return nullptr; } inst.reset(database.CreateItem( guild_bank->items.deposit_area[slot_id].item_id, guild_bank->items.deposit_area[slot_id].quantity, guild_bank->items.deposit_area[slot_id].augment_one_id, guild_bank->items.deposit_area[slot_id].augment_two_id, guild_bank->items.deposit_area[slot_id].augment_three_id, guild_bank->items.deposit_area[slot_id].augment_four_id, guild_bank->items.deposit_area[slot_id].augment_five_id, guild_bank->items.deposit_area[slot_id].augment_six_id) ); if (!inst) { return nullptr; } if (!inst->IsStackable()) { inst->SetCharges(guild_bank->items.deposit_area[slot_id].quantity); } else { if (quantity <= guild_bank->items.deposit_area[slot_id].quantity) { inst->SetCharges(quantity); } else { inst->SetCharges(guild_bank->items.deposit_area[slot_id].quantity); } } } else { if (slot_id > guild_bank->items.main_area.size()) { return nullptr; } inst.reset(database.CreateItem( guild_bank->items.main_area[slot_id].item_id, guild_bank->items.main_area[slot_id].quantity, guild_bank->items.main_area[slot_id].augment_one_id, guild_bank->items.main_area[slot_id].augment_two_id, guild_bank->items.main_area[slot_id].augment_three_id, guild_bank->items.main_area[slot_id].augment_four_id, guild_bank->items.main_area[slot_id].augment_five_id, guild_bank->items.main_area[slot_id].augment_six_id) ); if (!inst) { return nullptr; } if (!inst->IsStackable()) { inst->SetCharges(guild_bank->items.main_area[slot_id].quantity); } else { if (quantity <= guild_bank->items.main_area[slot_id].quantity) { inst->SetCharges(quantity); } else { inst->SetCharges(guild_bank->items.main_area[slot_id].quantity); } } } return inst; } bool GuildBankManager::HasItem(uint32 guild_id, uint32 item_id) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return false; } for (auto const &[slot, item]: guild_bank->items.deposit_area) { if (item.item_id == item_id) { return true; } } for (auto const &[slot, item]: guild_bank->items.main_area) { if (item.item_id == item_id) { return true; } } return false; } std::shared_ptr GuildBankManager::GetGuildBank(const uint32 guild_id) { if (!GuildBanks) { return nullptr; } for (auto &b:banks) { if (b->guild_id == guild_id) { return b; } } return nullptr; } bool GuildBankManager::DeleteItem(uint32 guild_id, uint16 area, uint16 slot_id, uint32 quantity, Client* c) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return false; } auto item = &guild_bank->items.deposit_area[slot_id]; if (area == GuildBankMainArea) { item = &guild_bank->items.main_area[slot_id]; } if (item->item_id == 0) { return false; } // delete the item if (item->quantity == quantity) { SendGuildBankItemUpdate(item->guild_id, item->slot, item->area, false, c); GuildBankRepository::DeleteOne(database, item->id); item->item_id = 0; item->id = 0; item->guild_id = 0; item->slot = 0; item->quantity = 0; item->permissions = 0; item->donator.clear(); item->who_for.clear(); return true; } //otherwise update the quantity item->quantity -= quantity; GuildBankRepository::UpdateOne(database, *item); SendGuildBankItemUpdate(item->guild_id, item->slot, item->area, true, c); return true; } bool GuildBankManager::MergeStacks(uint32 guild_id, uint16 slot_id, Client* c) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return false; } auto merge_bank_item = &guild_bank->items.main_area[slot_id]; const auto merge_item = database.GetItem(merge_bank_item->item_id); if (merge_bank_item->item_id == 0 || !merge_item) { return false; } const int16 stack_size = merge_item->StackSize; std::vector queue{}; std::vector queue_delete{}; std::vector queue_updates{}; for (auto &[key, item]: guild_bank->items.main_area) { if (item.item_id == merge_bank_item->item_id && item.slot != slot_id) { queue.push_back(&item); } } if (queue.empty()) { return false; } std::ranges::sort(queue.begin(), queue.end(), [](auto &a, auto &b) { return a->quantity < b->quantity; }); for (auto const &i: queue) { const int16 remaining_stack_size = stack_size - static_cast(merge_bank_item->quantity); merge_bank_item->quantity += i->quantity; if (merge_bank_item->quantity <= stack_size) { i->item_id = 0; queue_delete.push_back(std::to_string(i->id)); queue_updates.push_back(*i); continue; } if (merge_bank_item->quantity > stack_size) { merge_bank_item->quantity = stack_size; i->quantity -= remaining_stack_size; queue_updates.push_back(*i); queue_updates.push_back(*merge_bank_item); break; } } if (merge_bank_item->quantity < stack_size) { queue_updates.push_back(*merge_bank_item); queue.push_back(merge_bank_item); } GuildBankRepository::ReplaceMany(database, queue_updates); if (!queue_delete.empty()) { GuildBankRepository::DeleteWhere( database, fmt::format("`id` IN({})", Strings::Implode(",", queue_delete)).c_str() ); } for (auto const &i: queue_updates) { if (i.item_id == 0) { SendGuildBankItemUpdate(i.guild_id, i.slot, GuildBankMainArea, false, c); continue; } SendGuildBankItemUpdate(i.guild_id, i.slot, GuildBankMainArea, true, c); } return true; } bool GuildBankManager::SplitStack(uint32 guild_id, uint16 slot_id, uint32 quantity, Client* c) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return false; } if (IsAreaFull(guild_id, GuildBankMainArea)) { return false; } auto split_bank_item = &guild_bank->items.main_area[slot_id]; const auto split_item = database.GetItem(split_bank_item->item_id); if (split_bank_item->item_id == 0 || !split_item) { return false; } auto new_item = GuildBankRepository::NewEntity(); new_item = *split_bank_item; new_item.id = 0; new_item.slot = NextFreeBankSlot(guild_id, GuildBankMainArea); new_item.quantity = quantity; split_bank_item->quantity -= quantity; GuildBankRepository::UpdateOne(database, *split_bank_item); auto e = GuildBankRepository::InsertOne(database, new_item); new_item.id = e.id; guild_bank->items.main_area[new_item.slot] = new_item; SendGuildBankItemUpdate(split_bank_item->guild_id, split_bank_item->slot, split_bank_item->area, true, c); SendGuildBankItemUpdate(new_item.guild_id, new_item.slot, new_item.area, true, c); return true; } int32 GuildBankManager::NextFreeBankSlot(uint32 guild_id, uint32 area) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return -1; } if (area == GuildBankMainArea) { auto it = std::ranges::find_if(guild_bank->items.main_area.begin(), guild_bank->items.main_area.end(), [&](auto &m) { return m.second.item_id == 0; } ); if (it == guild_bank->items.main_area.end()) { return -1; } return it->first; } auto it = std::ranges::find_if( guild_bank->items.deposit_area.begin(), guild_bank->items.deposit_area.end(), [&](auto &m) { return m.second.item_id == 0; } ); if (it == guild_bank->items.deposit_area.end()) { return -1; } return it->first; } void ZoneGuildManager::UpdateRankPermission(uint32 gid, uint32 charid, uint32 fid, uint32 rank, uint32 value) { auto guild = GetGuildByGuildID(gid); if (!guild) { return; } if (value) { guild->functions[fid].perm_value |= (1UL << (8 - rank)); } else { guild->functions[fid].perm_value &= ~(1UL << (8 - rank)); } auto query = fmt::format("UPDATE guild_permissions SET permission = {} WHERE perm_id = {} AND guild_id = {};", guild->functions[fid].perm_value, fid, gid); auto results = m_db->QueryDatabase(query); } void ZoneGuildManager::SendPermissionUpdate(uint32 guild_id, uint32 rank, uint32 function_id, uint32 value) { auto pack = new ServerPacket(ServerOP_GuildPermissionUpdate, sizeof(ServerGuildPermissionUpdate_Struct)); auto *sgpus = (ServerGuildPermissionUpdate_Struct *) pack->pBuffer; sgpus->guild_id = guild_id; sgpus->rank = rank; sgpus->function_id = function_id; sgpus->function_value = value; worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::UpdateRankName(uint32 guild_id, uint32 rank, std::string rank_name) { GuildRanksRepository::UpdateTitle(*m_db, guild_id, rank, rank_name); } void ZoneGuildManager::SendRankName(uint32 guild_id, uint32 rank, std::string rank_name) { auto pack = new ServerPacket(ServerOP_GuildRankNameChange, sizeof(ServerGuildRankNameChange)); auto *sgpus = (ServerGuildRankNameChange *) pack->pBuffer; sgpus->guild_id = guild_id; sgpus->rank = rank; strn0cpy(sgpus->rank_name, rank_name.c_str(), sizeof(sgpus->rank_name)); worldserver.SendPacket(pack); safe_delete(pack); } void ZoneGuildManager::SendAllRankNames(uint32 guild_id, uint32 char_id) { auto guild = GetGuildByGuildID(guild_id); auto c = entity_list.GetClientByCharID(char_id); if (guild && c) { auto outapp = new EQApplicationPacket(OP_GuildUpdate, sizeof(GuildUpdateUCPStruct)); GuildUpdateUCPStruct *gucp = (GuildUpdateUCPStruct *)outapp->pBuffer; for (int i = GUILD_LEADER; i <= GUILD_RECRUIT; i++) { gucp->payload.rank_name.rank = i; strn0cpy(gucp->payload.rank_name.rank_name, guild->rank_names[i].c_str(), sizeof(gucp->payload.rank_name.rank_name)); gucp->action = GuildUpdateRanks; c->QueuePacket(outapp); } safe_delete(outapp); } } BaseGuildManager::GuildInfo* ZoneGuildManager::GetGuildByGuildID(uint32 guild_id) { auto guild = m_guilds.find(guild_id); if (guild != m_guilds.end()) { return guild->second; } return nullptr; } uint8* ZoneGuildManager::MakeGuildMembers(uint32 guild_id, const char* prefix_name, uint32& length) { uint8* retbuffer; std::vector members; if (!GetEntireGuild(guild_id, members)) return(nullptr); //figure out the actual packet length. uint32 fixed_length = sizeof(Internal_GuildMembers_Struct) + members.size() * sizeof(Internal_GuildMemberEntry_Struct); std::vector::iterator cur, end; CharGuildInfo* ci; cur = members.begin(); end = members.end(); uint32 name_len = 0; uint32 note_len = 0; for (; cur != end; ++cur) { ci = *cur; name_len += ci->char_name.length(); note_len += ci->public_note.length(); } //calc total length. length = fixed_length + name_len + note_len + members.size() * 2; //string data + null terminators //make our nice buffer retbuffer = new uint8[length]; auto gms = (Internal_GuildMembers_Struct*)retbuffer; //fill in the global header strcpy(gms->player_name, prefix_name); gms->count = members.size(); gms->name_length = name_len; gms->note_length = note_len; char *name_buf = (char *) (retbuffer + fixed_length); char *note_buf = (char *) (name_buf + name_len + members.size()); //fill in each member's entry. Internal_GuildMemberEntry_Struct* e = gms->member; cur = members.begin(); end = members.end(); for (; cur != end; ++cur) { ci = *cur; //the order we set things here must match the struct //nice helper macro #define SlideStructString(field, str) \ strcpy(field, str.c_str()); \ field += str.length() + 1 #define PutField(field) \ e->field = ci->field SlideStructString(name_buf, ci->char_name); PutField(level); e->banker = ci->banker + (ci->alt * 2); // low bit is banker flag, next bit is 'alt' flag. PutField(class_); auto c = entity_list.GetClientByID(ci->char_id); if (c && c->ClientVersion() < EQ::versions::ClientVersion::RoF) { switch (ci->rank) { case GUILD_RECRUIT: case GUILD_INITIATE: case GUILD_JUNIOR_MEMBER: case GUILD_MEMBER: case GUILD_SENIOR_MEMBER: { ci->rank = GUILD_MEMBER_TI; break; } case GUILD_OFFICER: case GUILD_SENIOR_OFFICER: { ci->rank = GUILD_OFFICER_TI; break; } case GUILD_LEADER: { ci->rank = GUILD_LEADER_TI; break; } default: { ci->rank = GUILD_RANK_NONE_TI; break; } } } PutField(rank); PutField(time_last_on); PutField(tribute_enable); PutField(total_tribute); PutField(last_tribute); SlideStructString(note_buf, ci->public_note); //e->zoneinstance = 0; if (ci->online) { e->zone_id = ci->zone_id; //This routine, if there is a zone_id, will update the entire guild window (roster, notes, tribute) for online characters. } else { e->zone_id = 0; //If zone_id is 0 and we rely on the current world routine, the notes/tribute tabs are not updated for online characters. } #undef SlideStructString #undef PutFieldN delete* cur; e++; } return(retbuffer); } void ZoneGuildManager::SendToWorldMemberLevelUpdate(uint32 guild_id, uint32 level, std::string player_name) { auto s_outapp = new ServerPacket(ServerOP_GuildMemberLevelUpdate, sizeof(ServerOP_GuildMessage_Struct)); auto *s_out = (ServerOP_GuildMessage_Struct *) s_outapp->pBuffer; s_out->guild_id = guild_id; s_out->player_level = level; strn0cpy(s_out->player_name, player_name.c_str(), sizeof(s_out->player_name)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note) { auto s_outapp = new ServerPacket(ServerOP_GuildMemberPublicNote, sizeof(ServerOP_GuildMessage_Struct)); auto *s_out = (ServerOP_GuildMessage_Struct *) s_outapp->pBuffer; s_out->guild_id = guild_id; strn0cpy(s_out->player_name, player_name.c_str(), sizeof(s_out->player_name)); strn0cpy(s_out->note, public_note.c_str(), sizeof(s_out->note)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldGuildChannel(uint32 guild_id, std::string channel) { auto s_outapp = new ServerPacket(ServerOP_GuildChannel, sizeof(ServerOP_GuildMessage_Struct)); auto *s_out = (ServerOP_GuildMessage_Struct *) s_outapp->pBuffer; s_out->guild_id = guild_id; strn0cpy(s_out->channel, channel.c_str(), sizeof(s_out->channel)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldGuildURL(uint32 guild_id, std::string url) { auto s_outapp = new ServerPacket(ServerOP_GuildURL, sizeof(ServerOP_GuildMessage_Struct)); auto *s_out = (ServerOP_GuildMessage_Struct *) s_outapp->pBuffer; s_out->guild_id = guild_id; strn0cpy(s_out->url, url.c_str(), sizeof(s_out->url)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldMemberRemove(uint32 guild_id, std::string player_name) { auto s_outapp = new ServerPacket(ServerOP_GuildMemberRemove, sizeof(ServerOP_GuildMessage_Struct)); ServerOP_GuildMessage_Struct* s_out = (ServerOP_GuildMessage_Struct*)s_outapp->pBuffer; s_out->guild_id = guild_id; strn0cpy(s_out->player_name, player_name.c_str(), sizeof(s_out->player_name)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldMemberAdd(uint32 guild_id, uint32 char_id, uint32 level, uint32 _class, uint32 rank, uint32 zone_id, std::string player_name) { auto s_outapp = new ServerPacket(ServerOP_GuildMemberAdd, sizeof(ServerOP_GuildMessage_Struct)); auto s_out = (ServerOP_GuildMessage_Struct*)s_outapp->pBuffer; s_out->guild_id = guild_id; s_out->player_level = level; s_out->player_rank = rank; s_out->player_zone_id = zone_id; s_out->player_class = _class; strn0cpy(s_out->player_name, player_name.c_str(), sizeof(s_out->player_name)); worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } void ZoneGuildManager::SendToWorldSendGuildList() { auto s_outapp = new ServerPacket(ServerOP_GuildSendGuildList, sizeof(ServerOP_GuildMessage_Struct)); auto s_out = (ServerOP_GuildMessage_Struct*)s_outapp->pBuffer; worldserver.SendPacket(s_outapp); safe_delete(s_outapp); } bool ZoneGuildManager::RemoveMember(uint32 guild_id, uint32 char_id, std::string player_name) { GuildMembersRepository::DeleteOne(*m_db, char_id); SendToWorldMemberRemove(guild_id, player_name); return true; } void ZoneGuildManager::MemberAdd(uint32 guild_id, uint32 char_id, uint32 level, uint32 _class, uint32 rank, uint32 zone_id, std::string player_name) { GuildMembersRepository::GuildMembers m; m.alt = 0; m.banker = rank == GUILD_LEADER ? 1 : 0; m.last_tribute = 0; m.total_tribute = 0; m.tribute_enable = 0; m.rank_ = rank; m.char_id = char_id; m.guild_id = guild_id; m.online = 1; m.public_note.clear(); GuildMembersRepository::ReplaceOne(*m_db, m); SendToWorldMemberAdd(guild_id, char_id, level, _class, rank, zone_id, player_name); } bool ZoneGuildManager::IsActionABankAction(GuildAction action) { return action == GUILD_ACTION_BANK_DEPOSIT_ITEMS || action == GUILD_ACTION_BANK_PROMOTE_ITEMS || action == GUILD_ACTION_BANK_VIEW_ITEMS || action == GUILD_ACTION_BANK_WITHDRAW_ITEMS; } void ZoneGuildManager::SendToWorldMemberRankUpdate(uint32 guild_id, uint32 rank, uint32 banker, uint32 alt, bool no_update, const char *player_name) { auto outapp = new ServerPacket(ServerOP_GuildRankUpdate, sizeof(ServerGuildRankUpdate_Struct)); auto sr = (ServerGuildRankUpdate_Struct *) outapp->pBuffer; sr->guild_id = guild_id; sr->rank = rank; sr->banker = banker; sr->alt = alt; sr->no_update = no_update; strn0cpy(sr->member_name, player_name, sizeof(sr->member_name)); worldserver.SendPacket(outapp); safe_delete(outapp) } bool ZoneGuildManager::MemberRankUpdate(uint32 guild_id, uint32 rank, uint32 banker, uint32 alt, bool no_update, const char *player_name) { SendToWorldMemberRankUpdate(guild_id, rank, banker, alt, no_update, player_name); return true; } void ZoneGuildManager::SendToWorldSendGuildMembersList(uint32 guild_id) { auto outapp = new ServerPacket(ServerOP_GuildMembersList, sizeof(ServerOP_GuildMessage_Struct)); auto sr = (ServerOP_GuildMessage_Struct *) outapp->pBuffer; sr->guild_id = guild_id; worldserver.SendPacket(outapp); safe_delete(outapp) } void GuildBankManager::SendGuildBankItemUpdate(uint32 guild_id, int32 slot_id, uint32 area, bool display, Client* c) { auto guild_bank = GetGuildBank(guild_id); if (!guild_bank) { return; } auto item = guild_bank->items.deposit_area[slot_id]; if (area == GuildBankMainArea) { item = guild_bank->items.main_area[slot_id]; } const auto item_data = database.GetItem(item.item_id); if (item_data && (item_data->MaxCharges > 0 || item_data->MaxCharges < 0)) { item.quantity = 1; } GuildBankItemUpdate_Struct gbius{}; gbius.action = GuildBankItemUpdate; gbius.slot_id = item.slot; gbius.area = item.area; gbius.item_id = item.item_id; gbius.permissions = item.permissions; gbius.icon_id = item_data ? item_data->Icon : 0; gbius.display = display; gbius.allow_merge = item_data ? item_data->Stackable : false; gbius.is_useable = item_data ? item_data->IsEquipable(c->GetRace(), c->GetClass()) : false; gbius.quantity = item.quantity; strn0cpy(gbius.item_name, item_data ? item_data->Name : "", sizeof(gbius.item_name)); strn0cpy(gbius.donator, item.donator.empty() ? "" : item.donator.c_str(), sizeof(gbius.donator)); strn0cpy(gbius.who_for, item.who_for.empty() ? "" : item.who_for.c_str(), sizeof(gbius.who_for)); entity_list.QueueClientsGuildBankItemUpdate(&gbius, guild_id); } bool ZoneGuildManager::SetGuild(Client *client, uint32 guild_id, uint8 rank) { if (!client || rank > GUILD_MAX_RANK || !GetGuildByGuildID(guild_id)) { return false; } if (rank <= GUILD_RANK_NONE) { rank = GUILD_RECRUIT; } const uint32 current_guild_id = client->GuildID(); if (current_guild_id == guild_id) { return false; } if (current_guild_id != guild_id && current_guild_id != GUILD_NONE) { guild_mgr.RemoveMember(client->GuildID(), client->CharacterID(), std::string(client->GetCleanName())); } client->SetGuildID(guild_id); client->SetGuildRank(rank); MemberAdd( guild_id, client->CharacterID(), client->GetLevel(), client->GetClass(), rank, client->GetZoneID(), client->GetName() ); client->SendGuildSpawnAppearance(); return true; }