From 91f5932c6d00b4b6530efaa942043cbf49eb9f90 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sat, 10 Feb 2024 05:27:58 -0400 Subject: [PATCH] [Feature] Add RoF2 Guild features (#3699) * [Feature] Add additional Guild Features This adds the following guild features and design pattern - the existing guild system was used - guild features are based on RoF2 within source with translaters used to converted between client differences - backward compatible with Ti and UF, and allows for mixed client servers - Guild Back for Ti and UF is based on RoF2 Permissions for banking if Guild Leader does not use Ti/UF - Guild Ranks and Permissions are enabled. - Guild Tributes are enabled. - Event logging via rules for donating tribute items and plat - Rules to limit Guild Tributes based on max level of server - Rewrote guild communications to client using specific opcodes -- Server no longer sends a guild member list on each zone -- Guild window is updated when a member levels, rank changes, zone changes, banker/alt status using individual opcodes -- When a member is removed or added to a guild, a single opcode is sent to each guild member -- This reduces network traffic considerably Known issues: - Visual bug only. Guild Tributes window will display a 0 for level if tribute is above max level rule setting. - Visual bug only. Guild Mgmt Window will not display an online member if the player has 'show offline' unchecked and a guild member zones within the Notes/Tribute tab. This is resolved by selecting and de-selecting the 'Show Offline' checkbox. * Updated RoF2 Guild Comms Updated RoF2 Guild Comms Update RoF2 Opcodes Rewrote RoF2 Guild Communications using specific opcodes. Added database changes - they are irreversible * Formatting * Update base_guild_members_repository.h * Format GuildInfo * Format GuildAction enum * Formatting in clientlist * quantity vs quantity * desc vs description * Format structs * Inline struct values * Formatting * Formatting * Formatting fixes * Formatting items * Formatting * Formatting * struct formatting updates * Updated formatting * Updated - std:string items - naming conventions - magic numbers * Repo refactors Other formatting updates * Remove test guild commands * Updated #guild info command * Add new repo methods for Neckolla ReplaceOne and ReplaceMany * Fix guild_tributes repo * Update database_update_manifest.cpp * Phase 1 of final testing with RoF2 -> RoF2. Next phase will be inter compatibility review * Remove #guild testing commands * Fix uf translator error Rewrite LoadGuilds * Use extended repository * FIx guild window on member add * LoadGuild Changes * Update guild_base.cpp * Few small fixes for display issue with UF * Update guild_base.cpp * Update guild_members_repository.h * Update zoneserver.cpp * Update guild.cpp * Update entity.h * Switch formatting * Formatting * Update worldserver.cpp * Switch formatting * Formatting switch statement * Update guild.cpp * Formatting in guild_base * We don't need to validate m_db everywhere * More formatting / spacing issues * Switch format * Update guild_base.cpp * Fix an UF issue displaying incorrect guildtag as <> * Updated several constants, fixed a few issues with Ti/UF and guild tributes not being removed or sent when a member is removed/disbands from a guild. * Formatting and logging updates * Fix for Loadguilds and permissions after repo updates. * Cleanup unnecessary m_db checks * Updated logging to use player_event_logs * Updated to use the single opcodes for guild traffic for Ti/UF/RoF2. Several enhancements for guild functionality for more reusable code and readability. * Update to fix Demote Self and guild invites declining when option set to not accept guild invites * Potential fix for guild notes/tribute display issues when client has 'Show Offline' unchecked. * Updates to fox recent master changes Updates to fix recent master changes * Updates in response to comments * Further Updates in response to comments * Comment updates and refactor for SendAppearance functions * Comment updates * Update client spawn process for show guild name Add show guild tag to default spawn process * Update to use zone spawn packets for RoF2 Removed several unused functions as a result Updated MemberRankUpdate to properly update guild_show on rank change. Updated OP_GuildURLAndChannel opcode for UF/RoF2 * Cleanup of world changes Created function for repetitive zonelist sendpackets to only booted zones Re-Inserted accidental delete of scanclosemobs * Fixes * Further world cleanup * Fix a few test guild bank cases for backward compat Removed a duplicate db call Fixed a fallthrough issue * Update guild_mgr.cpp * Cleanup --------- Co-authored-by: Akkadius --- common/CMakeLists.txt | 6 + common/database/database_update_manifest.cpp | 65 +- common/database_schema.h | 1 + common/emu_oplist.h | 22 +- common/eq_packet_structs.h | 327 ++- common/events/player_event_logs.cpp | 2 + common/events/player_events.h | 36 +- common/guild_base.cpp | 1808 +++++++++-------- common/guild_base.h | 202 +- common/guilds.h | 69 +- common/inventory_profile.cpp | 11 +- common/item_instance.cpp | 10 + common/item_instance.h | 1 + common/patches/rof.cpp | 51 +- common/patches/rof2.cpp | 111 +- common/patches/rof2_ops.h | 2 + common/patches/rof2_structs.h | 41 +- common/patches/rof_structs.h | 10 +- common/patches/titanium.cpp | 414 +++- common/patches/titanium_ops.h | 8 + common/patches/titanium_structs.h | 47 +- common/patches/uf.cpp | 291 ++- common/patches/uf_ops.h | 7 + common/patches/uf_structs.h | 47 + .../base/base_guild_bank_repository.h | 404 ++++ .../base/base_guild_permissions_repository.h | 416 ++++ .../base/base_guild_tributes_repository.h | 453 +++++ .../base/base_guilds_repository.h | 1 + common/repositories/guild_bank_repository.h | 313 +-- .../repositories/guild_members_repository.h | 194 ++ .../guild_permissions_repository.h | 50 + common/repositories/guild_ranks_repository.h | 15 + .../repositories/guild_tributes_repository.h | 77 + common/repositories/guilds_repository.h | 84 +- common/ruletypes.h | 4 + common/servertalk.h | 96 +- common/version.h | 3 +- queryserv/lfguild.cpp | 34 +- utils/patches/patch_RoF2.conf | 35 +- utils/patches/patch_Titanium.conf | 28 +- utils/patches/patch_UF.conf | 29 +- world/client.cpp | 42 + world/client.h | 2 + world/cliententry.cpp | 3 + world/cliententry.h | 51 +- world/clientlist.cpp | 100 + world/clientlist.h | 5 +- world/eqemu_api_world_data_service.cpp | 62 +- world/eqemu_api_world_data_service.h | 1 + world/main.cpp | 2 + world/wguild_mgr.cpp | 324 ++- world/wguild_mgr.h | 13 +- world/world_boot.cpp | 1 + world/zonelist.cpp | 12 + world/zonelist.h | 1 + world/zoneserver.cpp | 178 +- zone/bonuses.cpp | 11 + zone/client.cpp | 25 +- zone/client.h | 31 +- zone/client_packet.cpp | 1311 ++++++++---- zone/client_packet.h | 9 +- zone/client_process.cpp | 13 + zone/entity.cpp | 11 +- zone/entity.h | 22 +- zone/exp.cpp | 5 + zone/gm_commands/guild.cpp | 206 +- zone/guild.cpp | 591 +++++- zone/guild_mgr.cpp | 1133 +++++++---- zone/guild_mgr.h | 24 +- zone/inventory.cpp | 9 +- zone/lua_packet.cpp | 8 +- zone/lua_packet.h | 1 + zone/main.cpp | 3 +- zone/queryserv.h | 2 + zone/spell_effects.cpp | 44 +- zone/string_ids.h | 3 + zone/tribute.cpp | 299 ++- zone/worldserver.cpp | 230 +++ zone/zone.cpp | 2 + zone/zonedb.cpp | 0 80 files changed, 8152 insertions(+), 2463 deletions(-) create mode 100644 common/repositories/base/base_guild_bank_repository.h create mode 100644 common/repositories/base/base_guild_permissions_repository.h create mode 100644 common/repositories/base/base_guild_tributes_repository.h create mode 100644 common/repositories/guild_members_repository.h create mode 100644 common/repositories/guild_permissions_repository.h create mode 100644 common/repositories/guild_tributes_repository.h mode change 100755 => 100644 zone/zonedb.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3c44cc573..a7ff13728 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -222,6 +222,9 @@ SET(repositories repositories/base/base_group_leaders_repository.h repositories/base/base_guilds_repository.h repositories/base/base_guild_ranks_repository.h + repositories/base/base_guild_permissions_repository.h + repositories/base/base_guild_members_repository.h + repositories/base/base_guild_bank_repository.h repositories/base/base_guild_relations_repository.h repositories/base/base_horses_repository.h repositories/base/base_instance_list_repository.h @@ -399,6 +402,9 @@ SET(repositories repositories/group_leaders_repository.h repositories/guilds_repository.h repositories/guild_ranks_repository.h + repositories/guild_permissions_repository.h + repositories/guild_members_repository.h + repositories/guild_bank_repository.h repositories/guild_relations_repository.h repositories/horses_repository.h repositories/instance_list_repository.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index a81d12cf9..84f576a74 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -4844,7 +4844,7 @@ UPDATE data_buckets SET bot_id = SUBSTRING_INDEX(SUBSTRING_INDEX( `key`, '-', 2 ADD COLUMN `marked_npc_3_zone_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_3_entity_id`, ADD COLUMN `marked_npc_3_instance_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_3_zone_id`; )" - }, + }, ManifestEntry{ .version = 9235, .description = "2023_07_31_character_stats_record.sql", @@ -5284,7 +5284,68 @@ ADD COLUMN `gm_exp` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `rez_time`, ADD COLUMN `killed_by` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `gm_exp`, ADD COLUMN `rezzable` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `killed_by`; )" - } + }, + ManifestEntry{ + .version = 9260, + .description = "2023_11_11_guild_features.sql", + .check = "SHOW TABLES LIKE 'guild_permissions'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `guild_permissions` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `perm_id` INT(11) NOT NULL DEFAULT '0', + `guild_id` INT(11) NOT NULL DEFAULT '0', + `permission` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `perm_id_guild_id` (`perm_id`, `guild_id`) USING BTREE +) +ENGINE=InnoDB +AUTO_INCREMENT=1; + +UPDATE guild_ranks SET title = 'Leader' WHERE `rank` = '1'; +UPDATE guild_ranks SET title = 'Senior Officer' WHERE `rank` = '2'; +UPDATE guild_ranks SET title = 'Officer' WHERE `rank` = '3'; +UPDATE guild_ranks SET title = 'Senior Member' WHERE `rank` = '4'; +UPDATE guild_ranks SET title = 'Member' WHERE `rank` = '5'; +UPDATE guild_ranks SET title = 'Junior Member' WHERE `rank` = '6'; +UPDATE guild_ranks SET title = 'Initiate' WHERE `rank` = '7'; +UPDATE guild_ranks SET title = 'Recruit' WHERE `rank` = '8'; + +DELETE FROM guild_ranks WHERE `rank` = 0; + +ALTER TABLE `guild_ranks` + DROP COLUMN `can_hear`, + DROP COLUMN `can_speak`, + DROP COLUMN `can_invite`, + DROP COLUMN `can_remove`, + DROP COLUMN `can_promote`, + DROP COLUMN `can_demote`, + DROP COLUMN `can_motd`, + DROP COLUMN `can_warpeace`; + +UPDATE guild_members SET `rank` = '5' WHERE `rank` = '0'; +UPDATE guild_members SET `rank` = '3' WHERE `rank` = '1'; +UPDATE guild_members SET `rank` = '1' WHERE `rank` = '2'; + +ALTER TABLE `guild_members` + ADD COLUMN `online` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `alt`; + +ALTER TABLE `guilds` + ADD COLUMN `favor` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `url`; + +CREATE TABLE guild_tributes ( + guild_id int(11) unsigned NOT NULL DEFAULT 0, + tribute_id_1 int(11) unsigned NOT NULL DEFAULT 0, + tribute_id_1_tier int(11) unsigned NOT NULL DEFAULT 0, + tribute_id_2 int(11) unsigned NOT NULL DEFAULT 0, + tribute_id_2_tier int(11) unsigned NOT NULL DEFAULT 0, + time_remaining int(11) unsigned NOT NULL DEFAULT 0, + enabled int(11) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (guild_id) USING BTREE +) ENGINE=InnoDB; +)" + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/database_schema.h b/common/database_schema.h index fdd158505..5b52a252a 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -148,6 +148,7 @@ namespace DatabaseSchema { "guild_members", "guild_ranks", "guild_relations", + "guild_tributes", "guilds", "instance_list_player", "inventory", diff --git a/common/emu_oplist.h b/common/emu_oplist.h index ffd8792ee..b7c30dc8c 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -228,6 +228,7 @@ N(OP_GuildBank), N(OP_GuildBankItemList), N(OP_GuildCreate), N(OP_GuildDelete), +N(OP_GuildDeleteGuild), N(OP_GuildDemote), N(OP_GuildInvite), N(OP_GuildInviteAccept), @@ -239,15 +240,33 @@ N(OP_GuildManageStatus), N(OP_GuildMemberLevelUpdate), N(OP_GuildMemberList), N(OP_GuildMemberUpdate), +N(OP_GuildMemberLevel), +N(OP_GuildMemberRankAltBanker), +N(OP_GuildMemberPublicNote), +N(OP_GuildMemberAdd), +N(OP_GuildMemberRename), +N(OP_GuildMemberDelete), +N(OP_GuildMemberDetails), +N(OP_GuildRenameGuild), N(OP_GuildMOTD), N(OP_GuildPeace), N(OP_GuildPromote), N(OP_GuildPublicNote), N(OP_GuildRemove), +N(OP_GuildSelectTribute), +N(OP_GuildModifyBenefits), +N(OP_GuildTributeToggleReq), +N(OP_GuildTributeToggleReply), +N(OP_GuildOptInOut), +N(OP_GuildSaveActiveTributes), +N(OP_GuildSendActiveTributes), +N(OP_GuildTributeFavorAndTimer), N(OP_GuildsList), N(OP_GuildStatus), N(OP_GuildTributeInfo), -N(OP_GuildUpdateURLAndChannel), +N(OP_GuildUpdate), +N(OP_GuildTributeDonateItem), +N(OP_GuildTributeDonatePlat), N(OP_GuildWar), N(OP_Heartbeat), N(OP_Hide), @@ -425,6 +444,7 @@ N(OP_ReqClientSpawn), N(OP_ReqNewZone), N(OP_RequestClientZoneChange), N(OP_RequestDuel), +N(OP_RequestGuildTributes), N(OP_RequestKnowledgeBase), N(OP_RequestTitles), N(OP_RespawnWindow), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index f651e937c..ede731167 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -314,6 +314,7 @@ union uint32 DestructibleUnk9; bool targetable_with_hotkey; bool show_name; + bool guild_show; }; struct PlayerState_Struct { @@ -1677,6 +1678,68 @@ struct GuildUpdate_Struct { GuildsListEntry_Struct entry; }; +struct GuildMemberAdd_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 unknown04; + /*008*/ uint32 unknown08; + /*012*/ uint32 unknown12; + /*016*/ uint32 level; + /*020*/ uint32 class_; + /*024*/ uint32 rank_; + /*028*/ uint32 guild_show; + /*032*/ uint32 zone_id; + /*036*/ uint32 last_on; + /*040*/ char player_name[64]; +}; + +struct GuildMemberLevel_Struct { + /*000*/ uint32 guild_id; + /*004*/ char player_name[64]; + /*068*/ uint32 level; +}; + +struct GuildMemberRank_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 rank_; + /*008*/ char player_name[64]; + /*072*/ uint32 alt_banker; //Banker/Alt bit 00 - none 10 - Alt 11 - Alt and Banker 01 - Banker. Banker not functional for RoF2+ + /*076*/ uint32 offline; +}; + +struct GuildMemberPublicNote_Struct { + /*000*/ uint32 guild_id; + /*004*/ char player_name[64]; + /*068*/ char public_note[256]; //RoF2 256 +}; + +struct GuildDelete_Struct { + /*000*/ uint32 guild_id; +}; + +struct GuildRenameGuild_Struct { + /*000*/ uint32 guild_id; + /*004*/ char new_guild_name[64]; +}; + +struct GuildRenameMember_Struct { + /*000*/ uint32 guild_id; + /*004*/ char player_name[64]; + /*068*/ char new_player_name[64]; +}; + +struct GuildMemberDetails_Struct { + /*000*/ uint32 guild_id; + /*004*/ char player_name[64]; + /*068*/ uint32 zone_id; + /*072*/ uint32 last_on; + /*076*/ uint32 offline_mode; //1 Offline +}; + +struct GuildMemberDelete_Struct { + /*000*/ uint32 guild_id; + /*004*/ char player_name[64]; +}; + /* ** Money Loot ** Length: 22 Bytes @@ -1725,10 +1788,10 @@ struct GuildJoin_Struct{ /*092*/ }; struct GuildInviteAccept_Struct { - char inviter[64]; - char newmember[64]; + char inviter[64]; + char new_member[64]; uint32 response; - uint32 guildeqid; + uint32 guild_id; }; struct GuildManageRemove_Struct { uint32 guildeqid; @@ -1760,6 +1823,14 @@ struct PopupResponse_Struct { /*0004*/ uint32 popupid; }; +enum GuildInformationActions +{ + GuildUpdateURL = 0, + GuildUpdateChannel = 1, + GuildUpdateRanks = 4, + GuildUpdatePermissions = 5 +}; + struct GuildManageBanker_Struct { uint32 unknown0; char myname[64]; @@ -1773,9 +1844,9 @@ struct GuildSetRank_Struct { /*00*/ uint32 Unknown00; /*04*/ uint32 Unknown04; -/*08*/ uint32 Rank; -/*12*/ char MemberName[64]; -/*76*/ uint32 Banker; +/*08*/ uint32 rank; +/*12*/ char member_name[64]; +/*76*/ uint32 banker; /*80*/ }; @@ -3333,6 +3404,7 @@ struct Internal_GuildMemberEntry_Struct { // char public_note[1]; //variable length. uint16 zoneinstance; //network byte order uint16 zone_id; //network byte order + uint32 online; }; struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. @@ -3359,7 +3431,42 @@ struct GuildUpdate_PublicNote{ uint32 unknown0; char name[64]; char target[64]; - char note[1]; //variable length. + char note[256]; +}; + +struct GuildUpdateURLAndChannelStruct { + char text[512]; +}; + +struct GuildUpdatePermissionsStruct { + uint32 rank; // the rank that is being changed + uint32 function_id; // the id of the guild function + uint32 value; // 1 is on, 0 is off + +}; + +struct GuildUpdateRankNamesStruct { + uint32 rank; // the rank that is being updated + char rank_name[76]; // the rank name +}; + +struct GuildUpdateUCPStruct { + uint32 action; // 0 and 1 use url and channel payload. 5 uses permissions payload + char unknown[76]; + union { + GuildUpdateURLAndChannelStruct url_channel; + GuildUpdatePermissionsStruct permissions; + GuildUpdateRankNamesStruct rank_name; + }payload; +}; + +struct GuildPermission_Struct +{ + uint32 Action; // 5 = Update function permission + char Unknown0004[76]; // not used + uint32 rank; // the rank that is being changed + uint32 function_id; // the id of the guild function + uint32 value; // 1 is on, 0 is off }; struct GuildUpdateURLAndChannel_Struct @@ -3378,7 +3485,7 @@ struct GuildUpdateURLAndChannel_Struct //The client sends this struct on changing a guild rank. The server sends each rank in 32 or less packets upon zonein if you are in a guild. struct GuildUpdateRanks_Struct { -/*0000*/ uint32 Action; // 0 = Update URL, 1 = Update Channel, 5 = RoF Ranks +/*0000*/ uint32 Action; // 0 = Update URL, 1 = Update Channel, 4 = Ranks 5 = Permissions /*0004*/ uint32 Unknown0004; //Seen 00 00 00 00 /*0008*/ uint32 Unknown0008; //Seen 96 29 00 00 /*0008*/ char Unknown0012[64]; //Seen "CharacterName" @@ -3400,6 +3507,7 @@ struct GuildStatus_Struct struct GuildDemoteStruct{ char name[64]; char target[64]; + uint32 rank; }; struct GuildRemoveStruct{ @@ -3409,9 +3517,9 @@ struct GuildRemoveStruct{ uint32 leaderstatus; //? }; -struct GuildMakeLeader{ - char name[64]; - char target[64]; +struct GuildMakeLeader_Struct{ + char requestor[64]; + char new_leader[64]; }; struct BugReport_Struct { @@ -3505,66 +3613,193 @@ struct ZoneInSendName_Struct2 { static const uint32 MAX_TRIBUTE_TIERS = 10; struct StartTribute_Struct { - uint32 client_id; - uint32 tribute_master_id; - uint32 response; + uint32 client_id; + uint32 tribute_master_id; + uint32 response; }; struct TributeLevel_Struct { - uint32 level; //backwards byte order! - uint32 tribute_item_id; //backwards byte order! - uint32 cost; //backwards byte order! + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! }; struct TributeAbility_Struct { - uint32 tribute_id; //backwards byte order! - uint32 tier_count; //backwards byte order! + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; - char name[0]; -}; - -struct GuildTributeAbility_Struct { - uint32 guild_id; - TributeAbility_Struct ability; + char name[0]; }; struct SelectTributeReq_Struct { - uint32 client_id; //? maybe action ID? - uint32 tribute_id; - uint32 unknown8; //seen E3 00 00 00 + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct GuildTributeAbilityDetail_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + uint32 unknown132; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + GuildTributeAbilityDetail_Struct ability; +}; + +struct GuildTributeSelectReq_Struct { + uint32 tribute_id; + uint32 tier; + uint32 tribute_id2; + uint32 unknown12; //seen A7 01 00 00 +}; + +struct GuildTributeSelectReply_Struct { + uint32 tribute_id; + uint32 tier; + uint32 tribute_id2; + char description; +}; + +struct GuildTributeModifyBenefits_Struct { +/*000*/uint32 command; +/*004*/uint32 data; +/*008*/char unknown8[12]; +/*020*/uint32 tribute_master_id; +/*024*/uint32 tribute_id_1; +/*028*/uint32 tribute_id_2; +/*032*/uint32 tribute_id_1_tier; +/*036*/uint32 tribute_id_2_tier; +/*040*/char unknown[40]; +}; + +struct GuildTributeOptInOutReq_Struct { +/*000*/uint32 guild_id; +/*004*/uint32 tribute_toggle; +/*008*/char player[64]; +/*072*/uint32 command; +/*076*/uint32 tribute_master_id; +}; + +struct GuildTributeOptInOutReply_Struct { +/*000*/uint32 guild_id; +/*004*/char player_name[64]; +/*068*/uint32 tribute_toggle;// 0 off 1 on +/*072*/uint32 tribute_trophy_toggle;// 0 off 1 on not yet implemented +/*076*/uint32 no_donations; +/*080*/uint32 time; +/*084*/uint32 command; +}; + +struct GuildTributeSaveActive_Struct { +/*000*/ uint32 command; +/*004*/ char unknown04[16]; +/*020*/ uint32 master_tribute_id; +/*024*/ uint32 tribute_id_1; +/*028*/ uint32 tribute_id_2; +/*032*/ uint32 tribute_1_tier; +/*036*/ uint32 tribute_2_tier; +/*040*/ char unknown40[8]; +}; + +struct GuildTributeFavorTimer_Struct { +/*000*/ uint32 guild_id; +/*004*/ uint32 guild_favor; +/*008*/ uint32 tribute_timer; +/*012*/ uint32 trophy_timer; +}; + +struct GuildTributeSendActive_Struct { +/*000*/ uint32 not_used; +/*004*/ uint32 guild_favor; +/*008*/ uint32 tribute_timer; +/*012*/ uint32 tribute_enabled; +/*016*/ char unknown16[8]; +/*024*/ uint32 tribute_id_1; +/*028*/ uint32 tribute_id_2; +/*032*/ uint32 tribute_id_1_tier; +/*036*/ uint32 tribute_id_2_tier; +}; + +struct GuildTributeToggleReq_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown4; +/*008*/ uint32 unknown8; +}; + +struct GuildTributeDonateItemRequest_Struct { +/*000*/ uint32 type; +/*004*/ uint16 slot; +/*006*/ uint16 sub_index; +/*008*/ uint16 aug_index; +/*010*/ uint16 unknown10; +/*012*/ uint32 quantity; +/*016*/ uint32 tribute_master_id; +/*020*/ uint32 unknown20; +/*024*/ uint32 guild_id; +/*028*/ uint32 unknown28; +/*032*/ uint32 unknown32; +}; + +struct GuildTributeDonateItemReply_Struct { +/*000*/ uint32 type; +/*004*/ uint16 slot; +/*006*/ uint16 sub_index; +/*008*/ uint16 aug_index; +/*010*/ uint16 unknown10; +/*012*/ uint32 quantity; +/*016*/ uint32 unknown20; +/*020*/ uint32 favor; +}; + +struct GuildTributeDonatePlatRequest_Struct { +/*000*/ uint32 quantity; +/*004*/ uint32 tribute_master_id; +/*008*/ uint32 unknown08; +/*012*/ uint32 guild_id; +/*016*/ uint32 unknown16; +}; + +struct GuildTributeDonatePlatReply_Struct { + /*000*/ uint32 quantity; + /*004*/ uint32 unknown4; + /*008*/ uint32 favor; }; struct SelectTributeReply_Struct { - uint32 client_id; //echoed from request. - uint32 tribute_id; - char desc[0]; + uint32 client_id; //echoed from request. + uint32 tribute_id; + char description[0]; }; struct TributeInfo_Struct { - uint32 active; //0 == inactive, 1 == active - uint32 tributes[EQ::invtype::TRIBUTE_SIZE]; //-1 == NONE - uint32 tiers[EQ::invtype::TRIBUTE_SIZE]; //all 00's - uint32 tribute_master_id; + uint32 active; //0 == inactive, 1 == active + uint32 tributes[EQ::invtype::TRIBUTE_SIZE]; //-1 == NONE + uint32 tiers[EQ::invtype::TRIBUTE_SIZE]; //all 00's + uint32 tribute_master_id; }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { - int32 tribute_points; - uint32 unknown04; - int32 career_tribute_points; - uint32 unknown12; + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; }; struct TributeMoney_Struct { - uint32 platinum; - uint32 tribute_master_id; - int32 tribute_points; + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; }; diff --git a/common/events/player_event_logs.cpp b/common/events/player_event_logs.cpp index a0b57f0d8..557b5830a 100644 --- a/common/events/player_event_logs.cpp +++ b/common/events/player_event_logs.cpp @@ -697,6 +697,8 @@ void PlayerEventLogs::SetSettingsDefaults() m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1; m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1; m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1; + m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM].event_enabled = 1; + m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_PLAT].event_enabled = 1; for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) { m_settings[i].retention_days = RETENTION_DAYS_DEFAULT; diff --git a/common/events/player_events.h b/common/events/player_events.h index bec940473..def80a0ec 100644 --- a/common/events/player_events.h +++ b/common/events/player_events.h @@ -56,6 +56,8 @@ namespace PlayerEvent { KILLED_NAMED_NPC, KILLED_RAID_NPC, ITEM_CREATION, + GUILD_TRIBUTE_DONATE_ITEM, + GUILD_TRIBUTE_DONATE_PLAT, MAX // dont remove }; @@ -112,7 +114,9 @@ namespace PlayerEvent { "Killed NPC", "Killed Named NPC", "Killed Raid NPC", - "Item Creation" + "Item Creation", + "Guild Tribute Donate Item", + "Guild Tribute Donate Platinum" }; // Generic struct used by all events @@ -942,6 +946,36 @@ namespace PlayerEvent { ); } }; + + struct GuildTributeDonateItem { + uint32 item_id; + uint32 guild_favor; + + // cereal + template + void serialize(Archive &ar) + { + ar( + CEREAL_NVP(item_id), + CEREAL_NVP(guild_favor) + ); + } + }; + + struct GuildTributeDonatePlat { + uint32 plat; + uint32 guild_favor; + + // cereal + template + void serialize(Archive &ar) + { + ar( + CEREAL_NVP(plat), + CEREAL_NVP(guild_favor) + ); + } + }; } #endif //EQEMU_PLAYER_EVENTS_H diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 1278d48ac..96fab657b 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -1,23 +1,31 @@ /* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net) + Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net) - 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; version 2 of the License. + 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; version 2 of the License. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + 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, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "guild_base.h" #include "database.h" +#include "../common/rulesys.h" +#include "../common/repositories/guilds_repository.h" +#include "../common/repositories/guild_ranks_repository.h" +#include "../common/repositories/guild_permissions_repository.h" +#include "../common/repositories/guild_members_repository.h" +#include "../common/repositories/guild_bank_repository.h" +#include "../common/repositories/guild_tributes_repository.h" + //#include "misc_functions.h" #include "strings.h" @@ -27,882 +35,865 @@ //until we move MAX_NUMBER_GUILDS #include "eq_packet_structs.h" -const char *const BaseGuildManager::GuildActionNames[_MaxGuildAction] = -{ "HearGuildChat", "SpeakGuildChat", "Invite", "Remove", "Promote", "Demote", "Set_MOTD", "War/Peace" }; +std::vector default_permissions = { + {GUILD_ACTION_BANK_CHANGE_ITEM_PERMISSIONS, 128}, + {GUILD_ACTION_BANK_DEPOSIT_ITEMS, 248}, + {GUILD_ACTION_BANK_PROMOTE_ITEMS, 128}, + {GUILD_ACTION_BANK_VIEW_ITEMS, 248}, + {GUILD_ACTION_BANK_WITHDRAW_ITEMS, 224}, + {GUILD_ACTION_BANNER_CHANGE, 224}, + {GUILD_ACTION_BANNER_PLANT, 224}, + {GUILD_ACTION_BANNER_REMOVE, 224}, + {GUILD_ACTION_CHANGE_THE_MOTD, 224}, + {GUILD_ACTION_DISPLAY_GUILD_NAME, 255}, + {GUILD_ACTION_EDIT_PUBLIC_NOTES, 224}, + {GUILD_ACTION_EDIT_RECRUITING_SETTINGS, 224}, + {GUILD_ACTION_GUILD_CHAT_SEE, 255}, + {GUILD_ACTION_GUILD_CHAT_SPEAK_IN, 255}, + {GUILD_ACTION_MEMBERS_CHANGE_ALT_FLAG_FOR_OTHER, 224}, + {GUILD_ACTION_MEMBERS_DEMOTE, 128}, + {GUILD_ACTION_MEMBERS_DEMOTE_SELF, 224}, + {GUILD_ACTION_MEMBERS_INVITE, 224}, + {GUILD_ACTION_MEMBERS_PROMOTE, 224}, + {GUILD_ACTION_MEMBERS_REMOVE, 128}, + {GUILD_ACTION_RANKS_CHANGE_PERMISSIONS, 128}, + {GUILD_ACTION_RANKS_CHANGE_RANK_NAMES, 128}, + {GUILD_ACTION_REAL_ESTATE_GUILD_PLOT_BUY, 128}, + {GUILD_ACTION_REAL_ESTATE_GUILD_PLOT_SELL, 128}, + {GUILD_ACTION_REAL_ESTATE_MODIFY_TROPHIES, 224}, + {GUILD_ACTION_SEND_THE_WHOLE_GUILD_E_MAIL, 224}, + {GUILD_ACTION_TRIBUTE_CHANGE_ACTIVE_BENEFIT, 224}, + {GUILD_ACTION_TRIBUTE_CHANGE_FOR_OTHERS, 224}, + {GUILD_ACTION_TROPHY_TRIBUTE_CHANGE_ACTIVE_BENEFIT, 224}, + {GUILD_ACTION_TROPHY_TRIBUTE_CHANGE_FOR_OTHERS, 224} +}; + +std::vector default_rank_names = { + {1, "Leader"}, + {2, "Senior Officer"}, + {3, "Officer"}, + {4, "Senior Member"}, + {5, "Member"}, + {6, "Junior Member"}, + {7, "Initiate"}, + {8, "Recruit"} +}; BaseGuildManager::BaseGuildManager() -: m_db(nullptr) + : m_db(nullptr) { } -BaseGuildManager::~BaseGuildManager() { +BaseGuildManager::~BaseGuildManager() +{ ClearGuilds(); } - - -bool BaseGuildManager::LoadGuilds() { - +bool BaseGuildManager::LoadGuilds() +{ ClearGuilds(); + auto guilds = GuildsRepository::All(*m_db); + auto guilds_ranks = GuildRanksRepository::All(*m_db); + auto guilds_permissions = GuildPermissionsRepository::All(*m_db); + auto guilds_tributes = GuildTributesRepository::All(*m_db); - if(m_db == nullptr) { - LogGuilds("Requested to load guilds when we have no database object"); - return(false); - } - - std::string query("SELECT id, name, leader, minstatus, motd, motd_setter,channel,url FROM guilds"); - std::map::iterator res; - - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { + if (guilds.empty()) { + LogGuilds("No Guilds found in database."); return false; } - for (auto row=results.begin();row!=results.end();++row) - _CreateGuild(Strings::ToUnsignedInt(row[0]), row[1], Strings::ToUnsignedInt(row[2]), Strings::ToUnsignedInt(row[3]), row[4], row[5], row[6], row[7]); + LogGuilds("Found {} guilds. Loading.....", guilds.size()); + for (auto &g: guilds) { + bool store_to_db = false; - LogInfo("Loaded [{}] Guilds", Strings::Commify(std::to_string(results.RowCount()))); + _CreateGuild(g.id, g.name, g.leader, g.minstatus, g.motd, g.motd_setter, g.channel, g.url, g.favor); - query = "SELECT guild_id,`rank`,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace FROM guild_ranks"; - results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - return false; - } - - for (auto row=results.begin();row!=results.end();++row) - { - uint32 guild_id = Strings::ToUnsignedInt(row[0]); - uint8 rankn = Strings::ToUnsignedInt(row[1]); - - if(rankn > GUILD_MAX_RANK) { - LogGuilds("Found invalid (too high) rank [{}] for guild [{}], skipping", rankn, guild_id); - continue; + for (auto const &r: guilds_ranks) { + if (r.guild_id == g.id) { + m_guilds[g.id]->rank_names[r.rank_] = r.title; + } } - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { - LogGuilds("Found rank [{}] for non-existent guild [{}], skipping", rankn, guild_id); - continue; + auto count = 0; + + for (auto const &p: guilds_permissions) { + if (p.guild_id == g.id) { + m_guilds[g.id]->functions[p.perm_id].id = p.id; + m_guilds[g.id]->functions[p.perm_id].guild_id = p.guild_id; + m_guilds[g.id]->functions[p.perm_id].perm_id = p.perm_id; + m_guilds[g.id]->functions[p.perm_id].perm_value = p.permission; + count++; + } } - RankInfo &rank = res->second->ranks[rankn]; + if (count < GUILD_MAX_FUNCTIONS) { + store_to_db = true; + } - rank.name = row[2]; - rank.permissions[GUILD_HEAR] = (row[3][0] == '1')?true:false; - rank.permissions[GUILD_SPEAK] = (row[4][0] == '1')?true:false; - rank.permissions[GUILD_INVITE] = (row[5][0] == '1')?true:false; - rank.permissions[GUILD_REMOVE] = (row[6][0] == '1')?true:false; - rank.permissions[GUILD_PROMOTE] = (row[7][0] == '1')?true:false; - rank.permissions[GUILD_DEMOTE] = (row[8][0] == '1')?true:false; - rank.permissions[GUILD_MOTD] = (row[9][0] == '1')?true:false; - rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1')?true:false; + LogGuilds("Loaded guild id [{}]", g.id); + + if (store_to_db) { + LogGuilds("Found missing permissions for guild id [{}]. Setting missing to default values.", g.id); + _StoreGuildDB(g.id); + store_to_db = false; + } + + for (auto const >: guilds_tributes) { + if (gt.guild_id == g.id) { + m_guilds[g.id]->tribute.id_1 = gt.tribute_id_1; + m_guilds[g.id]->tribute.id_2 = gt.tribute_id_2; + m_guilds[g.id]->tribute.id_1_tier = gt.tribute_id_1_tier; + m_guilds[g.id]->tribute.id_2_tier = gt.tribute_id_2_tier; + m_guilds[g.id]->tribute.enabled = gt.enabled; + if (gt.time_remaining > RuleI(Guild, TributeTime) || gt.time_remaining <= 0) { + m_guilds[g.id]->tribute.time_remaining = RuleI(Guild, TributeTime); + } + m_guilds[g.id]->tribute.time_remaining = gt.time_remaining; + LogGuilds( + "Timer has [{}] time remaining from the load function.", + m_guilds[g.id]->tribute.time_remaining + ); + } + } } + LogGuilds("Completed loading {} guilds.", guilds.size()); return true; } -bool BaseGuildManager::RefreshGuild(uint32 guild_id) { - if(m_db == nullptr) { - LogGuilds("Requested to refresh guild [{}] when we have no database object", guild_id); - return(false); - } - - std::string query = StringFormat("SELECT name, leader, minstatus, motd, motd_setter, channel,url FROM guilds WHERE id=%lu", (unsigned long)guild_id); - std::map::iterator res; - GuildInfo *info; - - // load up all the guilds - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { +bool BaseGuildManager::RefreshGuild(uint32 guild_id) +{ + if (guild_id <= 0) { + LogError("Requested to refresh guild id [{}] but id must be greater than 0.", guild_id); return false; } - if (results.RowCount() == 0) - { - LogGuilds("Unable to find guild [{}] in the database", guild_id); + auto db_guild = GuildsRepository::FindOne(*m_db, guild_id); + if (!db_guild.id) { + LogGuilds("Guild ID [{}] not found in database.", db_guild.id); return false; } - auto row = results.begin(); + LogGuilds("Found guild id [{}]. Loading details.....", db_guild.id); + _CreateGuild( + db_guild.id, + db_guild.name, + db_guild.leader, + db_guild.minstatus, + db_guild.motd, + db_guild.motd_setter, + db_guild.channel, + db_guild.url, + db_guild.favor + ); - info = _CreateGuild(guild_id, row[0], Strings::ToUnsignedInt(row[1]), Strings::ToUnsignedInt(row[2]), row[3], row[4], row[5], row[6]); + auto guild = GetGuildByGuildID(guild_id); + auto where_filter = fmt::format("guild_id = '{}'", guild_id); + auto guild_ranks = GuildRanksRepository::GetWhere(*m_db, where_filter); - query = StringFormat("SELECT guild_id, `rank`, title, can_hear, can_speak, can_invite, can_remove, can_promote, can_demote, can_motd, can_warpeace " - "FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); - results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - return false; + for (auto const &r: guild_ranks) { + guild->rank_names[r.rank_] = r.title; } - for (auto row=results.begin();row!=results.end();++row) - { - uint8 rankn = Strings::ToUnsignedInt(row[1]); - - if(rankn > GUILD_MAX_RANK) { - LogGuilds("Found invalid (too high) rank [{}] for guild [{}], skipping", rankn, guild_id); - continue; - } - - RankInfo &rank = info->ranks[rankn]; - - rank.name = row[2]; - rank.permissions[GUILD_HEAR] = (row[3][0] == '1') ? true: false; - rank.permissions[GUILD_SPEAK] = (row[4][0] == '1') ? true: false; - rank.permissions[GUILD_INVITE] = (row[5][0] == '1') ? true: false; - rank.permissions[GUILD_REMOVE] = (row[6][0] == '1') ? true: false; - rank.permissions[GUILD_PROMOTE] = (row[7][0] == '1') ? true: false; - rank.permissions[GUILD_DEMOTE] = (row[8][0] == '1') ? true: false; - rank.permissions[GUILD_MOTD] = (row[9][0] == '1') ? true: false; - rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1') ? true: false; + where_filter = fmt::format("guild_id = '{}'", guild_id); + auto guild_permissions = GuildPermissionsRepository::GetWhere(*m_db, where_filter); + for (auto const &p: guild_permissions) { + guild->functions[p.perm_id].id = p.id; + guild->functions[p.perm_id].guild_id = p.guild_id; + guild->functions[p.perm_id].perm_id = p.perm_id; + guild->functions[p.perm_id].perm_value = p.permission; } - LogGuilds("Successfully refreshed guild [{}] from the database", guild_id); + auto guild_tributes = GuildTributesRepository::FindOne(*m_db, guild_id); + if (guild_tributes.guild_id) { + guild->tribute.id_1 = guild_tributes.tribute_id_1; + guild->tribute.id_2 = guild_tributes.tribute_id_2; + guild->tribute.id_1_tier = guild_tributes.tribute_id_1_tier; + guild->tribute.id_2_tier = guild_tributes.tribute_id_2_tier; + guild->tribute.enabled = guild_tributes.enabled; + } + + LogGuilds("Successfully refreshed guild id [{}] from the database", guild_id); + LogGuilds("Timer has [{}] time remaining from the refresh.", guild->tribute.time_remaining); return true; } -BaseGuildManager::GuildInfo *BaseGuildManager::_CreateGuild(uint32 guild_id, const char *guild_name, uint32 leader_char_id, uint8 minstatus, const char *guild_motd, const char *motd_setter, const char *Channel, const char *URL) +BaseGuildManager::GuildInfo *BaseGuildManager::_CreateGuild( + uint32 guild_id, + std::string guild_name, + uint32 leader_char_id, + uint8 minstatus, + std::string guild_motd, + std::string motd_setter, + std::string Channel, + std::string URL, + uint32 favor +) { std::map::iterator res; //remove any old entry. res = m_guilds.find(guild_id); - if(res != m_guilds.end()) { + if (res != m_guilds.end()) { delete res->second; m_guilds.erase(res); } //make the new entry and store it into the map. auto info = new GuildInfo; - info->name = guild_name; - info->leader_char_id = leader_char_id; - info->minstatus = minstatus; - info->motd = guild_motd; - info->motd_setter = motd_setter; - info->url = URL; - info->channel = Channel; + info->name = std::move(guild_name); + info->motd = std::move(guild_motd); + info->motd_setter = std::move(motd_setter); + info->url = std::move(URL); + info->channel = std::move(Channel); + info->leader = leader_char_id; + info->minstatus = minstatus; + info->tribute.favor = favor; + + for (auto r: default_rank_names) { + info->rank_names[r.id] = r.name; + } + + for (auto p: default_permissions) { + info->functions[p.id].id = 0; + info->functions[p.id].guild_id = guild_id; + info->functions[p.id].perm_id = p.id; + info->functions[p.id].perm_value = p.value; + } + + info->tribute.id_1 = 0xffffffff; + info->tribute.id_2 = 0xffffffff; + info->tribute.id_1_tier = 0; + info->tribute.id_2_tier = 0; + info->tribute.enabled = 0; + info->tribute.time_remaining = RuleI(Guild, TributeTime); + m_guilds[guild_id] = info; - //now setup default ranks (everything defaults to false) - info->ranks[0].name = "Member"; - info->ranks[0].permissions[GUILD_HEAR] = true; - info->ranks[0].permissions[GUILD_SPEAK] = true; - info->ranks[1].name = "Officer"; - info->ranks[1].permissions[GUILD_HEAR] = true; - info->ranks[1].permissions[GUILD_SPEAK] = true; - info->ranks[1].permissions[GUILD_INVITE] = true; - info->ranks[1].permissions[GUILD_REMOVE] = true; - info->ranks[1].permissions[GUILD_MOTD] = true; - info->ranks[2].name = "Leader"; - info->ranks[2].permissions[GUILD_HEAR] = true; - info->ranks[2].permissions[GUILD_SPEAK] = true; - info->ranks[2].permissions[GUILD_INVITE] = true; - info->ranks[2].permissions[GUILD_REMOVE] = true; - info->ranks[2].permissions[GUILD_PROMOTE] = true; - info->ranks[2].permissions[GUILD_DEMOTE] = true; - info->ranks[2].permissions[GUILD_MOTD] = true; - info->ranks[2].permissions[GUILD_WARPEACE] = true; - - return(info); + return info; } -bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { - if(m_db == nullptr) { - LogGuilds("Requested to store guild [{}] when we have no database object", guild_id); - return(false); - } - - std::map::const_iterator res; - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { - LogGuilds("Requested to store non-existent guild [{}]", guild_id); - return(false); - } - GuildInfo *info = res->second; - - std::string query = StringFormat("DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id); - - //clear out old `guilds` entry - auto results = m_db->QueryDatabase(query); - - //clear out old `guild_ranks` entries - query = StringFormat("DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); - results = m_db->QueryDatabase(query); - - //escape our strings. - auto name_esc = new char[info->name.length() * 2 + 1]; - auto motd_esc = new char[info->motd.length() * 2 + 1]; - auto motd_set_esc = new char[info->motd_setter.length() * 2 + 1]; - m_db->DoEscapeString(name_esc, info->name.c_str(), info->name.length()); - m_db->DoEscapeString(motd_esc, info->motd.c_str(), info->motd.length()); - m_db->DoEscapeString(motd_set_esc, info->motd_setter.c_str(), info->motd_setter.length()); - - //insert the new `guilds` entry - query = StringFormat("INSERT INTO guilds (id,name,leader,minstatus,motd,motd_setter) VALUES(%lu,'%s',%lu,%d,'%s', '%s')", - (unsigned long)guild_id, name_esc, (unsigned long)info->leader_char_id, info->minstatus, motd_esc, motd_set_esc); - results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - safe_delete_array(name_esc); - safe_delete_array(motd_esc); - safe_delete_array(motd_set_esc); +bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) +{ + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to save guild id [{}] to the database however guild could not be found.", guild_id); return false; } - safe_delete_array(name_esc); - safe_delete_array(motd_esc); - safe_delete_array(motd_set_esc); - //now insert the new ranks - uint8 rank; - for(rank = 0; rank <= GUILD_MAX_RANK; rank++) { - const RankInfo &rankInfo = info->ranks[rank]; - - auto title_esc = new char[rankInfo.name.length() * 2 + 1]; - m_db->DoEscapeString(title_esc, rankInfo.name.c_str(), rankInfo.name.length()); - - query = StringFormat("INSERT INTO guild_ranks " - "(guild_id,`rank`,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace)" - " VALUES(%d,%d,'%s',%d,%d,%d,%d,%d,%d,%d,%d)", - guild_id, rank, title_esc, - rankInfo.permissions[GUILD_HEAR], - rankInfo.permissions[GUILD_SPEAK], - rankInfo.permissions[GUILD_INVITE], - rankInfo.permissions[GUILD_REMOVE], - rankInfo.permissions[GUILD_PROMOTE], - rankInfo.permissions[GUILD_DEMOTE], - rankInfo.permissions[GUILD_MOTD], - rankInfo.permissions[GUILD_WARPEACE]); - results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - safe_delete_array(title_esc); + { + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); + GuildsRepository::DeleteOne(*m_db, guild_id); + auto result = GuildsRepository::InsertOne(*m_db, out); + if (!result.id) { + LogGuilds("Error storing guild [{}] details in the database", guild_id); return false; } - safe_delete_array(title_esc); + + LogGuilds("Stored guild [{}] details in the database", guild_id); } - LogGuilds("Stored guild [{}] in the database", guild_id); + { + std::vector out; + GuildRanksRepository::GuildRanks gr; + for (int i = GUILD_LEADER; i <= GUILD_MAX_RANK; i++) { + gr.guild_id = guild_id; + gr.rank_ = i; + gr.title = in->rank_names[i]; + out.push_back(gr); + } + if (!GuildRanksRepository::ReplaceMany(*m_db, out)) { + LogGuilds("Error storing guild [{}] ranks in the database", guild_id); + return false; + } + LogGuilds("Stored guild [{}] ranks in the database", guild_id); + } + + { + std::vector out; + GuildPermissionsRepository::GuildPermissions gp; + for (int i = 1; i <= GUILD_MAX_FUNCTIONS; i++) { + gp.id = in->functions[i].id; + gp.guild_id = in->functions[i].guild_id; + gp.perm_id = in->functions[i].perm_id; + gp.permission = in->functions[i].perm_value; + out.push_back(gp); + } + + if (!GuildPermissionsRepository::ReplaceMany(*m_db, out)) { + LogGuilds("Error storing guild [{}] permissions in the database", guild_id); + return false; + } + LogGuilds("Stored guild [{}] permissions in the database", guild_id); + + GuildTributesRepository::GuildTributes gt{}; + gt.tribute_id_1 = in->tribute.id_1; + gt.tribute_id_2 = in->tribute.id_2; + gt.tribute_id_1_tier = in->tribute.id_1_tier; + gt.tribute_id_2_tier = in->tribute.id_2_tier; + gt.enabled = in->tribute.enabled; + gt.time_remaining = in->tribute.time_remaining; + GuildTributesRepository::ReplaceOne(*m_db, gt); + } + + LogGuilds("Stored guild [{}] in the database successfully.", guild_id); return true; } -uint32 BaseGuildManager::_GetFreeGuildID() { - if(m_db == nullptr) { - LogGuilds("Requested find a free guild ID when we have no database object"); - return(GUILD_NONE); +uint32 BaseGuildManager::_GetFreeGuildID() +{ + GuildsRepository::DeleteWhere(*m_db, "`name` = ''"); + + GuildsRepository::Guilds out; + out.id = 0; + out.leader = 0; + out.minstatus = 0; + out.tribute = 0; + out.name = ""; + out.motd = ""; + out.motd_setter = ""; + out.url = ""; + out.channel = ""; + auto last_insert_id = GuildsRepository::InsertOne(*m_db, out); + if (last_insert_id.id > 0) { + LogGuilds("Located a free guild ID [{}] in the database", last_insert_id.id); + return last_insert_id.id; } - std::string query; - //this has got to be one of the more retarded things I have seen. - //none the less, im too lazy to rewrite it right now. - //possibly: - // - // SELECT t1.id + 1 - // FROM guilds t1 - // WHERE NOT EXISTS ( - // SELECT * - // FROM guilds t2 - // WHERE t2.id = t1.id + 1 - // ) - // LIMIT 1 - // - // Seems likely what we should be doing is auto incrementing the guild table - // inserting, then getting the id. NOT getting a free id then inserting. - // could be a race condition. - - for (auto index = 1; index < MAX_NUMBER_GUILDS; ++index) - { - query = StringFormat("SELECT id FROM guilds where id=%i;", index); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - continue; - } - - if (results.RowCount() == 0) - { - LogGuilds("Located free guild ID [{}] in the database", index); - return index; - } - } - - LogGuilds("Unable to find a free guild ID when requested"); + LogGuilds("Unable to find a free guild ID in the database"); return GUILD_NONE; } +uint32 BaseGuildManager::CreateGuild(std::string name, uint32 leader_char_id) +{ + uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id); + if (guild_id == GUILD_NONE) { + return (GUILD_NONE); + } + //RefreshGuild(guild_id); + //SendGuildRefresh(guild_id, true, false, false, false); + //SendCharRefresh(GUILD_NONE, guild_id, leader_char_id); - -uint32 BaseGuildManager::CreateGuild(const char* name, uint32 leader_char_id) { - uint32 gid = DBCreateGuild(name, leader_char_id); - if(gid == GUILD_NONE) - return(GUILD_NONE); - - SendGuildRefresh(gid, true, false, false, false); - SendCharRefresh(GUILD_NONE, gid, leader_char_id); - - return(gid); + return guild_id; } -bool BaseGuildManager::DeleteGuild(uint32 guild_id) { - if(!DBDeleteGuild(guild_id)) - return(false); +bool BaseGuildManager::DeleteGuild(uint32 guild_id) +{ + if (!UpdateDbDeleteGuild(guild_id)) { + return false; + } SendGuildDelete(guild_id); - - return(true); + return true; } -bool BaseGuildManager::RenameGuild(uint32 guild_id, const char* name) { - if(!DBRenameGuild(guild_id, name)) - return(false); +bool BaseGuildManager::RenameGuild(uint32 guild_id, std::string name) +{ + if (!UpdateDbRenameGuild(guild_id, name)) { + return false; + } SendGuildRefresh(guild_id, true, false, false, false); - return(true); + return (true); } -bool BaseGuildManager::SetGuildLeader(uint32 guild_id, uint32 leader_char_id) { +bool BaseGuildManager::SetGuildLeader(uint32 guild_id, uint32 leader_char_id) +{ //get old leader first. std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(false); - GuildInfo *info = res->second; - uint32 old_leader = info->leader_char_id; + if (res == m_guilds.end()) { + return (false); + } + GuildInfo *info = res->second; + uint32 old_leader = info->leader; - if(!DBSetGuildLeader(guild_id, leader_char_id)) - return(false); + if (!UpdateDbGuildLeader(guild_id, leader_char_id)) { + return (false); + } SendGuildRefresh(guild_id, false, false, false, false); SendCharRefresh(GUILD_NONE, guild_id, old_leader); SendCharRefresh(GUILD_NONE, guild_id, leader_char_id); - return(true); + return (true); } -bool BaseGuildManager::SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { - if(!DBSetGuildMOTD(guild_id, motd, setter)) - return(false); +bool BaseGuildManager::SetGuildMOTD(uint32 guild_id, std::string motd, std::string setter) +{ + if (!UpdateDbGuildMOTD(guild_id, motd, setter)) { + return false; + } SendGuildRefresh(guild_id, false, true, false, false); - return(true); + return (true); } -bool BaseGuildManager::SetGuildURL(uint32 GuildID, const char* URL) +bool BaseGuildManager::SetGuildURL(uint32 GuildID, std::string URL) { - if(!DBSetGuildURL(GuildID, URL)) - return(false); + if (!UpdateDbGuildURL(GuildID, URL)) { + return false; + } SendGuildRefresh(GuildID, false, true, false, false); - return(true); + return (true); } -bool BaseGuildManager::SetGuildChannel(uint32 GuildID, const char* Channel) +bool BaseGuildManager::SetGuildChannel(uint32 GuildID, std::string Channel) { - if(!DBSetGuildChannel(GuildID, Channel)) - return(false); + if (!UpdateDbGuildChannel(GuildID, Channel)) { + return false; + } SendGuildRefresh(GuildID, false, true, false, false); - return(true); + return (true); } -bool BaseGuildManager::SetGuild(uint32 charid, uint32 guild_id, uint8 rank) { - if(rank > GUILD_MAX_RANK && guild_id != GUILD_NONE) - return(false); +bool BaseGuildManager::SetGuild(uint32 charid, uint32 guild_id, uint8 rank) +{ + if (rank > GUILD_MAX_RANK) { + return false; + } //lookup their old guild, if they had one. - uint32 old_guild = GUILD_NONE; + uint32 old_guild = GUILD_NONE; CharGuildInfo gci; - if(GetCharInfo(charid, gci)) { + if (GetCharInfo(charid, gci)) { old_guild = gci.guild_id; } - if(!DBSetGuild(charid, guild_id, rank)) - return(false); + if (!UpdateDbGuild(charid, guild_id, rank)) { + return false; + } - SendCharRefresh(old_guild, guild_id, charid); + //SendGuildRefresh(guild_id, false, false, false, false); + //SendCharRefresh(old_guild, guild_id, charid); - return(true); + return true; } -//changes rank, but not guild. -bool BaseGuildManager::SetGuildRank(uint32 charid, uint8 rank) { - if(rank > GUILD_MAX_RANK) - return(false); +bool BaseGuildManager::SetGuildRank(uint32 charid, uint8 rank) +{ + if (rank > GUILD_MAX_RANK) { + return false; + } - if(!DBSetGuildRank(charid, rank)) - return(false); + if (!UpdateDbGuildRank(charid, rank)) { + return false; + } - SendCharRefresh(GUILD_NONE, 0, charid); - - return(true); + return true; } -bool BaseGuildManager::SetBankerFlag(uint32 charid, bool is_banker) { - if(!DBSetBankerFlag(charid, is_banker)) - return(false); +bool BaseGuildManager::SetBankerFlag(uint32 charid, bool is_banker) +{ + if (!UpdateDbBankerFlag(charid, is_banker)) { + return (false); + } + return (true); +} + +bool BaseGuildManager::ForceRankUpdate(uint32 charid) +{ SendRankUpdate(charid); - - return(true); -} - -bool BaseGuildManager::ForceRankUpdate(uint32 charid) { - SendRankUpdate(charid); - return(true); + return (true); } bool BaseGuildManager::SetAltFlag(uint32 charid, bool is_alt) { - if(!DBSetAltFlag(charid, is_alt)) - return(false); + if (!UpdateDbAltFlag(charid, is_alt)) { + return (false); + } + //SendRankUpdate(charid); - SendRankUpdate(charid); - - return(true); + return (true); } -bool BaseGuildManager::SetTributeFlag(uint32 charid, bool enabled) { - if(!DBSetTributeFlag(charid, enabled)) - return(false); +bool BaseGuildManager::SetTributeFlag(uint32 charid, bool enabled) +{ + if (!UpdateDbTributeFlag(charid, enabled)) { + return (false); + } SendCharRefresh(GUILD_NONE, 0, charid); - return(true); + return (true); } -bool BaseGuildManager::SetPublicNote(uint32 charid, const char *note) { - if(!DBSetPublicNote(charid, note)) - return(false); - - SendCharRefresh(GUILD_NONE, 0, charid); - - return(true); -} - -uint32 BaseGuildManager::DBCreateGuild(const char* name, uint32 leader) { - //first try to find a free ID. - uint32 new_id = _GetFreeGuildID(); - if(new_id == GUILD_NONE) - return(GUILD_NONE); - - //now make the guild record in our local manager. - //this also sets up the default ranks for us. - _CreateGuild(new_id, name, leader, 0, "", "", "", ""); - - //now store the resulting guild setup into the DB. - if(!_StoreGuildDB(new_id)) { - LogGuilds("Error storing new guild. It may have been partially created which may need manual removal"); - return(GUILD_NONE); - } - - LogGuilds("Created guild [{}] in the database", new_id); - - return(new_id); -} - -bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { - - //remove the local entry - std::map::iterator res; - res = m_guilds.find(guild_id); - if(res != m_guilds.end()) { - delete res->second; - m_guilds.erase(res); - } - - if(m_db == nullptr) { - LogGuilds("Requested to delete guild [{}] when we have no database object", guild_id); - return(false); - } - - //clear out old `guilds` entry - std::string query = StringFormat("DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id); - QueryWithLogging(query, "clearing old guild record"); - - //clear out old `guild_ranks` entries - query = StringFormat("DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); - QueryWithLogging(query, "clearing old guild_ranks records"); - - //clear out people belonging to this guild. - query = StringFormat("DELETE FROM guild_members WHERE guild_id=%lu", (unsigned long)guild_id); - QueryWithLogging(query, "clearing chars in guild"); - - // Delete the guild bank - query = StringFormat("DELETE FROM guild_bank WHERE guildid=%lu", (unsigned long)guild_id); - QueryWithLogging(query, "deleting guild bank"); - - LogGuilds("Deleted guild [{}] from the database", guild_id); - - return(true); -} - -bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { - if(m_db == nullptr) { - LogGuilds("Requested to rename guild [{}] when we have no database object", guild_id); +bool BaseGuildManager::SetPublicNote(uint32 charid, std::string public_note) +{ + if (!UpdateDbPublicNote(charid, public_note)) { return false; } - std::map::const_iterator res; - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return false; - GuildInfo *info = res->second; - - //escape our strings. - uint32 len = strlen(name); - auto esc = new char[len * 2 + 1]; - m_db->DoEscapeString(esc, name, len); - - //insert the new `guilds` entry - std::string query = StringFormat("UPDATE guilds SET name='%s' WHERE id=%d", esc, guild_id); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - LogGuilds("Error renaming guild [{}] [{}]: [{}]", guild_id, query.c_str(), results.Success()); - safe_delete_array(esc); - return false; - } - safe_delete_array(esc); - - LogGuilds("Renamed guild [{}] ([{}]) to [{}] in database", info->name.c_str(), guild_id, name); - - info->name = name; //update our local record. - return true; } -bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { - if(m_db == nullptr) { - LogGuilds("Requested to set the leader for guild [{}] when we have no database object", guild_id); +uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader) +{ + auto new_id = _GetFreeGuildID(); + if (new_id == GUILD_NONE) { + return GUILD_NONE; + } + + std::string empty = ""; + _CreateGuild(new_id, name, leader, 0, empty, empty, empty, empty, 0); + + if (!_StoreGuildDB(new_id)) { + LogGuilds("Error storing new guild with id [{}]", new_id); + return GUILD_NONE; + } + + LogGuilds("Created new guild with id [{}] in the database", new_id); + + return new_id; +} + +bool BaseGuildManager::UpdateDbDeleteGuild(uint32 guild_id, bool local_delete, bool db_delete) +{ + if (local_delete) { + auto where_filter = fmt::format("guildid = {}", guild_id); + auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter); + if (!bank_items.empty()) { + LogError( + "Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.", + guild_id, + bank_items.size() + ); + LogGuilds( + "Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.", + guild_id, + bank_items.size() + ); + return false; + } + else { + std::map::iterator res; + res = m_guilds.find(guild_id); + if (res != m_guilds.end()) { + delete res->second; + m_guilds.erase(res); + LogGuilds("Deleted guild [{}] from memory", guild_id); + //Does this need to be sent to world? + } + } + } + + if (db_delete) { + auto where_filter = fmt::format("guildid = {}", guild_id); + auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter); + if (!bank_items.empty()) { + LogError( + "Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.", + guild_id, + bank_items.size() + ); + LogGuilds( + "Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.", + guild_id, + bank_items.size() + ); + return false; + } + else { + auto where_filter = fmt::format("guild_id = {}", guild_id); + GuildTributesRepository::DeleteOne(*m_db, guild_id); + GuildsRepository::DeleteOne(*m_db, guild_id); + GuildRanksRepository::DeleteWhere(*m_db, where_filter); + GuildPermissionsRepository::DeleteWhere(*m_db, where_filter); + GuildMembersRepository::DeleteWhere(*m_db, where_filter); + LogGuilds("Deleted guild [{}] from the database", guild_id); + } + } + return true; +} + +bool BaseGuildManager::UpdateDbRenameGuild(uint32 guild_id, std::string new_name) +{ + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to rename guild id [{}] though guild could not be found", guild_id); return false; } - std::map::const_iterator res; - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return false; - GuildInfo *info = res->second; + auto old_name = in->name; + in->name = new_name; + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); - //insert the new `guilds` entry - std::string query = StringFormat("UPDATE guilds SET leader='%d' WHERE id=%d",leader, guild_id); - auto results = m_db->QueryDatabase(query); + if (GuildsRepository::UpdateOne(*m_db, out)) { + LogGuilds("Renamed guild id [{}] ([{}]) to [{}] in database", guild_id, old_name.c_str(), in->name.c_str()); + return true; + } + return false; +} - if (!results.Success()) - { +bool BaseGuildManager::UpdateDbGuildLeader(uint32 guild_id, uint32 leader) +{ + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to appoint new guild leader for guild id [{}] though guild could not be found", guild_id); return false; } - //set the old leader to officer - if(!DBSetGuildRank(info->leader_char_id, GUILD_OFFICER)) + auto old_leader = in->leader; + in->leader = leader; + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); + + if (!GuildsRepository::UpdateOne(*m_db, out)) { + LogGuilds("Could not make character id [{}] the leader for guild id [{}] in guilds table", leader, guild_id); return false; - //set the new leader to leader - if(!DBSetGuildRank(leader, GUILD_LEADER)) + } + + if (!UpdateDbGuildRank(old_leader, GUILD_OFFICER) || !UpdateDbBankerFlag(old_leader, false)) { + LogGuilds("Could not make character id [{}] an officer/non-banker for guild id [{}] in guild_members table", old_leader, guild_id); return false; + } + + if (!UpdateDbGuildRank(out.leader, GUILD_LEADER) || !UpdateDbBankerFlag(out.leader, true)) { + LogGuilds("Could not make character id [{}] the leader/banker for guild id [{}] in guild_members table", leader, guild_id); + return false; + } LogGuilds("Set guild leader for guild [{}] to [{}] in the database", guild_id, leader); - info->leader_char_id = leader; //update our local record. - return true; } -bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { - if(m_db == nullptr) { - LogGuilds("Requested to set the MOTD for guild [{}] when we have no database object", guild_id); - return(false); - } - - std::map::const_iterator res; - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(false); - GuildInfo *info = res->second; - - //escape our strings. - uint32 len = strlen(motd); - uint32 len2 = strlen(setter); - auto esc = new char[len * 2 + 1]; - auto esc_set = new char[len2 * 2 + 1]; - m_db->DoEscapeString(esc, motd, len); - m_db->DoEscapeString(esc_set, setter, len2); - - //insert the new `guilds` entry - std::string query = StringFormat("UPDATE guilds SET motd='%s',motd_setter='%s' WHERE id=%d", esc, esc_set, guild_id); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - safe_delete_array(esc); - safe_delete_array(esc_set); - return false; - } - safe_delete_array(esc); - safe_delete_array(esc_set); - - LogGuilds("Set MOTD for guild [{}] in the database", guild_id); - - info->motd = motd; //update our local record. - info->motd_setter = setter; //update our local record. - - return true; -} - -bool BaseGuildManager::DBSetGuildURL(uint32 GuildID, const char* URL) +bool BaseGuildManager::UpdateDbGuildMOTD(uint32 guild_id, std::string motd, std::string setter) { - if(m_db == nullptr) + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to rename guild id [{}] though guild could not be found", guild_id); return false; - - auto res = m_guilds.find(GuildID); - if(res == m_guilds.end()) - return false; - - GuildInfo *info = res->second; - - //escape our strings. - uint32 len = strlen(URL); - auto esc = new char[len * 2 + 1]; - m_db->DoEscapeString(esc, URL, len); - - std::string query = StringFormat("UPDATE guilds SET url='%s' WHERE id=%d", esc, GuildID); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - safe_delete_array(esc); - return(false); } - safe_delete_array(esc); - LogGuilds("Set URL for guild [{}] in the database", GuildID); + in->motd = motd; + in->motd_setter = setter; + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); - info->url = URL; //update our local record. - - return true; + if (GuildsRepository::UpdateOne(*m_db, out)) { + LogGuilds("Updated the motd for guild id [{}] in database", guild_id); + return true; + } + return false; } -bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) +bool BaseGuildManager::UpdateDbGuildURL(uint32 guild_id, std::string URL) { - if(m_db == nullptr) - return(false); - - auto res = m_guilds.find(GuildID); - - if(res == m_guilds.end()) - return(false); - - GuildInfo *info = res->second; - - //escape our strings. - uint32 len = strlen(Channel); - auto esc = new char[len * 2 + 1]; - m_db->DoEscapeString(esc, Channel, len); - - std::string query = StringFormat("UPDATE guilds SET channel='%s' WHERE id=%d", esc, GuildID); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - safe_delete_array(esc); - return(false); + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to update url for guild id [{}] though guild could not be found", guild_id); + return false; } - safe_delete_array(esc); - LogGuilds("Set Channel for guild [{}] in the database", GuildID); - - info->channel = Channel; //update our local record. - - return(true); + in->url = URL; + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); + if (GuildsRepository::UpdateOne(*m_db, out)) { + LogGuilds("Updated the url for guild id [{}] in database", guild_id); + return true; + } + return false; } -bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { - if(m_db == nullptr) { - LogGuilds("Requested to set char to guild [{}] when we have no database object", guild_id); - return(false); +bool BaseGuildManager::UpdateDbGuildChannel(uint32 guild_id, std::string Channel) +{ + auto in = GetGuildByGuildID(guild_id); + if (!in) { + LogGuilds("Request to update channel message for guild id [{}] though guild could not be found", guild_id); + return false; } - std::string query; + in->channel = Channel; + GuildsRepository::Guilds out = CreateGuildRepoFromGuildInfo(guild_id, *in); - if(guild_id != GUILD_NONE) { - query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,`rank`,public_note) VALUES(%d,%d,%d,'')", charid, guild_id, rank); - auto results = m_db->QueryDatabase(query); + if (GuildsRepository::UpdateOne(*m_db, out)) { + LogGuilds("Updated the channel message for guild id [{}] in database", guild_id); + return true; + } + return false; +} - if (!results.Success()) { +bool BaseGuildManager::UpdateDbGuild(uint32 char_id, uint32 guild_id, uint8 rank) +{ + if (guild_id == GUILD_NONE) { + if (!GuildMembersRepository::DeleteOne(*m_db, char_id)) { + LogError( + "Request to remove a character id {} from guild_members who is not in a guild {}.", + char_id, + guild_id + ); return false; } - - } else { - query = StringFormat("DELETE FROM guild_members WHERE char_id=%d", charid); - auto results = m_db->QueryDatabase(query); - if (!results.Success()) - { - return false; + else { + LogGuilds("Removed character id {} from guild id {}", char_id, guild_id); + return true; } - } - LogGuilds("Set char [{}] to guild [{}] and rank [{}] in the database", charid, guild_id, rank); - return true; -} + } -bool BaseGuildManager::DBSetGuildRank(uint32 charid, uint8 rank) { - std::string query = StringFormat("UPDATE guild_members SET `rank`=%d WHERE char_id=%d", rank, charid); - return(QueryWithLogging(query, "setting a guild member's rank")); -} + auto members = GuildMembersRepository::GetWhere( + *m_db, + fmt::format( + "char_id = {} and guild_id = {} LIMIT 1", + char_id, + guild_id + ) + ); -bool BaseGuildManager::DBSetBankerFlag(uint32 charid, bool is_banker) { - std::string query = StringFormat("UPDATE guild_members SET banker=%d WHERE char_id=%d", - is_banker? 1: 0, charid); - return(QueryWithLogging(query, "setting a guild member's banker flag")); -} + auto e = GuildMembersRepository::NewEntity(); + if (!members.empty()) { + e = members[0]; + } + e.char_id = (int32_t) char_id; + e.guild_id = guild_id; + e.rank_ = rank; -bool BaseGuildManager::GetBankerFlag(uint32 CharID) -{ - if(!m_db) - return false; - - std::string query = StringFormat("select `banker` from `guild_members` where char_id=%i LIMIT 1", CharID); - auto results = m_db->QueryDatabase(query); - if(!results.Success()) - { + auto r = GuildMembersRepository::ReplaceOne(*m_db, e); + if (!r) { + LogGuilds("Error updating or inserting character id [{}] to guild id [{}]", char_id, guild_id); return false; } - if(results.RowCount() != 1) - return false; - - auto row = results.begin(); - - return Strings::ToBool(row[0]); + LogGuilds("Set char [{}] to guild [{}] and rank [{}] in the database", char_id, guild_id, rank); + return true; } -bool BaseGuildManager::DBSetAltFlag(uint32 charid, bool is_alt) +bool BaseGuildManager::UpdateDbGuildRank(uint32 char_id, uint8 rank_id) { - std::string query = StringFormat("UPDATE guild_members SET alt=%d WHERE char_id=%d", - is_alt ? 1: 0, charid); + if (!GuildMembersRepository::UpdateMemberRank(*m_db, char_id, rank_id)) { + return false; + } + return true; +} - return(QueryWithLogging(query, "setting a guild member's alt flag")); +bool BaseGuildManager::UpdateDbBankerFlag(uint32 char_id, bool status) +{ + if(!GuildMembersRepository::UpdateBankerFlag(*m_db, char_id, status)) { + return false; + } + return true; +} + +bool BaseGuildManager::GetBankerFlag(uint32 CharID, bool compat_mode) +{ + auto db_banker = 0; + auto member = GuildMembersRepository::FindOne(*m_db, CharID); + + if (compat_mode) { + auto db_banker = member.banker; + return db_banker; + } + + return member.banker || GetGuildBankerStatus(member.guild_id, member.rank_); +} + +bool BaseGuildManager::UpdateDbAltFlag(uint32 charid, bool is_alt) +{ + std::string query = StringFormat( + "UPDATE guild_members SET alt=%d WHERE char_id=%d", + is_alt ? 1 : 0, charid + ); + + return (QueryWithLogging(query, "setting a guild member's alt flag")); } bool BaseGuildManager::GetAltFlag(uint32 CharID) { - if(!m_db) - return false; - - std::string query = StringFormat("SELECT `alt` FROM `guild_members` WHERE char_id=%i LIMIT 1", CharID); - auto results = m_db->QueryDatabase(query); - if(!results.Success()) - { + std::string query = StringFormat("SELECT `alt` FROM `guild_members` WHERE char_id=%i LIMIT 1", CharID); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { return false; } - if(results.RowCount() != 1) + if (results.RowCount() != 1) { return false; + } auto row = results.begin(); return Strings::ToBool(row[0]); } -bool BaseGuildManager::DBSetTributeFlag(uint32 charid, bool enabled) { - std::string query = StringFormat("UPDATE guild_members SET tribute_enable=%d WHERE char_id=%d", - enabled ? 1: 0, charid); - return(QueryWithLogging(query, "setting a guild member's tribute flag")); +bool BaseGuildManager::UpdateDbTributeFlag(uint32 charid, bool enabled) +{ + std::string query = StringFormat( + "UPDATE guild_members SET tribute_enable=%d WHERE char_id=%d", + enabled ? 1 : 0, charid + ); + return (QueryWithLogging(query, "setting a guild member's tribute flag")); } -bool BaseGuildManager::DBSetPublicNote(uint32 charid, const char* note) { - if(m_db == nullptr) - return(false); - - //escape our strings. - uint32 len = strlen(note); - auto esc = new char[len * 2 + 1]; - m_db->DoEscapeString(esc, note, len); - - //insert the new `guilds` entry - std::string query = StringFormat("UPDATE guild_members SET public_note='%s' WHERE char_id=%d", esc, charid); - safe_delete_array(esc); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { +bool BaseGuildManager::UpdateDbPublicNote(uint32 charid, std::string public_note) +{ + auto result = GuildMembersRepository::UpdateNote(*m_db, charid, public_note); + if (!result) { + LogGuilds("Set public not for char [{}]", charid); return false; } - - LogGuilds("Set public not for char [{}]", charid); - return true; } -bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) { - if(m_db == nullptr) - return(false); +bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) +{ + auto results = m_db->QueryDatabase(query); - auto results = m_db->QueryDatabase(query); - - if (!results.Success()) - { - return(false); + if (!results.Success()) { + return (false); } - return(true); + return (true); } #define GuildMemberBaseQuery \ "SELECT c.`id`, c.`name`, c.`class`, c.`level`, c.`last_login`, c.`zone_id`," \ " g.`guild_id`, g.`rank`, g.`tribute_enable`, g.`total_tribute`, g.`last_tribute`," \ -" g.`banker`, g.`public_note`, g.`alt` " \ +" g.`banker`, g.`public_note`, g.`alt`, g.`online` " \ " FROM `character_data` AS c LEFT JOIN `guild_members` AS g ON c.`id` = g.`char_id` " -static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into) { +static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into) +{ //fields from `characer_` - into.char_id = Strings::ToUnsignedInt(row[0]); - into.char_name = row[1]; - into.class_ = Strings::ToUnsignedInt(row[2]); - into.level = Strings::ToUnsignedInt(row[3]); - into.time_last_on = Strings::ToUnsignedInt(row[4]); - into.zone_id = Strings::ToUnsignedInt(row[5]); + into.char_id = Strings::ToUnsignedInt(row[0]); + into.char_name = row[1]; + into.class_ = Strings::ToUnsignedInt(row[2]); + into.level = Strings::ToUnsignedInt(row[3]); + into.time_last_on = Strings::ToUnsignedInt(row[4]); + into.zone_id = Strings::ToUnsignedInt(row[5]); //fields from `guild_members`, leave at defaults if missing - into.guild_id = row[6] ? Strings::ToUnsignedInt(row[6]) : GUILD_NONE; - into.rank = row[7] ? Strings::ToUnsignedInt(row[7]) : (GUILD_MAX_RANK+1); - into.tribute_enable = row[8] ? (row[8][0] == '0'?false:true) : false; - into.total_tribute = row[9] ? Strings::ToUnsignedInt(row[9]) : 0; - into.last_tribute = row[10]? Strings::ToUnsignedInt(row[10]) : 0; //timestamp - into.banker = row[11]? (row[11][0] == '0'?false:true) : false; - into.public_note = row[12]? row[12] : ""; - into.alt = row[13]? (row[13][0] == '0'?false:true) : false; + into.guild_id = row[6] ? Strings::ToUnsignedInt(row[6]) : GUILD_NONE; + into.rank = row[7] ? Strings::ToUnsignedInt(row[7]) : (GUILD_MAX_RANK); + into.tribute_enable = row[8] ? (row[8][0] == '0' ? false : true) : false; + into.total_tribute = row[9] ? Strings::ToUnsignedInt(row[9]) : 0; + into.last_tribute = row[10] ? Strings::ToUnsignedInt(row[10]) : 0; //timestamp + into.banker = row[11] ? (row[11][0] == '0' ? false : true) : false; + into.public_note = row[12] ? row[12] : ""; + into.alt = row[13] ? (row[13][0] == '0' ? false : true) : false; + into.online = row[14] ? (row[14][0] == '0' ? false : true) : false; //a little sanity checking/cleanup - if(into.guild_id == 0) + if (into.guild_id == 0) { into.guild_id = GUILD_NONE; - if(into.rank > GUILD_MAX_RANK) + } + if (into.rank > GUILD_MAX_RANK) { into.rank = GUILD_RANK_NONE; + } } -bool BaseGuildManager::GetEntireGuild(uint32 guild_id, std::vector &members) { +bool BaseGuildManager::GetEntireGuild(uint32 guild_id, std::vector &members) +{ members.clear(); - if(m_db == nullptr) - return(false); - //load up the rank info for each guild. - std::string query = StringFormat(GuildMemberBaseQuery " WHERE g.guild_id=%d AND c.deleted_at IS NULL", guild_id); - auto results = m_db->QueryDatabase(query); + std::string query = StringFormat(GuildMemberBaseQuery " WHERE g.guild_id=%d AND c.deleted_at IS NULL", guild_id); + auto results = m_db->QueryDatabase(query); if (!results.Success()) { return false; } @@ -913,48 +904,39 @@ bool BaseGuildManager::GetEntireGuild(uint32 guild_id, std::vectorDoEscapeString(esc, char_name, nl); //load up the rank info for each guild. - std::string query = StringFormat(GuildMemberBaseQuery " WHERE c.name='%s' AND c.deleted_at IS NULL", esc); - safe_delete_array(esc); - auto results = m_db->QueryDatabase(query); + std::string query = StringFormat(GuildMemberBaseQuery " WHERE c.name='%s' AND c.deleted_at IS NULL", esc); + safe_delete_array(esc); + auto results = m_db->QueryDatabase(query); if (!results.Success()) { return false; } - if (results.RowCount() == 0) - return false; - - auto row = results.begin(); - ProcessGuildMember(row, into); - LogGuilds("Retreived guild member info for char [{}] from the database", char_name); - - return true; - - -} - -bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into) { - if(m_db == nullptr) { - LogGuilds("Requested char info on [{}] when we have no database object", char_id); + if (results.RowCount() == 0) { return false; } + auto row = results.begin(); + ProcessGuildMember(row, into); + LogGuilds("Retrieved guild member info for char [{}] from the database", char_name); + + return true; +} + +bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into) +{ //load up the rank info for each guild. std::string query = StringFormat(GuildMemberBaseQuery " WHERE c.id=%d AND c.deleted_at IS NULL", char_id); auto results = m_db->QueryDatabase(query); @@ -962,27 +944,29 @@ bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into) { return false; } - if (results.RowCount() == 0) - return false; + if (results.RowCount() == 0) { + return false; + } - auto row = results.begin(); - ProcessGuildMember(row, into); - LogGuilds("Retreived guild member info for char [{}]", char_id); + auto row = results.begin(); + ProcessGuildMember(row, into); + LogGuilds("Retrieved guild member info for char [{}]", char_id); return true; } //returns ownership of the buffer. -uint8 *BaseGuildManager::MakeGuildList(const char *head_name, uint32 &length) const { +uint8 *BaseGuildManager::MakeGuildList(const char *head_name, uint32 &length) const +{ //dynamic structs will make this a lot less painful. length = sizeof(GuildsList_Struct); auto buffer = new uint8[length]; //a bit little better than memsetting the whole thing... - uint32 r,pos; - for(r = 0, pos = 0; r <= MAX_NUMBER_GUILDS; r++, pos += 64) { + uint32 r, pos; + for (r = 0, pos = 0; r <= MAX_NUMBER_GUILDS; r++, pos += 64) { //strcpy((char *) buffer+pos, "BAD GUILD"); // These 'BAD GUILD' entries were showing in the drop-downs for selecting guilds in the LFP window, // so just fill unused entries with an empty string instead. @@ -994,229 +978,254 @@ uint8 *BaseGuildManager::MakeGuildList(const char *head_name, uint32 &length) co std::map::const_iterator cur, end; cur = m_guilds.begin(); end = m_guilds.end(); - for(; cur != end; ++cur) { + for (; cur != end; ++cur) { pos = 64 + (64 * cur->first); strn0cpy((char *) buffer + pos, cur->second->name.c_str(), 64); } - return(buffer); + return (buffer); } -const char *BaseGuildManager::GetRankName(uint32 guild_id, uint8 rank) const { - if(rank > GUILD_MAX_RANK) - return("Invalid Rank"); +const char *BaseGuildManager::GetRankName(uint32 guild_id, uint8 rank) const +{ + if (rank > GUILD_MAX_RANK) { + return ("Invalid Rank"); + } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return("Invalid Guild Rank"); - return(res->second->ranks[rank].name.c_str()); + if (res == m_guilds.end()) { + return ("Invalid Guild Rank"); + } + return (res->second->rank_names[rank].c_str()); } -const char *BaseGuildManager::GetGuildName(uint32 guild_id) const { - if(guild_id == GUILD_NONE) - return(""); +const char *BaseGuildManager::GetGuildName(uint32 guild_id) const +{ + if (guild_id == GUILD_NONE) { + return (""); + } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return("Invalid Guild"); - return(res->second->name.c_str()); + if (res == m_guilds.end()) { + return ("Invalid Guild"); + } + return (res->second->name.c_str()); } -bool BaseGuildManager::GetGuildNameByID(uint32 guild_id, std::string &into) const { +bool BaseGuildManager::GetGuildNameByID(uint32 guild_id, std::string &into) const +{ std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(false); + if (res == m_guilds.end()) { + return (false); + } into = res->second->name; - return(true); + return (true); } uint32 BaseGuildManager::GetGuildIDByName(const char *GuildName) { std::map::iterator Iterator; - for(Iterator = m_guilds.begin(); Iterator != m_guilds.end(); ++Iterator) - { - if(!strcasecmp((*Iterator).second->name.c_str(), GuildName)) + for (Iterator = m_guilds.begin(); Iterator != m_guilds.end(); ++Iterator) { + if (!strcasecmp((*Iterator).second->name.c_str(), GuildName)) { return (*Iterator).first; + } } return GUILD_NONE; } -bool BaseGuildManager::GetGuildMOTD(uint32 guild_id, char *motd_buffer, char *setter_buffer) const { +bool BaseGuildManager::GetGuildMOTD(uint32 guild_id, char *motd_buffer, char *setter_buffer) const +{ std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(false); + if (res == m_guilds.end()) { + return (false); + } strn0cpy(motd_buffer, res->second->motd.c_str(), 512); strn0cpy(setter_buffer, res->second->motd_setter.c_str(), 64); - return(true); + return (true); } bool BaseGuildManager::GetGuildURL(uint32 GuildID, char *URLBuffer) const { std::map::const_iterator res; res = m_guilds.find(GuildID); - if(res == m_guilds.end()) - return(false); + if (res == m_guilds.end()) { + return (false); + } strn0cpy(URLBuffer, res->second->url.c_str(), 512); - return(true); + return (true); } bool BaseGuildManager::GetGuildChannel(uint32 GuildID, char *ChannelBuffer) const { std::map::const_iterator res; res = m_guilds.find(GuildID); - if(res == m_guilds.end()) - return(false); + if (res == m_guilds.end()) { + return (false); + } strn0cpy(ChannelBuffer, res->second->channel.c_str(), 128); - return(true); + return (true); } -bool BaseGuildManager::GuildExists(uint32 guild_id) const { - if(guild_id == GUILD_NONE) - return(false); - return(m_guilds.find(guild_id) != m_guilds.end()); +bool BaseGuildManager::GuildExists(uint32 guild_id) const +{ + if (guild_id == GUILD_NONE) { + return (false); + } + return (m_guilds.find(guild_id) != m_guilds.end()); } -bool BaseGuildManager::IsGuildLeader(uint32 guild_id, uint32 char_id) const { - if(guild_id == GUILD_NONE) { +bool BaseGuildManager::IsGuildLeader(uint32 guild_id, uint32 char_id) const +{ + if (guild_id == GUILD_NONE) { LogGuilds("Check leader for char [{}]: not a guild", char_id); - return(false); + return (false); } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { + if (res == m_guilds.end()) { LogGuilds("Check leader for char [{}]: invalid guild", char_id); - return(false); //invalid guild + return (false); //invalid guild } - LogGuilds("Check leader for guild [{}], char [{}]: leader id=[{}]", guild_id, char_id, res->second->leader_char_id); - return(char_id == res->second->leader_char_id); + LogGuilds("Check leader for guild [{}]\, char [{}]\: leader id=[{}]", guild_id, char_id, res->second->leader); + return (char_id == res->second->leader); } -uint32 BaseGuildManager::FindGuildByLeader(uint32 leader) const { +uint32 BaseGuildManager::FindGuildByLeader(uint32 leader) const +{ std::map::const_iterator cur, end; cur = m_guilds.begin(); end = m_guilds.end(); - for(; cur != end; ++cur) { - if(cur->second->leader_char_id == leader) - return(cur->first); + for (; cur != end; ++cur) { + if (cur->second->leader == leader) { + return (cur->first); + } } - return(GUILD_NONE); + return (GUILD_NONE); } //returns the rank to be sent to the client for display purposes, given their eqemu rank. -uint8 BaseGuildManager::GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const { - std::map::const_iterator res; - res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(3); //invalid guild rank - if (res->second->ranks[rank].permissions[GUILD_WARPEACE] || res->second->leader_char_id == char_id) - return(2); //leader rank - else if (res->second->ranks[rank].permissions[GUILD_INVITE] || res->second->ranks[rank].permissions[GUILD_REMOVE] || res->second->ranks[rank].permissions[GUILD_MOTD]) - return(1); //officer rank - return(0); //member rank +uint8 BaseGuildManager::GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const +{ + return rank; } -bool BaseGuildManager::CheckGMStatus(uint32 guild_id, uint8 status) const { - if(status >= 250) { +bool BaseGuildManager::CheckGMStatus(uint32 guild_id, uint8 status) const +{ + if (status >= 250) { LogGuilds("Check permission on guild [{}] with user status [{}] > 250, granted", guild_id, status); - return(true); //250+ as allowed anything + return (true); //250+ as allowed anything } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { + if (res == m_guilds.end()) { LogGuilds("Check permission on guild [{}] with user status [{}], no such guild, denied", guild_id, status); - return(false); //invalid guild + return (false); //invalid guild } bool granted = (res->second->minstatus <= status); - LogGuilds("Check permission on guild [{}] ([{}]) with user status [{}]. Min status [{}]: [{}]", - res->second->name.c_str(), guild_id, status, res->second->minstatus, granted?"granted":"denied"); + LogGuilds( + "Check permission on guild [{}] ([{}]) with user status [{}]. Min status [{}]: [{}]", + res->second->name.c_str(), + guild_id, + status, + res->second->minstatus, + granted ? "granted" : "denied" + ); - return(granted); + return (granted); } -bool BaseGuildManager::CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const { - if(rank > GUILD_MAX_RANK) { - LogGuilds("Check permission on guild [{}] and rank [{}] for action [{}] ([{}]): Invalid rank, denied", - guild_id, rank, GuildActionNames[act], act); - return(false); //invalid rank +bool BaseGuildManager::CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const +{ + if (rank > GUILD_MAX_RANK) { + LogGuilds( + "Check permission on guild [{}] and rank [{}] for action ([{}]): Invalid rank, denied", + guild_id, + rank, + act); + return false; //invalid rank } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { - LogGuilds("Check permission on guild [{}] and rank [{}] for action [{}] ([{}]): Invalid guild, denied", - guild_id, rank, GuildActionNames[act], act); - return(false); //invalid guild + if (res == m_guilds.end()) { + LogGuilds( + "Check permission on guild [{}] and rank [{}] for action ([{}]): Invalid guild, denied", + guild_id, + rank, + act + ); + return false; //invalid guild } - bool granted = res->second->ranks[rank].permissions[act]; + bool granted = (res->second->functions[act].perm_value >> (8 - rank)) & 1; - LogGuilds("Check permission on guild [{}] ([{}]) and rank [{}] ([{}]) for action [{}] ([{}]): [{}]", - res->second->name.c_str(), guild_id, - res->second->ranks[rank].name.c_str(), rank, - GuildActionNames[act], act, - granted?"granted":"denied"); - - return(granted); + return granted; } -bool BaseGuildManager::LocalDeleteGuild(uint32 guild_id) { +bool BaseGuildManager::LocalDeleteGuild(uint32 guild_id) +{ std::map::iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) - return(false); //invalid guild + if (res == m_guilds.end()) { + return (false); + } //invalid guild m_guilds.erase(res); - return(true); + return (true); } -void BaseGuildManager::ClearGuilds() { +void BaseGuildManager::ClearGuilds() +{ std::map::iterator cur, end; cur = m_guilds.begin(); end = m_guilds.end(); - for(; cur != end; ++cur) { + for (; cur != end; ++cur) { delete cur->second; } m_guilds.clear(); } -BaseGuildManager::RankInfo::RankInfo() { +BaseGuildManager::RankInfo::RankInfo() +{ uint8 r; - for(r = 0; r < _MaxGuildAction; r++) - permissions[r] = false; } -BaseGuildManager::GuildInfo::GuildInfo() { - leader_char_id = 0; +BaseGuildManager::GuildInfo::GuildInfo() +{ + leader = 0; minstatus = AccountStatus::Player; } uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) { - std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id IN " - "(SELECT id FROM `character_data` WHERE account_id = %i) AND rank = 2", - AccountID); - auto results = m_db->QueryDatabase(query); - if (!results.Success()) - { + + std::string query = StringFormat( + "SELECT guild_id FROM guild_members WHERE char_id IN " + "(SELECT id FROM `character_data` WHERE account_id = %i) AND rank = 1", + AccountID + ); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { return 0; } return results.RowCount(); } -std::string BaseGuildManager::GetGuildNameByID(uint32 guild_id) const { - if(guild_id == GUILD_NONE) { +std::string BaseGuildManager::GetGuildNameByID(uint32 guild_id) const +{ + if (guild_id == GUILD_NONE) { return std::string(); } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { + if (res == m_guilds.end()) { return "Invalid Guild"; } @@ -1225,35 +1234,31 @@ std::string BaseGuildManager::GetGuildNameByID(uint32 guild_id) const { std::string BaseGuildManager::GetGuildRankName(uint32 guild_id, uint8 rank) const { - if(rank > GUILD_MAX_RANK) { + if (rank > GUILD_MAX_RANK) { return "Invalid Rank"; } std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { + if (res == m_guilds.end()) { return "Invalid Guild Rank"; } - return res->second->ranks[rank].name; + return res->second->rank_names[rank]; } uint32 BaseGuildManager::GetGuildIDByCharacterID(uint32 character_id) { - if(!m_db) { - return GUILD_NONE; - } - - std::string query = fmt::format( + std::string query = fmt::format( "SELECT `guild_id` FROM `guild_members` WHERE char_id = {} LIMIT 1", character_id ); - auto results = m_db->QueryDatabase(query); - if(!results.Success() || !results.RowCount()) { + auto results = m_db->QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { return GUILD_NONE; } - auto row = results.begin(); + auto row = results.begin(); auto guild_id = Strings::ToUnsignedInt(row[0]); return guild_id; } @@ -1272,3 +1277,204 @@ bool BaseGuildManager::IsCharacterInGuild(uint32 character_id, uint32 guild_id) return true; } + +BaseGuildManager::GuildInfo *BaseGuildManager::GetGuildByGuildID(uint32 guild_id) +{ + auto guild = m_guilds.find(guild_id); + if (guild != m_guilds.end()) { + return guild->second; + } + return nullptr; +} + +bool BaseGuildManager::GetGuildBankerStatus(uint32 guild_id, uint32 guild_rank) +{ + auto guild = m_guilds.find(guild_id); + if (guild != m_guilds.end()) { + if (guild_rank == GUILD_LEADER) { + return true; + } + else { + return (CheckPermission(guild_id, guild_rank, GUILD_ACTION_BANK_DEPOSIT_ITEMS) && + CheckPermission(guild_id, guild_rank, GUILD_ACTION_BANK_PROMOTE_ITEMS) && + CheckPermission(guild_id, guild_rank, GUILD_ACTION_BANK_VIEW_ITEMS) && + CheckPermission(guild_id, guild_rank, GUILD_ACTION_BANK_WITHDRAW_ITEMS)); + } + } + return false; +} + +std::vector BaseGuildManager::GetGuildMembers(uint32 guild_id) +{ + std::string where_filter = fmt::format("`guild_id` = '{}'", guild_id); + auto guild_members = GuildMembersRepository::GetWhere(*m_db, where_filter); + return guild_members; +} + +bool BaseGuildManager::StoreGuildDB(uint32 guild_id) +{ + return _StoreGuildDB(guild_id); +} + +uint32 BaseGuildManager::UpdateDbGuildFavor(uint32 guild_id, uint32 favor) +{ + if (!GuildsRepository::UpdateFavor(*m_db, guild_id, favor)) { + LogError("Error updating guild favor [{}] for guild id [{}] in database.", favor, guild_id); + return false; + } + + LogGuilds("Set guild favor of [{}] for guild id [{}] in the database", favor, guild_id); + + return favor; +} + +bool BaseGuildManager::UpdateDbGuildTributeEnabled(uint32 guild_id, uint32 enabled) +{ + if (!GuildTributesRepository::UpdateEnabled(*m_db, guild_id, enabled)) { + LogError("Error updating tribute enabled [{}] for guild id [{}] in database.", enabled, guild_id); + return false; + } + + LogGuilds("Set tribute enabled [{}] for guild id [{}] in the database", enabled, guild_id); + + return true; +} + +bool BaseGuildManager::UpdateDbTributeTimeRemaining(uint32 guild_id, uint32 time_remaining) +{ + if (!GuildTributesRepository::UpdateTimeRemaining(*m_db, guild_id, time_remaining)) { + LogError("Error updating tribute time_remaining [{}] for guild id [{}] in database.", time_remaining, guild_id); + return false; + } + + LogGuilds("Set tribute time_remaining [{}] for guild id [{}] in the database", time_remaining, guild_id); + + return true; +} + +bool BaseGuildManager::UpdateDbMemberTributeEnabled(uint32 guild_id, uint32 char_id, uint32 enabled) +{ + CharGuildInfo gci; + GetCharInfo(char_id, gci); + if (gci.char_name.empty()) { + LogGuilds( + "Requested to set member id {} tribute to enabled [{}] in guild [{}] but we could not find the character.", + char_id, + enabled, + guild_id + ); + return false; + } + + if (!GuildMembersRepository::UpdateEnabled(*m_db, guild_id, char_id, enabled)) { + LogError( + "Error updating member id {} tribute enabled [{}] for guild id [{}] in database.", + char_id, + enabled, + guild_id + ); + return false; + } + + LogGuilds( + "Set member {} id {} tribute enabled [{}] for guild id [{}] in the database", + gci.char_name.c_str(), + char_id, + enabled, + guild_id + ); + + return true; +} + +uint32 BaseGuildManager::UpdateDbMemberFavor(uint32 guild_id, uint32 char_id, uint32 favor) +{ + CharGuildInfo gci; + GetCharInfo(char_id, gci); + + if (gci.char_name.empty()) { + LogGuilds( + "Requested to set member id {} tribute to favor [{}] in guild [{}] but we could not find the character.", + char_id, + favor, + guild_id + ); + return false; + } + + gci.total_tribute += favor; + if (!GuildMembersRepository::UpdateFavor(*m_db, guild_id, char_id, gci.total_tribute)) { + LogError( + "Error updating member id {} tribute favor [{}] for guild id [{}] in database.", + char_id, + favor, + guild_id + ); + return false; + } + + LogGuilds( + "Set member {} id {} tribute enabled [{}] for guild id [{}] in the database", + gci.char_name.c_str(), + char_id, + favor, + guild_id + ); + + return gci.total_tribute; +} + +bool BaseGuildManager::UpdateDbMemberOnline(uint32 char_id, bool status) +{ + CharGuildInfo gci; + GetCharInfo(char_id, gci); + + if (gci.char_name.empty()) { + LogGuilds( + "Requested to set member id {} online status [{}] but could not find the character.", + char_id, + status + ); + return false; + } + + if (!GuildMembersRepository::UpdateOnline(*m_db, char_id, status)) { + LogError("Error updating member id {} online status [{}] in database.", char_id, status); + return false; + } + + LogGuilds("Set member {} id {} online status [{}] in the database", gci.char_name.c_str(), char_id, status); + + return true; +} + +GuildsRepository::Guilds +BaseGuildManager::CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo &in) +{ + GuildsRepository::Guilds out{}; + + out.id = guild_id; + out.channel = in.channel; + out.name = in.name; + out.url = in.url; + out.motd = in.motd; + out.motd_setter = in.motd_setter; + out.leader = in.leader; + out.minstatus = in.minstatus; + out.favor = in.tribute.favor; + + return out; +} + +uint32 BaseGuildManager::GetGuildTributeTimeRemaining(uint32 guild_id) +{ + auto guild = GetGuildByGuildID(guild_id); + if (guild) { + if (guild->tribute.timer.Enabled()) { + guild->tribute.time_remaining = guild->tribute.timer.GetRemainingTime(); + } + + return guild->tribute.time_remaining; + } + return 0; +} diff --git a/common/guild_base.h b/common/guild_base.h index 122a9ff2f..c9e95e484 100644 --- a/common/guild_base.h +++ b/common/guild_base.h @@ -5,6 +5,42 @@ #include #include #include +#include "timer.h" +#include "../common/repositories/guild_members_repository.h" +#include "../common/repositories/guilds_repository.h" + +struct DefaultPermissionStruct { + GuildAction id; + uint32 value; +}; + +struct DefaultRankNamesStruct { + uint32 id; + std::string name; +}; + +struct GuildTributeStruct { + Timer timer; + uint32 id_1; + uint32 id_2; + uint32 id_1_tier; + uint32 id_2_tier; + uint32 favor; + uint32 time_remaining; + uint32 enabled; + bool send_timer; +}; + +class TributeData { +public: + //this level data stored in regular byte order and must be flipped before sending + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + uint8 tier_count; + uint32 unknown; + std::string name; + std::string description; + bool is_guild; //is a guild tribute item +}; class Database; @@ -12,22 +48,23 @@ class CharGuildInfo { public: //fields from `characer_` - uint32 char_id; + uint32 char_id; std::string char_name; - uint8 class_; - uint16 level; - uint32 time_last_on; - uint32 zone_id; + uint8 class_; + uint16 level; + uint32 time_last_on; + uint32 zone_id; //fields from `guild_members` - uint32 guild_id; - uint8 rank; - bool tribute_enable; - uint32 total_tribute; - uint32 last_tribute; //timestamp - bool banker; - bool alt; + uint32 guild_id; + uint8 rank; + bool tribute_enable; + uint32 total_tribute; + uint32 last_tribute; //timestamp + bool banker; + bool alt; std::string public_note; + bool online; }; //this object holds guild functionality shared between world and zone. @@ -44,15 +81,16 @@ class BaseGuildManager } bool LoadGuilds(); - bool RefreshGuild(uint32 guild_id); + virtual bool RefreshGuild(uint32 guild_id); //guild edit actions. - uint32 CreateGuild(const char* name, uint32 leader_char_id); + uint32 CreateGuild(std::string name, uint32 leader_char_id); + bool StoreGuildDB(uint32 guild_id); bool DeleteGuild(uint32 guild_id); - bool RenameGuild(uint32 guild_id, const char* name); - bool SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter); - bool SetGuildURL(uint32 GuildID, const char* URL); - bool SetGuildChannel(uint32 GuildID, const char* Channel); + bool RenameGuild(uint32 guild_id, std::string name); + bool SetGuildMOTD(uint32 guild_id, std::string motd, std::string setter); + bool SetGuildURL(uint32 guild_id, std::string URL); + bool SetGuildChannel(uint32 guild_id, std::string Channel); //character edit actions bool SetGuildLeader(uint32 guild_id, uint32 leader_char_id); @@ -62,9 +100,16 @@ class BaseGuildManager bool ForceRankUpdate(uint32 charid); bool GetAltFlag(uint32 CharID); bool SetAltFlag(uint32 charid, bool is_alt); - bool GetBankerFlag(uint32 CharID); + bool GetBankerFlag(uint32 CharID, bool compat_mode = false); + bool GetGuildBankerStatus(uint32 guild_id, uint32 guild_rank); bool SetTributeFlag(uint32 charid, bool enabled); - bool SetPublicNote(uint32 charid, const char *note); + bool SetPublicNote(uint32 charid, std::string public_note); + uint32 UpdateDbGuildFavor(uint32 guild_id, uint32 enabled); + bool UpdateDbGuildTributeEnabled(uint32 guild_id, uint32 enabled); + bool UpdateDbMemberTributeEnabled(uint32 guild_id, uint32 char_id, uint32 enabled); + bool UpdateDbTributeTimeRemaining(uint32 guild_id, uint32 enabled); + uint32 UpdateDbMemberFavor(uint32 guild_id, uint32 char_id, uint32 favor); + bool UpdateDbMemberOnline(uint32 char_id, bool status); //queries bool GetCharInfo(const char *char_name, CharGuildInfo &into); @@ -74,29 +119,24 @@ class BaseGuildManager bool GetGuildMOTD(uint32 guild_id, char *motd_buffer, char *setter_buffer) const; bool GetGuildURL(uint32 GuildID, char *URLBuffer) const; bool GetGuildChannel(uint32 GuildID, char *ChannelBuffer) const; - const char *GetRankName(uint32 guild_id, uint8 rank) const; - const char *GetGuildName(uint32 guild_id) const; - std::string GetGuildNameByID(uint32 guild_id) const; - std::string GetGuildRankName(uint32 guild_id, uint8 rank) const; - bool IsCharacterInGuild(uint32 character_id, uint32 guild_id = 0); - bool GetGuildNameByID(uint32 guild_id, std::string &into) const; - uint32 GetGuildIDByName(const char *GuildName); - uint32 GetGuildIDByCharacterID(uint32 character_id); - bool IsGuildLeader(uint32 guild_id, uint32 char_id) const; - uint8 GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const; - bool CheckGMStatus(uint32 guild_id, uint8 status) const; - bool CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const; -// uint32 Getguild_id(uint32 eqid); - uint32 FindGuildByLeader(uint32 leader) const; -// void GetGuildMembers(uint32 guild_id,GuildMember_Struct* gms); - uint32 NumberInGuild(uint32 guild_id); -// bool GetGuildRanks(uint32 guildeqid, GuildRanks_Struct* gr); -// bool EditGuild(uint32 guild_id, uint8 ranknum, GuildRankLevel_Struct* grl); - - uint8 *MakeGuildList(const char *head_name, uint32 &length) const; //make a guild list packet, returns ownership of the buffer. - - static const char *const GuildActionNames[_MaxGuildAction]; - uint32 DoesAccountContainAGuildLeader(uint32 AccountID); + bool IsCharacterInGuild(uint32 character_id, uint32 guild_id = 0); + bool GetGuildNameByID(uint32 guild_id, std::string& into) const; + bool IsGuildLeader(uint32 guild_id, uint32 char_id) const; + bool CheckGMStatus(uint32 guild_id, uint8 status) const; + bool CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const; + bool UpdateDbBankerFlag(uint32 charid, bool is_banker); + uint8* MakeGuildList(const char* head_name, uint32& length) const; //make a guild list packet, returns ownership of the buffer. + uint8 GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const; + uint32 GetGuildIDByName(const char *GuildName); + uint32 GetGuildIDByCharacterID(uint32 character_id); + uint32 FindGuildByLeader(uint32 leader) const; + uint32 NumberInGuild(uint32 guild_id); + uint32 DoesAccountContainAGuildLeader(uint32 AccountID); + const char* GetRankName(uint32 guild_id, uint8 rank) const; + const char* GetGuildName(uint32 guild_id) const; + std::string GetGuildNameByID(uint32 guild_id) const; + std::string GetGuildRankName(uint32 guild_id, uint8 rank) const; + std::vector GetGuildMembers(uint32 guild_id); protected: //the methods which must be defined by base classes. @@ -105,58 +145,62 @@ class BaseGuildManager virtual void SendRankUpdate(uint32 CharID) = 0; virtual void SendGuildDelete(uint32 guild_id) = 0; - uint32 DBCreateGuild(const char* name, uint32 leader_char_id); - bool DBDeleteGuild(uint32 guild_id); - bool DBRenameGuild(uint32 guild_id, const char* name); - bool DBSetGuildLeader(uint32 guild_id, uint32 leader_char_id); - bool DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter); - bool DBSetGuildURL(uint32 GuildID, const char* URL); - bool DBSetGuildChannel(uint32 GuildID, const char* Channel); - bool DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank); - bool DBSetGuildRank(uint32 charid, uint8 rank); - bool DBSetBankerFlag(uint32 charid, bool is_banker); - bool DBSetAltFlag(uint32 charid, bool is_alt); - bool DBSetTributeFlag(uint32 charid, bool enabled); - bool DBSetPublicNote(uint32 charid, const char *note); + uint32 UpdateDbCreateGuild(std::string name, uint32 leader_char_id); + bool UpdateDbDeleteGuild(uint32 guild_id, bool local_delete = true, bool db_delete = true); + bool UpdateDbRenameGuild(uint32 guild_id, std::string name); + bool UpdateDbGuildLeader(uint32 guild_id, uint32 leader_char_id); + bool UpdateDbGuildMOTD(uint32 guild_id, std::string motd, std::string setter); + bool UpdateDbGuildURL(uint32 GuildID, std::string URL); + bool UpdateDbGuildChannel(uint32 GuildID, std::string Channel); + bool UpdateDbGuild(uint32 charid, uint32 guild_id, uint8 rank); + bool UpdateDbGuildRank(uint32 charid, uint8 rank); + bool UpdateDbAltFlag(uint32 charid, bool is_alt); + bool UpdateDbTributeFlag(uint32 charid, bool enabled); + bool UpdateDbPublicNote(uint32 charid, std::string public_note); bool QueryWithLogging(std::string query, const char *errmsg); -// void DBSetPublicNote(uint32 guild_id,char* charname, char* note); - bool LocalDeleteGuild(uint32 guild_id); - class RankInfo + struct RankInfo { - public: - RankInfo(); - std::string name; - bool permissions[_MaxGuildAction]; + RankInfo(); + std::string rank_name; }; - class GuildInfo + struct Functions { - public: - GuildInfo(); - std::string name; - std::string motd; - std::string motd_setter; - std::string url; - std::string channel; - - uint32 leader_char_id; - uint8 minstatus; - //tribute is not in here on purpose, since it is only valid in world! - RankInfo ranks[GUILD_MAX_RANK + 1]; + uint32 id; + uint32 perm_id; + uint32 guild_id; + uint32 perm_value; }; + public: + class GuildInfo { + public: + GuildInfo(); + std::string name; + std::string motd; + std::string motd_setter; + std::string url; + std::string channel; + uint32 leader; + uint8 minstatus; + std::string rank_names[GUILD_MAX_RANK + 1]; + Functions functions[GUILD_MAX_FUNCTIONS + 1]; + GuildTributeStruct tribute; + }; + virtual BaseGuildManager::GuildInfo* GetGuildByGuildID(uint32 guild_id); + uint32 GetGuildTributeTimeRemaining(uint32 guild_id); + protected: std::map m_guilds; //we own the pointers in this map void ClearGuilds(); //clears internal structure Database *m_db; //we do not own this bool _StoreGuildDB(uint32 guild_id); - GuildInfo *_CreateGuild(uint32 guild_id, const char *guild_name, uint32 account_id, uint8 minstatus, const char *guild_motd, const char *motd_setter, const char *Channel, const char *URL); + GuildInfo* _CreateGuild(uint32 guild_id, std::string guild_name, uint32 leader_char_id, uint8 minstatus, std::string guild_motd, std::string motd_setter, std::string Channel, std::string URL, uint32 favour); uint32 _GetFreeGuildID(); + GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in); }; - - #endif /*GUILD_BASE_H_*/ diff --git a/common/guilds.h b/common/guilds.h index ad91892ce..fca30a5d8 100644 --- a/common/guilds.h +++ b/common/guilds.h @@ -16,31 +16,70 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef GUILD_H -#define GUILD_H +#ifndef GUILDS_H +#define GUILDS_H #include "types.h" #define GUILD_NONE 0xFFFFFFFF // user has no guild -#define GUILD_MAX_RANK 8 // 0-2 - some places in the code assume a single digit, dont go above 9 +#define GUILD_MAX_RANK 8 // 0-2 - some places in the code assume a single digit, dont go above 9 +#define GUILD_MAX_FUNCTIONS 30 +#define GUILD_TRIBUTES_MODIFY 1 +#define GUILD_TRIBUTES_SAVE 0 +#define GUILD_TRIBUTES_OFF 0 +#define GUILD_TRIBUTES_ON 1 +#define GUILD_INVITE_DECLINE 9 //defines for standard ranks -#define GUILD_MEMBER 0 -#define GUILD_OFFICER 1 -#define GUILD_LEADER 2 +#define GUILD_MEMBER_TI 0 +#define GUILD_OFFICER_TI 1 +#define GUILD_LEADER_TI 2 #define GUILD_RANK_NONE (GUILD_MAX_RANK+1) +//defines for standard ranks base on RoF2 definitions +#define GUILD_RANK_NONE 0 +#define GUILD_LEADER 1 +#define GUILD_SENIOR_OFFICER 2 +#define GUILD_OFFICER 3 +#define GUILD_SENIOR_MEMBER 4 +#define GUILD_MEMBER 5 +#define GUILD_JUNIOR_MEMBER 6 +#define GUILD_INITIATE 7 +#define GUILD_RECRUIT 8 + typedef enum { - GUILD_HEAR = 0, - GUILD_SPEAK = 1, - GUILD_INVITE = 2, - GUILD_REMOVE = 3, - GUILD_PROMOTE = 4, - GUILD_DEMOTE = 5, - GUILD_MOTD = 6, - GUILD_WARPEACE = 7, - _MaxGuildAction + GUILD_ACTION_BANNER_CHANGE = 1, + GUILD_ACTION_BANNER_PLANT = 2, + GUILD_ACTION_BANNER_REMOVE = 3, + GUILD_ACTION_DISPLAY_GUILD_NAME = 4, + GUILD_ACTION_RANKS_CHANGE_PERMISSIONS = 5, + GUILD_ACTION_RANKS_CHANGE_RANK_NAMES = 6, + GUILD_ACTION_MEMBERS_INVITE = 7, + GUILD_ACTION_MEMBERS_PROMOTE = 8, + GUILD_ACTION_MEMBERS_DEMOTE = 9, + GUILD_ACTION_MEMBERS_REMOVE = 10, + GUILD_ACTION_EDIT_RECRUITING_SETTINGS = 11, + GUILD_ACTION_EDIT_PUBLIC_NOTES = 12, + GUILD_ACTION_BANK_DEPOSIT_ITEMS = 13, + GUILD_ACTION_BANK_WITHDRAW_ITEMS = 14, + GUILD_ACTION_BANK_VIEW_ITEMS = 15, + GUILD_ACTION_BANK_PROMOTE_ITEMS = 16, + GUILD_ACTION_BANK_CHANGE_ITEM_PERMISSIONS = 17, + GUILD_ACTION_CHANGE_THE_MOTD = 18, + GUILD_ACTION_GUILD_CHAT_SEE = 19, + GUILD_ACTION_GUILD_CHAT_SPEAK_IN = 20, + GUILD_ACTION_SEND_THE_WHOLE_GUILD_E_MAIL = 21, + GUILD_ACTION_TRIBUTE_CHANGE_FOR_OTHERS = 22, + GUILD_ACTION_TRIBUTE_CHANGE_ACTIVE_BENEFIT = 23, + GUILD_ACTION_TROPHY_TRIBUTE_CHANGE_FOR_OTHERS = 24, + GUILD_ACTION_TROPHY_TRIBUTE_CHANGE_ACTIVE_BENEFIT = 25, + GUILD_ACTION_MEMBERS_CHANGE_ALT_FLAG_FOR_OTHER = 26, + GUILD_ACTION_REAL_ESTATE_GUILD_PLOT_BUY = 27, + GUILD_ACTION_REAL_ESTATE_GUILD_PLOT_SELL = 28, + GUILD_ACTION_REAL_ESTATE_MODIFY_TROPHIES = 29, + GUILD_ACTION_MEMBERS_DEMOTE_SELF = 30, } GuildAction; + #endif diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index a8211a7ac..b8d77edbf 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -173,7 +173,8 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id) const result = _GetItem(m_inv, slot_id); } else if ((slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) || - (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END)) { + (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) || + (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END)) { // Equippable slots (on body) result = _GetItem(m_worn, slot_id); } @@ -465,6 +466,10 @@ EQ::ItemInstance* EQ::InventoryProfile::PopItem(int16 slot_id) p = m_worn[slot_id]; m_worn.erase(slot_id); } + else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + p = m_worn[slot_id]; + m_worn.erase(slot_id); + } else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { p = m_bank[slot_id]; m_bank.erase(slot_id); @@ -1419,6 +1424,10 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) m_worn[slot_id] = inst; result = slot_id; } + else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + m_worn[slot_id] = inst; + result = slot_id; + } else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { if (slot_id - EQ::invslot::BANK_BEGIN < m_lookup->InventoryTypeSize.Bank) { m_bank[slot_id] = inst; diff --git a/common/item_instance.cpp b/common/item_instance.cpp index a0cfd1f9e..11d824d92 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -1791,6 +1791,16 @@ int EQ::ItemInstance::RemoveTaskDeliveredItems() return count; } +uint32 EQ::ItemInstance::GetItemGuildFavor() const +{ + uint32 total = 0; + const auto item = GetItem(); + if (item) { + return total = item->GuildFavor; + } + return 0; +} + // // class EvolveInfo // diff --git a/common/item_instance.h b/common/item_instance.h index f9ab558c2..fe8fddd86 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -299,6 +299,7 @@ namespace EQ int GetItemHeroicDR(bool augments = false) const; int GetItemHeroicCorrup(bool augments = false) const; int GetItemHaste(bool augments = false) const; + uint32 GetItemGuildFavor() const; protected: ////////////////////////// diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 376c82019..e036b9a2f 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1308,13 +1308,13 @@ namespace RoF PutFieldN(class_); /* Translate older ranks to new values */ - switch (emu_e->rank) { - case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 - case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 - case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 - default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE - } - + //switch (emu_e->rank) { + //case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 + //case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 + //case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 + //default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE + //} + PutFieldN(rank); PutFieldN(time_last_on); PutFieldN(tribute_enable); e->unknown01 = 0; @@ -3051,19 +3051,12 @@ namespace RoF ENCODE_LENGTH_EXACT(GuildSetRank_Struct); SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); - eq->GuildID = emu->Unknown00; + eq->guild_id = emu->Unknown00; + eq->rank = emu->rank; - /* Translate older ranks to new values */ - switch (emu->Rank) { - case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 - case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 - case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 - default: { eq->Rank = emu->Rank; break; } - } - - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(Banker); - eq->Unknown76 = 1; + memcpy(eq->member_name, emu->member_name, sizeof(eq->member_name)); + OUT(banker); + eq->unknown76 = 1; FINISH_ENCODE(); } @@ -3973,10 +3966,22 @@ namespace RoF /* Translate older ranks to new values */ switch (emu->guildrank) { - case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 - case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 - case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 - default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // + case 0: { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); + break; + } // GUILD_MEMBER 0 + case 1: { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); + break; + } // GUILD_OFFICER 1 + case 2: { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + break; + } // GUILD_LEADER 2 + default: { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); + break; + } // } } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 0ce61f27b..f657ca842 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1319,6 +1319,7 @@ namespace RoF2 buffer++; // Guild ID + //*((uint32*)buffer) = htonl(2); buffer += sizeof(uint32); //add member count. @@ -1357,15 +1358,7 @@ namespace RoF2 PutFieldN(level); PutFieldN(banker); PutFieldN(class_); - - /* Translate older ranks to new values */ - switch (emu_e->rank) { - case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 - case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 - case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 - default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE - } - + PutFieldN(rank); PutFieldN(time_last_on); PutFieldN(tribute_enable); e->unknown01 = 0; @@ -1395,8 +1388,10 @@ namespace RoF2 OUT(GuildID); memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(ZoneID); - OUT(InstanceID); + //OUT(ZoneID); + //OUT(InstanceID); + eq->InstanceID = emu->InstanceID; + eq->ZoneID = emu->ZoneID; OUT(LastSeen); eq->Unknown76 = 0; @@ -1422,7 +1417,7 @@ namespace RoF2 if (InBuffer[0]) { PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; + HighestGuildID += 1; } InBuffer += 64; } @@ -1460,6 +1455,30 @@ namespace RoF2 dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_GuildTributeDonateItem) + { + SETUP_DIRECT_ENCODE(GuildTributeDonateItemReply_Struct, structs::GuildTributeDonateItemReply_Struct); + + Log(Logs::Detail, Logs::Netcode, "RoF2::ENCODE(OP_GuildTributeDonateItem)"); + + OUT(type); + OUT(sub_index); + OUT(aug_index); + OUT(quantity); + OUT(unknown10); + OUT(unknown20); + OUT(favor); + + structs::InventorySlot_Struct iss; + iss = ServerToRoF2Slot(emu->slot); + + eq->slot = iss.Slot; + eq->sub_index = iss.SubIndex; + + FINISH_ENCODE(); + } + + ENCODE(OP_HPUpdate) { SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); @@ -2466,6 +2485,8 @@ namespace RoF2 { outapp->WriteUInt32(0xffffffff); outapp->WriteUInt32(0); +// outapp->WriteUInt32(60); +// outapp->WriteUInt32(1); } outapp->WriteUInt32(0); // Unknown @@ -3104,19 +3125,12 @@ namespace RoF2 ENCODE_LENGTH_EXACT(GuildSetRank_Struct); SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); - eq->GuildID = emu->Unknown00; + eq->guild_id= emu->Unknown00; + eq->rank = emu->rank; - /* Translate older ranks to new values */ - switch (emu->Rank) { - case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 - case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 - case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 - default: { eq->Rank = emu->Rank; break; } - } - - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(Banker); - eq->Unknown76 = 1; + memcpy(eq->member_name, emu->member_name, sizeof(eq->member_name)); + OUT(banker); + eq->unknown76 = 1; FINISH_ENCODE(); } @@ -4178,12 +4192,13 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); /* Translate older ranks to new values */ - switch (emu->guildrank) { - case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 - case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 - case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 - default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // - } + //switch (emu->guildrank) { + //case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 + //case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 + //case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 + //default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // + //} + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); } VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); @@ -4199,7 +4214,7 @@ namespace RoF2 VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC ? 0 : 1); // unknown - Must be 1 for guild name to be shown abover players head. + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); @@ -4955,7 +4970,7 @@ namespace RoF2 strn0cpy(emu->target, eq->target, sizeof(emu->target)); strn0cpy(emu->name, eq->name, sizeof(emu->name)); - // IN(rank); + IN(rank); FINISH_DIRECT_DECODE(); } @@ -4983,6 +4998,38 @@ namespace RoF2 FINISH_DIRECT_DECODE(); } + DECODE(OP_GuildTributeDonateItem) + { + DECODE_LENGTH_EXACT(structs::GuildTributeDonateItemRequest_Struct); + SETUP_DIRECT_DECODE(GuildTributeDonateItemRequest_Struct, structs::GuildTributeDonateItemRequest_Struct); + + Log(Logs::Detail, Logs::Netcode, "RoF2::DECODE(OP_GuildTributeDonateItem)"); + + IN(type); + IN(slot); + IN(sub_index); + IN(aug_index); + IN(unknown10); + IN(quantity); + IN(tribute_master_id); + IN(unknown20); + IN(guild_id); + IN(unknown28); + IN(unknown32); + + structs::InventorySlot_Struct iss; + iss.Slot = eq->slot; + iss.SubIndex = eq->sub_index; + iss.AugIndex = eq->aug_index; + iss.Type = eq->type; + iss.Unknown01 = 0; + iss.Unknown02 = 0; + + emu->slot = RoF2ToServerSlot(iss); + + FINISH_DIRECT_DECODE(); + } + /*DECODE(OP_InspectAnswer) { DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 3e8b2cecc..9c6c22349 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -82,6 +82,7 @@ E(OP_GuildBank) E(OP_GuildMemberList) E(OP_GuildMemberUpdate) E(OP_GuildsList) +E(OP_GuildTributeDonateItem) E(OP_HPUpdate) E(OP_Illusion) E(OP_InspectBuffs) @@ -182,6 +183,7 @@ D(OP_GuildBank) D(OP_GuildDemote) D(OP_GuildRemove) D(OP_GuildStatus) +D(OP_GuildTributeDonateItem) D(OP_InspectRequest) D(OP_ItemLinkClick) D(OP_ItemVerifyRequest) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 66f183599..83ab33295 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -2011,6 +2011,31 @@ struct GuildBankClear_Struct /*16*/ uint32 MainAreaCount; }; +struct GuildTributeDonateItemRequest_Struct { + /*000*/ uint32 type; + /*004*/ uint16 slot; + /*006*/ uint16 sub_index; + /*008*/ uint16 aug_index; + /*010*/ uint16 unknown10; + /*012*/ uint32 quantity; + /*016*/ uint32 tribute_master_id; + /*020*/ uint32 unknown20; + /*024*/ uint32 guild_id; + /*028*/ uint32 unknown28; + /*032*/ uint32 unknown32; +}; + +struct GuildTributeDonateItemReply_Struct { + /*000*/ uint32 type; + /*004*/ uint16 slot; + /*006*/ uint16 sub_index; + /*008*/ uint16 aug_index; + /*010*/ uint16 unknown10; + /*012*/ uint32 quantity; + /*016*/ uint32 unknown20; + /*020*/ uint32 favor; +}; + /* ** Money Loot ** Length: 22 Bytes @@ -3600,11 +3625,11 @@ struct GuildMakeLeader { // Update a guild members rank and banker status struct GuildSetRank_Struct { -/*00*/ uint32 GuildID; // Was Unknown00 -/*04*/ uint32 Rank; -/*08*/ char MemberName[64]; -/*72*/ uint32 Banker; -/*76*/ uint32 Unknown76; // Seen 1 - Maybe Banker? +/*00*/ uint32 guild_id; // Was Unknown00 +/*04*/ uint32 rank; +/*08*/ char member_name[64]; +/*72*/ uint32 banker; +/*76*/ uint32 unknown76; // Seen 1 - Maybe Banker? /*80*/ }; @@ -3722,15 +3747,15 @@ struct TributeItem_Struct struct TributePoint_Struct { int32 tribute_points; - uint32 unknown04; + uint32 unknown04; int32 career_tribute_points; - uint32 unknown12; + uint32 unknown12; }; struct TributeMoney_Struct { uint32 platinum; uint32 tribute_master_id; - int32 tribute_points; + int32 tribute_points; }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 202a6a6df..5f2b8e214 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -3537,11 +3537,11 @@ struct GuildMakeLeader { // Update a guild members rank and banker status struct GuildSetRank_Struct { -/*00*/ uint32 GuildID; // Was Unknown00 -/*04*/ uint32 Rank; -/*08*/ char MemberName[64]; -/*72*/ uint32 Banker; -/*76*/ uint32 Unknown76; // Seen 1 - Maybe Banker? +/*00*/ uint32 guild_id; // Was Unknown00 +/*04*/ uint32 rank; +/*08*/ char member_name[64]; +/*72*/ uint32 banker; +/*76*/ uint32 unknown76; // Seen 1 - Maybe Banker? /*80*/ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 6725f496a..fd8f68acd 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -34,6 +34,7 @@ #include "titanium_structs.h" #include "../path_manager.h" #include "../raid.h" +#include "../guilds.h" #include @@ -664,6 +665,151 @@ namespace Titanium dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_SetGuildRank) + { + ENCODE_LENGTH_EXACT(GuildSetRank_Struct); + SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); + + eq->unknown00 = 0; + eq->unknown04 = 0; + + //Translate older ranks to new values* / + switch (emu->rank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->rank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->rank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->rank = GUILD_LEADER_TI; + break; + } + } + + memcpy(eq->member_name, emu->member_name, sizeof(eq->member_name)); + OUT(banker); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnAppearance) + { + ENCODE_LENGTH_EXACT(SpawnAppearance_Struct); + SETUP_DIRECT_ENCODE(SpawnAppearance_Struct, structs::SpawnAppearance_Struct); + + OUT(spawn_id); + OUT(type); + OUT(parameter); + switch (emu->type) { + case AppearanceType::GuildRank: { + //Translate new ranks to old values* / + switch (emu->parameter) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->parameter = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->parameter = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->parameter = GUILD_LEADER_TI; + break; + } + } + break; + } + case AppearanceType::GuildShow: { + FAIL_ENCODE(); + return; + } + default: { + break; + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberAdd) + { + ENCODE_LENGTH_EXACT(GuildMemberAdd_Struct) + SETUP_DIRECT_ENCODE(GuildMemberAdd_Struct, structs::GuildMemberAdd_Struct) + + OUT(guild_id) + OUT(level) + OUT(class_) + switch (emu->rank_) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->rank_ = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->rank_ = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->rank_ = GUILD_LEADER_TI; + break; + } + } + OUT(zone_id) + OUT(last_on) + OUT_str(player_name) + + FINISH_ENCODE() + } + + ENCODE(OP_GuildMemberRankAltBanker) + { + ENCODE_LENGTH_EXACT(GuildMemberRank_Struct) + SETUP_DIRECT_ENCODE(GuildMemberRank_Struct, structs::GuildMemberRank_Struct) + + OUT(guild_id) + OUT(alt_banker) + OUT_str(player_name) + + switch (emu->rank_) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->rank_ = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->rank_ = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->rank_ = GUILD_LEADER_TI; + break; + } + } + + FINISH_ENCODE() + } + ENCODE(OP_GuildMemberList) { //consume the packet @@ -727,12 +873,34 @@ namespace Titanium str += sl + 1; \ } #define PutFieldN(field) e->field = htonl(emu_e->field) - + /* Translate new ranks to older values */ SlideStructString(name, emu_name); PutFieldN(level); PutFieldN(banker); PutFieldN(class_); - PutFieldN(rank); + switch (emu_e->rank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + e->rank = htonl(GUILD_MEMBER_TI); + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + e->rank = htonl(GUILD_OFFICER_TI); + break; + } + case GUILD_LEADER: { + e->rank = htonl(GUILD_LEADER_TI); + break; + } + default: { + e->rank = htonl(GUILD_MEMBER_TI); + break; + } + } PutFieldN(time_last_on); PutFieldN(tribute_enable); PutFieldN(total_tribute); @@ -753,6 +921,38 @@ namespace Titanium dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_SendGuildTributes) + { + ENCODE_LENGTH_ATLEAST(structs::GuildTributeAbility_Struct); + SETUP_VAR_ENCODE(GuildTributeAbility_Struct); + ALLOC_VAR_ENCODE(structs::GuildTributeAbility_Struct, sizeof(GuildTributeAbility_Struct) + strlen(emu->ability.name)); + + eq->guild_id = emu->guild_id;; + strncpy(eq->ability.name, emu->ability.name, strlen(emu->ability.name)); + eq->ability.tribute_id = emu->ability.tribute_id; + eq->ability.tier_count = emu->ability.tier_count; + for (int i = 0; i < ntohl(emu->ability.tier_count); i++) { + eq->ability.tiers[i].cost = emu->ability.tiers[i].cost; + eq->ability.tiers[i].level = emu->ability.tiers[i].level; + eq->ability.tiers[i].tribute_item_id = emu->ability.tiers[i].tribute_item_id; + } + FINISH_ENCODE(); + } + + ENCODE(OP_GuildTributeDonateItem) + { + SETUP_DIRECT_ENCODE(GuildTributeDonateItemReply_Struct, structs::GuildTributeDonateItemReply_Struct); + + Log(Logs::Detail, Logs::Netcode, "UF::ENCODE(OP_GuildTributeDonateItem)"); + + OUT(quantity); + OUT(favor); + eq->unknown8 = 0; + eq->slot = ServerToTitaniumSlot(emu->slot); + + FINISH_ENCODE(); + } + ENCODE(OP_Illusion) { ENCODE_LENGTH_EXACT(Illusion_Struct); @@ -867,11 +1067,60 @@ namespace Titanium ENCODE(OP_LFGuild) { + struct bit_mask_conversion { + uint32 titanium_mask; + uint32 rof2_mask; + }; + + std::vector bit_mask = { + {.titanium_mask = 2, .rof2_mask = 256}, + {.titanium_mask = 4, .rof2_mask = 32768}, + {.titanium_mask = 8, .rof2_mask = 65536}, + {.titanium_mask = 16, .rof2_mask = 4}, + {.titanium_mask = 32, .rof2_mask = 64}, + {.titanium_mask = 64, .rof2_mask = 16384}, + {.titanium_mask = 128, .rof2_mask = 8192}, + {.titanium_mask = 256, .rof2_mask = 128}, + {.titanium_mask = 512, .rof2_mask = 2048}, + {.titanium_mask = 1024, .rof2_mask = 8}, + {.titanium_mask = 2048, .rof2_mask = 16}, + {.titanium_mask = 4096, .rof2_mask = 512}, + {.titanium_mask = 8192, .rof2_mask = 32}, + {.titanium_mask = 16384, .rof2_mask = 1024}, + {.titanium_mask = 32768, .rof2_mask = 2}, + {.titanium_mask = 65536, .rof2_mask = 4096}, + }; + EQApplicationPacket *in = *p; - *p = nullptr; uint32 Command = in->ReadUInt32(); + if (Command == 1) { + ENCODE_LENGTH_EXACT(LFGuild_GuildToggle_Struct); + SETUP_DIRECT_ENCODE(LFGuild_GuildToggle_Struct, structs::LFGuild_GuildToggle_Struct); + + OUT(Command); + OUT_str(Comment); + OUT(FromLevel); + OUT(ToLevel); + OUT(AACount); + OUT(TimeZone); + OUT(Toggle); + OUT(TimePosted); + OUT_str(Name); + + uint32 emu_bitmask = emu->Classes; + uint32 ti_bitmask = 0; + for (auto const& b : bit_mask) { + (emu_bitmask & b.rof2_mask) != 0 ? ti_bitmask |= b.titanium_mask : ti_bitmask &= ~b.titanium_mask; + } + eq->Classes = ti_bitmask; + + FINISH_ENCODE(); + return; + } + + *p = nullptr; if (Command != 0) { dest->FastQueuePacket(&in, ack_req); @@ -1149,7 +1398,28 @@ namespace Titanium OUT(pvp); OUT(anon); OUT(gm); - OUT(guildrank); + switch (emu->guildrank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->guildrank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->guildrank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->guildrank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } OUT(guildbanker); // OUT(unknown13054[8]); OUT(exp); @@ -1250,7 +1520,7 @@ namespace Titanium { ENCODE_LENGTH_EXACT(MarkNPC_Struct); SETUP_DIRECT_ENCODE(MarkNPC_Struct, MarkNPC_Struct); - + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MarkNPC, sizeof(MarkNPC_Struct)); MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer; mnpcs->TargetID = emu->TargetID; @@ -1267,10 +1537,10 @@ namespace Titanium *p = nullptr; unsigned char* __emu_buffer = inapp->pBuffer; RaidGeneral_Struct* raid_gen = (RaidGeneral_Struct*)__emu_buffer; - + switch (raid_gen->action) { - case raidAdd: + case raidAdd: { RaidAddMember_Struct* emu = (RaidAddMember_Struct*)__emu_buffer; @@ -1340,7 +1610,7 @@ namespace Titanium dest->QueuePacket(inapp); break; } - default: + default: { RaidGeneral_Struct* emu = (RaidGeneral_Struct*)__emu_buffer; @@ -1864,7 +2134,28 @@ namespace Titanium eq->beard = emu->beard; strcpy(eq->suffix, emu->suffix); eq->petOwnerId = emu->petOwnerId; - eq->guildrank = emu->guildrank; + switch (emu->guildrank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->guildrank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->guildrank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->guildrank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } // eq->unknown0194[3] = emu->unknown0194[3]; for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; k++) { eq->equipment.Slot[k].Material = emu->equipment.Slot[k].Material; @@ -2205,6 +2496,34 @@ namespace Titanium FINISH_DIRECT_DECODE(); } + DECODE(OP_GuildDemote) + { + DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); + SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); + + memcpy(emu->name, eq->name, sizeof(emu->name)); + memcpy(emu->target, eq->target, sizeof(emu->target)); + emu->rank = GUILD_MEMBER; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildTributeDonateItem) + { + DECODE_LENGTH_EXACT(structs::GuildTributeDonateItemRequest_Struct); + SETUP_DIRECT_DECODE(GuildTributeDonateItemRequest_Struct, structs::GuildTributeDonateItemRequest_Struct); + + Log(Logs::Detail, Logs::Netcode, "UF::DECODE(OP_GuildTributeDonateItem)"); + + IN(quantity); + IN(tribute_master_id); + IN(guild_id); + + emu->slot = TitaniumToServerSlot(eq->slot); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_InspectAnswer) { DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); @@ -2260,8 +2579,77 @@ namespace Titanium DECODE(OP_LFGuild) { + struct bit_mask_conversion { + uint32 titanium_mask; + uint32 rof2_mask; + }; + + std::vector bit_mask = { + {.titanium_mask = 2, .rof2_mask = 256}, + {.titanium_mask = 4, .rof2_mask = 32768}, + {.titanium_mask = 8, .rof2_mask = 65536}, + {.titanium_mask = 16, .rof2_mask = 4}, + {.titanium_mask = 32, .rof2_mask = 64}, + {.titanium_mask = 64, .rof2_mask = 16384}, + {.titanium_mask = 128, .rof2_mask = 8192}, + {.titanium_mask = 256, .rof2_mask = 128}, + {.titanium_mask = 512, .rof2_mask = 2048}, + {.titanium_mask = 1024, .rof2_mask = 8}, + {.titanium_mask = 2048, .rof2_mask = 16}, + {.titanium_mask = 4096, .rof2_mask = 512}, + {.titanium_mask = 8192, .rof2_mask = 32}, + {.titanium_mask = 16384, .rof2_mask = 1024}, + {.titanium_mask = 32768, .rof2_mask = 2}, + {.titanium_mask = 65536, .rof2_mask = 4096}, + }; + uint32 Command = __packet->ReadUInt32(); + if (Command == 3) { + DECODE_LENGTH_EXACT(structs::LFGuild_SearchPlayer_Struct); + SETUP_DIRECT_DECODE(LFGuild_SearchPlayer_Struct, structs::LFGuild_SearchPlayer_Struct); + + IN(Command); + IN(Unknown04); + IN(FromLevel); + IN(ToLevel); + IN(MinAA); + IN(TimeZone); + + uint32 new_bitmask = 0; + uint32 ti_bitmask = eq->Classes; + for (auto const& b : bit_mask) { + (ti_bitmask & b.titanium_mask) != 0 ? new_bitmask |= b.rof2_mask : new_bitmask &= ~b.rof2_mask; + } + emu->Classes = new_bitmask; + FINISH_DIRECT_DECODE(); + return; + } + + if (Command == 1) { + DECODE_LENGTH_EXACT(structs::LFGuild_GuildToggle_Struct); + SETUP_DIRECT_DECODE(LFGuild_GuildToggle_Struct, structs::LFGuild_GuildToggle_Struct); + + IN(Command); + IN_str(Comment); + IN(FromLevel); + IN(ToLevel); + IN(AACount); + IN(TimeZone); + IN(Toggle); + IN(TimePosted); + IN_str(Name); + + uint32 new_bitmask = 0; + uint32 ti_bitmask = eq->Classes; + for (auto const& b : bit_mask) { + (ti_bitmask & b.titanium_mask) != 0 ? new_bitmask |= b.rof2_mask : new_bitmask &= ~b.rof2_mask; + } + emu->Classes = new_bitmask; + FINISH_DIRECT_DECODE(); + return; + } + if (Command != 0) return; @@ -2401,7 +2789,7 @@ namespace Titanium IN(general.parameter); IN_str(general.leader_name); IN_str(general.player_name); - + auto len = 0; if (__packet->size < sizeof(structs::RaidMOTD_Struct)) { len = __packet->size - sizeof(structs::RaidGeneral_Struct); @@ -2409,8 +2797,8 @@ namespace Titanium else { len = sizeof(eq->motd); } - - strn0cpy(emu->motd, eq->motd, len > 1024 ? 1024 : len); + + strn0cpy(emu->motd, eq->motd, len > 1024 ? 1024 : len); emu->motd[len - 1] = '\0'; FINISH_VAR_DECODE(); @@ -2428,7 +2816,7 @@ namespace Titanium FINISH_VAR_DECODE(); break; - } + } default: { SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index c5c774a17..a57e42942 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -44,8 +44,13 @@ E(OP_DzSetLeaderName) E(OP_Emote) E(OP_FormattedMessage) E(OP_GroundSpawn) +E(OP_SetGuildRank) E(OP_GuildMemberLevelUpdate) E(OP_GuildMemberList) +E(OP_GuildMemberAdd) +E(OP_GuildMemberRankAltBanker) +E(OP_SendGuildTributes) +E(OP_GuildTributeDonateItem) E(OP_Illusion) E(OP_InspectAnswer) E(OP_InspectRequest) @@ -69,6 +74,7 @@ E(OP_SendCharInfo) E(OP_SendAATable) E(OP_SetFace) E(OP_ShopPlayerSell) +E(OP_SpawnAppearance) E(OP_SpecialMesg) E(OP_TaskDescription) E(OP_Track) @@ -100,9 +106,11 @@ D(OP_DzRemovePlayer) D(OP_DzSwapPlayer) D(OP_Emote) D(OP_FaceChange) +D(OP_GuildDemote) D(OP_InspectAnswer) D(OP_InspectRequest) D(OP_ItemLinkClick) +D(OP_GuildTributeDonateItem) D(OP_LFGuild) D(OP_LoadSpellSet) D(OP_LootItem) diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index ae89fe385..61888c9df 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -1423,6 +1423,23 @@ struct GuildUpdate_Struct { GuildsListEntry_Struct entry; }; +struct GuildTributeDonateItemRequest_Struct { + /*000*/ uint32 slot; + /*004*/ uint32 quantity; + /*008*/ uint32 tribute_master_id; + /*012*/ uint32 unknown12; + /*016*/ uint32 guild_id; + /*020*/ uint32 unknown20; + /*024*/ uint32 unknown24; +}; + +struct GuildTributeDonateItemReply_Struct { + /*000*/ uint32 slot; + /*004*/ uint32 quantity; + /*008*/ uint32 unknown8; + /*012*/ uint32 favor; +}; + /* ** Money Loot ** Length: 22 Bytes @@ -3508,7 +3525,7 @@ struct LFGuild_GuildToggle_Struct /*540*/ uint32 TimeZone; /*544*/ uint8 Toggle; /*545*/ uint8 Unknown545[3]; -/*548*/ uint32 Expires; +/*548*/ uint32 TimePosted; /*552*/ char Name[64]; /*616*/ }; @@ -3699,6 +3716,34 @@ struct SayLinkBodyFrame_Struct { /*045*/ }; +struct GuildSetRank_Struct +{ + /*00*/ uint32 unknown00; + /*04*/ uint32 unknown04; + /*08*/ uint32 rank; + /*72*/ char member_name[64]; + /*76*/ uint32 banker; +}; + +struct GuildMemberAdd_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 unknown04; + /*008*/ uint32 level; + /*012*/ uint32 class_; + /*016*/ uint32 rank_; + /*020*/ uint32 zone_id; + /*024*/ uint32 last_on; + /*028*/ char player_name[64]; +}; + +struct GuildMemberRank_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 unknown_004; + /*008*/ uint32 rank_; + /*012*/ char player_name[64]; + /*076*/ uint32 alt_banker; //Banker/Alt bit 00 - none 10 - Alt 11 - Alt and Banker 01 - Banker. Banker not functional for RoF2+ +}; + }; /*structs*/ }; /*Titanium*/ diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 4a40203d0..67bcae1b0 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -36,6 +36,7 @@ #include "../classes.h" #include "../races.h" #include "../raid.h" +#include "../guilds.h" #include #include @@ -1134,6 +1135,29 @@ namespace UF PutFieldN(level); PutFieldN(banker); PutFieldN(class_); + //Translate older ranks to new values* / + switch (emu_e->rank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + emu_e->rank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + emu_e->rank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + emu_e->rank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } PutFieldN(rank); PutFieldN(time_last_on); PutFieldN(tribute_enable); @@ -1165,14 +1189,14 @@ namespace UF unsigned char *__emu_buffer = in->pBuffer; char *InBuffer = (char *)__emu_buffer; - uint32 HighestGuildID = 0; + uint32 actual_no_guilds = 0; for (unsigned int i = 0; i < NumberOfGuilds; ++i) { if (InBuffer[0]) { PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; + actual_no_guilds++; } InBuffer += 64; } @@ -1188,7 +1212,7 @@ namespace UF memset(OutBuffer, 0, 64); OutBuffer += 64; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, actual_no_guilds); for (unsigned int i = 0; i < NumberOfGuilds; ++i) { @@ -1206,6 +1230,103 @@ namespace UF dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_GuildMemberAdd) + { + ENCODE_LENGTH_EXACT(GuildMemberAdd_Struct) + SETUP_DIRECT_ENCODE(GuildMemberAdd_Struct, structs::GuildMemberAdd_Struct) + + OUT(guild_id) + OUT(level) + OUT(class_) + switch (emu->rank_) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->rank_ = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->rank_ = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->rank_ = GUILD_LEADER_TI; + break; + } + } + OUT(zone_id) + OUT(last_on) + OUT_str(player_name) + + FINISH_ENCODE() + } + + ENCODE(OP_GuildMemberRankAltBanker) + { + ENCODE_LENGTH_EXACT(GuildMemberRank_Struct) + SETUP_DIRECT_ENCODE(GuildMemberRank_Struct, structs::GuildMemberRank_Struct) + + OUT(guild_id) + OUT(alt_banker) + OUT_str(player_name) + + switch (emu->rank_) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->rank_ = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->rank_ = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->rank_ = GUILD_LEADER_TI; + break; + } + } + FINISH_ENCODE() + } + + ENCODE(OP_SendGuildTributes) + { + ENCODE_LENGTH_ATLEAST(structs::GuildTributeAbility_Struct) + SETUP_VAR_ENCODE(GuildTributeAbility_Struct) + ALLOC_VAR_ENCODE(structs::GuildTributeAbility_Struct, sizeof(GuildTributeAbility_Struct) + strlen(emu->ability.name)) + + eq->guild_id = emu->guild_id; + eq->ability.tribute_id = emu->ability.tribute_id; + eq->ability.tier_count = emu->ability.tier_count; + strncpy(eq->ability.name, emu->ability.name, strlen(emu->ability.name)); + for (int i = 0; i < ntohl(emu->ability.tier_count); i++) { + eq->ability.tiers[i].cost = emu->ability.tiers[i].cost; + eq->ability.tiers[i].level = emu->ability.tiers[i].level; + eq->ability.tiers[i].tribute_item_id = emu->ability.tiers[i].tribute_item_id; + } + FINISH_ENCODE() + } + + ENCODE(OP_GuildTributeDonateItem) + { + SETUP_DIRECT_ENCODE(GuildTributeDonateItemReply_Struct, structs::GuildTributeDonateItemReply_Struct); + + Log(Logs::Detail, Logs::Netcode, "UF::ENCODE(OP_GuildTributeDonateItem)"); + + OUT(quantity) + OUT(favor) + eq->unknown8 = 0; + eq->slot = ServerToUFSlot(emu->slot); + + FINISH_ENCODE() + } + ENCODE(OP_Illusion) { ENCODE_LENGTH_EXACT(Illusion_Struct); @@ -1829,6 +1950,29 @@ namespace UF OUT(pvp); OUT(anon); OUT(gm); + //Translate older ranks to new values* / + switch (emu->guildrank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + emu->guildrank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + emu->guildrank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + emu->guildrank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } OUT(guildrank); OUT(guildbanker); // OUT(unknown13054[12]); @@ -2332,29 +2476,83 @@ namespace UF ENCODE(OP_SpawnAppearance) { - EQApplicationPacket *in = *p; - *p = nullptr; + ENCODE_LENGTH_EXACT(SpawnAppearance_Struct); + SETUP_DIRECT_ENCODE(SpawnAppearance_Struct, structs::SpawnAppearance_Struct); - unsigned char *emu_buffer = in->pBuffer; - - SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; - - if (sas->type != AppearanceType::Size) - { - dest->FastQueuePacket(&in, ack_req); - return; + OUT(spawn_id); + OUT(type); + OUT(parameter); + switch (emu->type) { + case AppearanceType::GuildRank: { + //Translate new ranks to old values* / + switch (emu->parameter) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + eq->parameter = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + eq->parameter = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + eq->parameter = GUILD_LEADER_TI; + break; + } + } + break; + } + case AppearanceType::GuildShow: { + FAIL_ENCODE(); + return; + } + default: { + break; + } } - auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; + FINISH_ENCODE(); + } - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; + ENCODE(OP_SetGuildRank) + { + ENCODE_LENGTH_EXACT(GuildSetRank_Struct); + SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); - dest->FastQueuePacket(&outapp, ack_req); - delete in; + eq->unknown00 = 0; + eq->unknown04 = 0; + + switch (emu->rank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + emu->rank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + emu->rank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + emu->rank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } + + memcpy(eq->member_name, emu->member_name, sizeof(eq->member_name)); + OUT(banker); + + FINISH_ENCODE(); } ENCODE(OP_SpawnDoor) @@ -2945,6 +3143,29 @@ namespace UF else { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); + //Translate older ranks to new values* / + switch (emu->guildrank) { + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + emu->guildrank = GUILD_MEMBER_TI; + break; + } + case GUILD_OFFICER: + case GUILD_SENIOR_OFFICER: { + emu->guildrank = GUILD_OFFICER_TI; + break; + } + case GUILD_LEADER: { + emu->guildrank = GUILD_LEADER_TI; + break; + } + default: { + break; + } + } VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); } VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); @@ -3577,6 +3798,34 @@ namespace UF DECODE_FORWARD(OP_GroupInvite); } + DECODE(OP_GuildDemote) + { + DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); + SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); + + memcpy(emu->name, eq->name, sizeof(emu->name)); + memcpy(emu->target, eq->target, sizeof(emu->target)); + emu->rank = GUILD_MEMBER; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildTributeDonateItem) + { + DECODE_LENGTH_EXACT(structs::GuildTributeDonateItemRequest_Struct); + SETUP_DIRECT_DECODE(GuildTributeDonateItemRequest_Struct, structs::GuildTributeDonateItemRequest_Struct); + + Log(Logs::Detail, Logs::Netcode, "UF::DECODE(OP_GuildTributeDonateItem)"); + + IN(quantity); + IN(tribute_master_id); + IN(guild_id); + + emu->slot = UFToServerSlot(eq->slot); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_InspectRequest) { DECODE_LENGTH_EXACT(structs::Inspect_Struct); diff --git a/common/patches/uf_ops.h b/common/patches/uf_ops.h index 615aa787a..431456855 100644 --- a/common/patches/uf_ops.h +++ b/common/patches/uf_ops.h @@ -58,6 +58,10 @@ E(OP_GroupInvite) E(OP_GroupUpdate) E(OP_GuildMemberList) E(OP_GuildsList) +E(OP_GuildMemberAdd) +E(OP_SendGuildTributes) +E(OP_GuildMemberRankAltBanker) +E(OP_GuildTributeDonateItem) E(OP_Illusion) E(OP_InspectBuffs) E(OP_InspectRequest) @@ -82,6 +86,7 @@ E(OP_RespondAA) E(OP_SendAATable) E(OP_SendCharInfo) E(OP_SendZonepoints) +E(OP_SetGuildRank) E(OP_ShopPlayerBuy) E(OP_ShopPlayerSell) E(OP_SomeItemPacketMaybe) @@ -138,6 +143,8 @@ D(OP_GroupFollow) D(OP_GroupFollow2) D(OP_GroupInvite) D(OP_GroupInvite2) +D(OP_GuildDemote) +D(OP_GuildTributeDonateItem) D(OP_InspectRequest) D(OP_ItemLinkClick) D(OP_ItemVerifyRequest) diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index a009399e9..8c7dae5b5 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -1670,6 +1670,15 @@ struct Surname_Struct /*0100*/ }; +struct GuildSetRank_Struct +{ + /*00*/ uint32 unknown00; + /*04*/ uint32 unknown04; + /*08*/ uint32 rank; + /*72*/ char member_name[64]; + /*76*/ uint32 banker; +}; + struct GuildsListEntry_Struct { char name[64]; }; @@ -1685,6 +1694,27 @@ struct GuildUpdate_Struct { GuildsListEntry_Struct entry; }; +struct GuildMemberAdd_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 unknown04; + /*008*/ uint32 unknown08; + /*012*/ uint32 unknown12; + /*016*/ uint32 level; + /*020*/ uint32 class_; + /*024*/ uint32 rank_; + /*028*/ uint32 zone_id; + /*032*/ uint32 last_on; + /*036*/ char player_name[64]; +}; + +struct GuildMemberRank_Struct { + /*000*/ uint32 guild_id; + /*004*/ uint32 unknown_004; + /*008*/ uint32 rank_; + /*012*/ char player_name[64]; + /*076*/ uint32 alt_banker; //Banker/Alt bit 00 - none 10 - Alt 11 - Alt and Banker 01 - Banker. Banker not functional for RoF2+ +}; + /* ** Money Loot ** Length: 22 Bytes @@ -3067,6 +3097,23 @@ struct GuildMakeLeader{ char target[64]; }; +struct GuildTributeDonateItemRequest_Struct { +/*000*/ uint32 slot; +/*004*/ uint32 quantity; +/*008*/ uint32 tribute_master_id; +/*012*/ uint32 unknown12; +/*016*/ uint32 guild_id; +/*020*/ uint32 unknown20; +/*024*/ uint32 unknown24; +}; + +struct GuildTributeDonateItemReply_Struct { +/*000*/ uint32 slot; +/*004*/ uint32 quantity; +/*008*/ uint32 unknown8; +/*012*/ uint32 favor; +}; + struct Make_Pet_Struct { //Simple struct for getting pet info uint8 level; uint8 class_; diff --git a/common/repositories/base/base_guild_bank_repository.h b/common/repositories/base/base_guild_bank_repository.h new file mode 100644 index 000000000..bd359a77d --- /dev/null +++ b/common/repositories/base/base_guild_bank_repository.h @@ -0,0 +1,404 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_GUILD_BANK_REPOSITORY_H +#define EQEMU_BASE_GUILD_BANK_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + + +class BaseGuildBankRepository { +public: + struct GuildBank { + uint32_t id; + uint32_t guildid; + uint8_t area; + uint32_t slot; + uint32_t itemid; + uint32_t qty; + std::string donator; + uint8_t permissions; + std::string whofor; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "guildid", + "area", + "slot", + "itemid", + "qty", + "donator", + "permissions", + "whofor", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "guildid", + "area", + "slot", + "itemid", + "qty", + "donator", + "permissions", + "whofor", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("guild_bank"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static GuildBank NewEntity() + { + GuildBank e{}; + + e.id = 0; + e.guildid = 0; + e.area = 0; + e.slot = 0; + e.itemid = 0; + e.qty = 0; + e.donator = ""; + e.permissions = 0; + e.whofor = ""; + + return e; + } + + static GuildBank GetGuildBank( + const std::vector &guild_banks, + int guild_bank_id + ) + { + for (auto &guild_bank : guild_banks) { + if (guild_bank.id == guild_bank_id) { + return guild_bank; + } + } + + return NewEntity(); + } + + static GuildBank FindOne( + Database& db, + int guild_bank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + guild_bank_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + GuildBank e{}; + + e.id = static_cast(strtoul(row[0], nullptr, 10)); + e.guildid = static_cast(strtoul(row[1], nullptr, 10)); + e.area = static_cast(strtoul(row[2], nullptr, 10)); + e.slot = static_cast(strtoul(row[3], nullptr, 10)); + e.itemid = static_cast(strtoul(row[4], nullptr, 10)); + e.qty = static_cast(strtoul(row[5], nullptr, 10)); + e.donator = row[6] ? row[6] : ""; + e.permissions = static_cast(strtoul(row[7], nullptr, 10)); + e.whofor = row[8] ? row[8] : ""; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int guild_bank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + guild_bank_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const GuildBank &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.guildid)); + v.push_back(columns[2] + " = " + std::to_string(e.area)); + v.push_back(columns[3] + " = " + std::to_string(e.slot)); + v.push_back(columns[4] + " = " + std::to_string(e.itemid)); + v.push_back(columns[5] + " = " + std::to_string(e.qty)); + v.push_back(columns[6] + " = '" + Strings::Escape(e.donator) + "'"); + v.push_back(columns[7] + " = " + std::to_string(e.permissions)); + v.push_back(columns[8] + " = '" + Strings::Escape(e.whofor) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static GuildBank InsertOne( + Database& db, + GuildBank e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.guildid)); + v.push_back(std::to_string(e.area)); + v.push_back(std::to_string(e.slot)); + v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.qty)); + v.push_back("'" + Strings::Escape(e.donator) + "'"); + v.push_back(std::to_string(e.permissions)); + v.push_back("'" + Strings::Escape(e.whofor) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.id = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.guildid)); + v.push_back(std::to_string(e.area)); + v.push_back(std::to_string(e.slot)); + v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.qty)); + v.push_back("'" + Strings::Escape(e.donator) + "'"); + v.push_back(std::to_string(e.permissions)); + v.push_back("'" + Strings::Escape(e.whofor) + "'"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildBank e{}; + + e.id = static_cast(strtoul(row[0], nullptr, 10)); + e.guildid = static_cast(strtoul(row[1], nullptr, 10)); + e.area = static_cast(strtoul(row[2], nullptr, 10)); + e.slot = static_cast(strtoul(row[3], nullptr, 10)); + e.itemid = static_cast(strtoul(row[4], nullptr, 10)); + e.qty = static_cast(strtoul(row[5], nullptr, 10)); + e.donator = row[6] ? row[6] : ""; + e.permissions = static_cast(strtoul(row[7], nullptr, 10)); + e.whofor = row[8] ? row[8] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildBank e{}; + + e.id = static_cast(strtoul(row[0], nullptr, 10)); + e.guildid = static_cast(strtoul(row[1], nullptr, 10)); + e.area = static_cast(strtoul(row[2], nullptr, 10)); + e.slot = static_cast(strtoul(row[3], nullptr, 10)); + e.itemid = static_cast(strtoul(row[4], nullptr, 10)); + e.qty = static_cast(strtoul(row[5], nullptr, 10)); + e.donator = row[6] ? row[6] : ""; + e.permissions = static_cast(strtoul(row[7], nullptr, 10)); + e.whofor = row[8] ? row[8] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + +}; + +#endif //EQEMU_BASE_GUILD_BANK_REPOSITORY_H diff --git a/common/repositories/base/base_guild_permissions_repository.h b/common/repositories/base/base_guild_permissions_repository.h new file mode 100644 index 000000000..57bffb677 --- /dev/null +++ b/common/repositories/base/base_guild_permissions_repository.h @@ -0,0 +1,416 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_GUILD_PERMISSIONS_REPOSITORY_H +#define EQEMU_BASE_GUILD_PERMISSIONS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + + +class BaseGuildPermissionsRepository { +public: + struct GuildPermissions { + int32_t id; + int32_t perm_id; + int32_t guild_id; + int32_t permission; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "perm_id", + "guild_id", + "permission", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "perm_id", + "guild_id", + "permission", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("guild_permissions"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static GuildPermissions NewEntity() + { + GuildPermissions e{}; + + e.id = 0; + e.perm_id = 0; + e.guild_id = 0; + e.permission = 0; + + return e; + } + + static GuildPermissions GetGuildPermissions( + const std::vector &guild_permissionss, + int guild_permissions_id + ) + { + for (auto &guild_permissions : guild_permissionss) { + if (guild_permissions.id == guild_permissions_id) { + return guild_permissions; + } + } + + return NewEntity(); + } + + static GuildPermissions FindOne( + Database& db, + int guild_permissions_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + guild_permissions_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + GuildPermissions e{}; + + e.id = static_cast(atoi(row[0])); + e.perm_id = static_cast(atoi(row[1])); + e.guild_id = static_cast(atoi(row[2])); + e.permission = static_cast(atoi(row[3])); + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int guild_permissions_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + guild_permissions_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const GuildPermissions &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.perm_id)); + v.push_back(columns[2] + " = " + std::to_string(e.guild_id)); + v.push_back(columns[3] + " = " + std::to_string(e.permission)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static GuildPermissions InsertOne( + Database& db, + GuildPermissions e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.perm_id)); + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.permission)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.id = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.perm_id)); + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.permission)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildPermissions e{}; + + e.id = static_cast(atoi(row[0])); + e.perm_id = static_cast(atoi(row[1])); + e.guild_id = static_cast(atoi(row[2])); + e.permission = static_cast(atoi(row[3])); + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildPermissions e{}; + + e.id = static_cast(atoi(row[0])); + e.perm_id = static_cast(atoi(row[1])); + e.guild_id = static_cast(atoi(row[2])); + e.permission = static_cast(atoi(row[3])); + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const GuildPermissions &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.perm_id)); + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.permission)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.perm_id)); + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.permission)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_GUILD_PERMISSIONS_REPOSITORY_H diff --git a/common/repositories/base/base_guild_tributes_repository.h b/common/repositories/base/base_guild_tributes_repository.h new file mode 100644 index 000000000..be74e12b6 --- /dev/null +++ b/common/repositories/base/base_guild_tributes_repository.h @@ -0,0 +1,453 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_GUILD_TRIBUTES_REPOSITORY_H +#define EQEMU_BASE_GUILD_TRIBUTES_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + + +class BaseGuildTributesRepository { +public: + struct GuildTributes { + uint32_t guild_id; + uint32_t tribute_id_1; + uint32_t tribute_id_1_tier; + uint32_t tribute_id_2; + uint32_t tribute_id_2_tier; + uint32_t time_remaining; + uint32_t enabled; + }; + + static std::string PrimaryKey() + { + return std::string("guild_id"); + } + + static std::vector Columns() + { + return { + "guild_id", + "tribute_id_1", + "tribute_id_1_tier", + "tribute_id_2", + "tribute_id_2_tier", + "time_remaining", + "enabled", + }; + } + + static std::vector SelectColumns() + { + return { + "guild_id", + "tribute_id_1", + "tribute_id_1_tier", + "tribute_id_2", + "tribute_id_2_tier", + "time_remaining", + "enabled", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("guild_tributes"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static GuildTributes NewEntity() + { + GuildTributes e{}; + + e.guild_id = 0; + e.tribute_id_1 = 0; + e.tribute_id_1_tier = 0; + e.tribute_id_2 = 0; + e.tribute_id_2_tier = 0; + e.time_remaining = 0; + e.enabled = 0; + + return e; + } + + static GuildTributes GetGuildTributes( + const std::vector &guild_tributess, + int guild_tributes_id + ) + { + for (auto &guild_tributes : guild_tributess) { + if (guild_tributes.guild_id == guild_tributes_id) { + return guild_tributes; + } + } + + return NewEntity(); + } + + static GuildTributes FindOne( + Database& db, + int guild_tributes_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + guild_tributes_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + GuildTributes e{}; + + e.guild_id = static_cast(strtoul(row[0], nullptr, 10)); + e.tribute_id_1 = static_cast(strtoul(row[1], nullptr, 10)); + e.tribute_id_1_tier = static_cast(strtoul(row[2], nullptr, 10)); + e.tribute_id_2 = static_cast(strtoul(row[3], nullptr, 10)); + e.tribute_id_2_tier = static_cast(strtoul(row[4], nullptr, 10)); + e.time_remaining = static_cast(strtoul(row[5], nullptr, 10)); + e.enabled = static_cast(strtoul(row[6], nullptr, 10)); + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int guild_tributes_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + guild_tributes_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const GuildTributes &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.guild_id)); + v.push_back(columns[1] + " = " + std::to_string(e.tribute_id_1)); + v.push_back(columns[2] + " = " + std::to_string(e.tribute_id_1_tier)); + v.push_back(columns[3] + " = " + std::to_string(e.tribute_id_2)); + v.push_back(columns[4] + " = " + std::to_string(e.tribute_id_2_tier)); + v.push_back(columns[5] + " = " + std::to_string(e.time_remaining)); + v.push_back(columns[6] + " = " + std::to_string(e.enabled)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.guild_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static GuildTributes InsertOne( + Database& db, + GuildTributes e + ) + { + std::vector v; + + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.tribute_id_1)); + v.push_back(std::to_string(e.tribute_id_1_tier)); + v.push_back(std::to_string(e.tribute_id_2)); + v.push_back(std::to_string(e.tribute_id_2_tier)); + v.push_back(std::to_string(e.time_remaining)); + v.push_back(std::to_string(e.enabled)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.guild_id = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.tribute_id_1)); + v.push_back(std::to_string(e.tribute_id_1_tier)); + v.push_back(std::to_string(e.tribute_id_2)); + v.push_back(std::to_string(e.tribute_id_2_tier)); + v.push_back(std::to_string(e.time_remaining)); + v.push_back(std::to_string(e.enabled)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildTributes e{}; + + e.guild_id = static_cast(strtoul(row[0], nullptr, 10)); + e.tribute_id_1 = static_cast(strtoul(row[1], nullptr, 10)); + e.tribute_id_1_tier = static_cast(strtoul(row[2], nullptr, 10)); + e.tribute_id_2 = static_cast(strtoul(row[3], nullptr, 10)); + e.tribute_id_2_tier = static_cast(strtoul(row[4], nullptr, 10)); + e.time_remaining = static_cast(strtoul(row[5], nullptr, 10)); + e.enabled = static_cast(strtoul(row[6], nullptr, 10)); + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildTributes e{}; + + e.guild_id = static_cast(strtoul(row[0], nullptr, 10)); + e.tribute_id_1 = static_cast(strtoul(row[1], nullptr, 10)); + e.tribute_id_1_tier = static_cast(strtoul(row[2], nullptr, 10)); + e.tribute_id_2 = static_cast(strtoul(row[3], nullptr, 10)); + e.tribute_id_2_tier = static_cast(strtoul(row[4], nullptr, 10)); + e.time_remaining = static_cast(strtoul(row[5], nullptr, 10)); + e.enabled = static_cast(strtoul(row[6], nullptr, 10)); + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const GuildTributes &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.tribute_id_1)); + v.push_back(std::to_string(e.tribute_id_1_tier)); + v.push_back(std::to_string(e.tribute_id_2)); + v.push_back(std::to_string(e.tribute_id_2_tier)); + v.push_back(std::to_string(e.time_remaining)); + v.push_back(std::to_string(e.enabled)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.guild_id)); + v.push_back(std::to_string(e.tribute_id_1)); + v.push_back(std::to_string(e.tribute_id_1_tier)); + v.push_back(std::to_string(e.tribute_id_2)); + v.push_back(std::to_string(e.tribute_id_2_tier)); + v.push_back(std::to_string(e.time_remaining)); + v.push_back(std::to_string(e.enabled)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_GUILD_TRIBUTES_REPOSITORY_H diff --git a/common/repositories/base/base_guilds_repository.h b/common/repositories/base/base_guilds_repository.h index 0a5387879..61745b14f 100644 --- a/common/repositories/base/base_guilds_repository.h +++ b/common/repositories/base/base_guilds_repository.h @@ -16,6 +16,7 @@ #include "../../strings.h" #include + class BaseGuildsRepository { public: struct Guilds { diff --git a/common/repositories/guild_bank_repository.h b/common/repositories/guild_bank_repository.h index 502844804..a24aa2c1c 100644 --- a/common/repositories/guild_bank_repository.h +++ b/common/repositories/guild_bank_repository.h @@ -3,286 +3,47 @@ #include "../database.h" #include "../strings.h" +#include "base/base_guild_bank_repository.h" -class GuildBankRepository { +class GuildBankRepository: public BaseGuildBankRepository { public: - struct GuildBank { - int guildid; - int8 area; - int slot; - int itemid; - int qty; - std::string donator; - int8 permissions; - std::string whofor; - }; - static std::string PrimaryKey() - { - return std::string(""); - } + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * GuildBankRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * GuildBankRepository::GetWhereNeverExpires() + * GuildBankRepository::GetWhereXAndY() + * GuildBankRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ - static std::vector Columns() - { - return { - "guildid", - "area", - "slot", - "itemid", - "qty", - "donator", - "permissions", - "whofor", - }; - } - - static std::string ColumnsRaw() - { - return std::string(Strings::Implode(", ", Columns())); - } - - static std::string InsertColumnsRaw() - { - std::vector insert_columns; - - for (auto &column : Columns()) { - if (column == PrimaryKey()) { - continue; - } - - insert_columns.push_back(column); - } - - return std::string(Strings::Implode(", ", insert_columns)); - } - - static std::string TableName() - { - return std::string("guild_bank"); - } - - static std::string BaseSelect() - { - return fmt::format( - "SELECT {} FROM {}", - ColumnsRaw(), - TableName() - ); - } - - static std::string BaseInsert() - { - return fmt::format( - "INSERT INTO {} ({}) ", - TableName(), - InsertColumnsRaw() - ); - } - - static GuildBank NewEntity() - { - GuildBank entry{}; - - entry.guildid = 0; - entry.area = 0; - entry.slot = 0; - entry.itemid = 0; - entry.qty = 0; - entry.donator = 0; - entry.permissions = 0; - entry.whofor = 0; - - return entry; - } - - static GuildBank GetGuildBankEntry( - const std::vector &guild_banks, - int guild_bank_id - ) - { - for (auto &guild_bank : guild_banks) { - if (guild_bank. == guild_bank_id) { - return guild_bank; - } - } - - return NewEntity(); - } - - static GuildBank FindOne( - int guild_bank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "{} WHERE id = {} LIMIT 1", - BaseSelect(), - guild_bank_id - ) - ); - - auto row = results.begin(); - if (results.RowCount() == 1) { - GuildBank entry{}; - - entry.guildid = atoi(row[0]); - entry.area = atoi(row[1]); - entry.slot = atoi(row[2]); - entry.itemid = atoi(row[3]); - entry.qty = atoi(row[4]); - entry.donator = row[5]; - entry.permissions = atoi(row[6]); - entry.whofor = row[7]; - - return entry; - } - - return NewEntity(); - } - - static int DeleteOne( - int guild_bank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "DELETE FROM {} WHERE {} = {}", - TableName(), - PrimaryKey(), - guild_bank_id - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int UpdateOne( - GuildBank guild_bank_entry - ) - { - std::vector update_values; - - auto columns = Columns(); - - update_values.push_back(columns[0] + " = " + std::to_string(guild_bank_entry.guildid)); - update_values.push_back(columns[1] + " = " + std::to_string(guild_bank_entry.area)); - update_values.push_back(columns[2] + " = " + std::to_string(guild_bank_entry.slot)); - update_values.push_back(columns[3] + " = " + std::to_string(guild_bank_entry.itemid)); - update_values.push_back(columns[4] + " = " + std::to_string(guild_bank_entry.qty)); - update_values.push_back(columns[5] + " = '" + Strings::Escape(guild_bank_entry.donator) + "'"); - update_values.push_back(columns[6] + " = " + std::to_string(guild_bank_entry.permissions)); - update_values.push_back(columns[7] + " = '" + Strings::Escape(guild_bank_entry.whofor) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "UPDATE {} SET {} WHERE {} = {}", - TableName(), - Strings::Implode(", ", update_values), - PrimaryKey(), - guild_bank_entry. - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static GuildBank InsertOne( - GuildBank guild_bank_entry - ) - { - std::vector insert_values; - - insert_values.push_back(std::to_string(guild_bank_entry.guildid)); - insert_values.push_back(std::to_string(guild_bank_entry.area)); - insert_values.push_back(std::to_string(guild_bank_entry.slot)); - insert_values.push_back(std::to_string(guild_bank_entry.itemid)); - insert_values.push_back(std::to_string(guild_bank_entry.qty)); - insert_values.push_back("'" + Strings::Escape(guild_bank_entry.donator) + "'"); - insert_values.push_back(std::to_string(guild_bank_entry.permissions)); - insert_values.push_back("'" + Strings::Escape(guild_bank_entry.whofor) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "{} VALUES ({})", - BaseInsert(), - Strings::Implode(",", insert_values) - ) - ); - - if (results.Success()) { - guild_bank_entry.id = results.LastInsertedID(); - return guild_bank_entry; - } - - guild_bank_entry = InstanceListRepository::NewEntity(); - - return guild_bank_entry; - } - - static int InsertMany( - std::vector guild_bank_entries - ) - { - std::vector insert_chunks; - - for (auto &guild_bank_entry: guild_bank_entries) { - std::vector insert_values; - - insert_values.push_back(std::to_string(guild_bank_entry.guildid)); - insert_values.push_back(std::to_string(guild_bank_entry.area)); - insert_values.push_back(std::to_string(guild_bank_entry.slot)); - insert_values.push_back(std::to_string(guild_bank_entry.itemid)); - insert_values.push_back(std::to_string(guild_bank_entry.qty)); - insert_values.push_back("'" + Strings::Escape(guild_bank_entry.donator) + "'"); - insert_values.push_back(std::to_string(guild_bank_entry.permissions)); - insert_values.push_back("'" + Strings::Escape(guild_bank_entry.whofor) + "'"); - - insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")"); - } - - std::vector insert_values; - - auto results = database.QueryDatabase( - fmt::format( - "{} VALUES {}", - BaseInsert(), - Strings::Implode(",", insert_chunks) - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static std::vector All() - { - std::vector all_entries; - - auto results = database.QueryDatabase( - fmt::format( - "{}", - BaseSelect() - ) - ); - - all_entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) { - GuildBank entry{}; - - entry.guildid = atoi(row[0]); - entry.area = atoi(row[1]); - entry.slot = atoi(row[2]); - entry.itemid = atoi(row[3]); - entry.qty = atoi(row[4]); - entry.donator = row[5]; - entry.permissions = atoi(row[6]); - entry.whofor = row[7]; - - all_entries.push_back(entry); - } - - return all_entries; - } + // Custom extended repository methods here }; diff --git a/common/repositories/guild_members_repository.h b/common/repositories/guild_members_repository.h new file mode 100644 index 000000000..9d1133f63 --- /dev/null +++ b/common/repositories/guild_members_repository.h @@ -0,0 +1,194 @@ +#ifndef EQEMU_GUILD_MEMBERS_REPOSITORY_H +#define EQEMU_GUILD_MEMBERS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_guild_members_repository.h" + +class GuildMembersRepository : public BaseGuildMembersRepository { +public: + + struct GuildMembersWithTributeOnStruct { + int32_t char_id; + uint32_t guild_id; + uint8_t tribute_enable; + std::string char_name; + uint32_t char_level; + }; + + struct GuildMembershipStatsStruct { + uint32 leaders; + uint32 senior_officers; + uint32 officers; + uint32 senior_members; + uint32 members; + uint32 junior_members; + uint32 initates; + uint32 recruits; + uint32 tribute_enabled; + }; + + static int UpdateMemberRank(Database &db, uint32 char_id, uint32 rank_id) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = '{}'", char_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.rank_ = rank_id; + + return UpdateOne(db, m); + } + + static int UpdateEnabled(Database &db, uint32 guild_id, uint32 char_id, uint32 enabled) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = '{}' AND guild_id = '{}'", char_id, guild_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.tribute_enable = enabled ? 1 : 0; + + return UpdateOne(db, m); + } + + static std::vector GetMembersWithTributeOn(Database &db, uint32 guild_id) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "SELECT gm.char_id, gm.guild_id, gm.tribute_enable, cd.`name`, cd.`level`, cd.deleted_at " + "FROM guild_members gm JOIN character_data cd ON cd.id = gm.char_id " + "WHERE ISNULL(cd.deleted_at) AND gm.tribute_enable = 1 AND gm.guild_id = '{}';", + guild_id + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + GuildMembersWithTributeOnStruct e{}; + + e.char_id = static_cast(atoi(row[0])); + e.guild_id = static_cast(strtoul(row[1], nullptr, 10)); + e.tribute_enable = static_cast(strtoul(row[2], nullptr, 10)); + e.char_name = row[3] ? row[3] : ""; + e.char_level = static_cast(strtoul(row[4], nullptr, 10)); + + all_entries.push_back(e); + } + + return all_entries; + } + + static int UpdateFavor(Database &db, uint32 guild_id, uint32 char_id, uint32 favor) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = '{}' AND guild_id = '{}'", char_id, guild_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.total_tribute = favor; + m.last_tribute = time(nullptr); + + return UpdateOne(db, m); + } + + static int UpdateOnline(Database &db, uint32 char_id, bool status) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = '{}'", char_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.online = status ? 1 : 0; + + return UpdateOne(db, m); + } + + static int UpdateNote(Database &db, uint32 char_id, std::string &public_note) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = '{}'", char_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.public_note = public_note; + + return UpdateOne(db, m); + } + + static GuildMembershipStatsStruct GetGuildMembershipStats(Database &db, uint32 guild_id) + { + std::string query = fmt::format( + "SELECT " + "SUM(CASE WHEN gm.`rank` = 1 THEN 1 ELSE 0 END) AS RANK1, " + "SUM(CASE WHEN gm.`rank` = 2 THEN 1 ELSE 0 END) AS RANK2, " + "SUM(CASE WHEN gm.`rank` = 3 THEN 1 ELSE 0 END) AS RANK3, " + "SUM(CASE WHEN gm.`rank` = 4 THEN 1 ELSE 0 END) AS RANK4, " + "SUM(CASE WHEN gm.`rank` = 5 THEN 1 ELSE 0 END) AS RANK5, " + "SUM(CASE WHEN gm.`rank` = 6 THEN 1 ELSE 0 END) AS RANK6, " + "SUM(CASE WHEN gm.`rank` = 7 THEN 1 ELSE 0 END) AS RANK7, " + "SUM(CASE WHEN gm.`rank` = 8 THEN 1 ELSE 0 END) AS RANK8, " + "SUM(CASE WHEN gm.tribute_enable = 1 THEN 1 ELSE 0 END) AS TRIBUTE " + "FROM guild_members gm " + "WHERE gm.guild_id = '{}';", + guild_id + ); + + GuildMembershipStatsStruct gmss{}; + + auto results = db.QueryDatabase(query); + if (!results.Success()) { + return gmss; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + + gmss.leaders = static_cast(Strings::ToUnsignedInt(row[0])); + gmss.senior_officers = static_cast(Strings::ToUnsignedInt(row[1])); + gmss.officers = static_cast(Strings::ToUnsignedInt(row[2])); + gmss.senior_members = static_cast(Strings::ToUnsignedInt(row[3])); + gmss.members = static_cast(Strings::ToUnsignedInt(row[4])); + gmss.junior_members = static_cast(Strings::ToUnsignedInt(row[5])); + gmss.initates = static_cast(Strings::ToUnsignedInt(row[6])); + gmss.recruits = static_cast(Strings::ToUnsignedInt(row[7])); + gmss.tribute_enabled = static_cast(Strings::ToUnsignedInt(row[8])); + } + + return gmss; + } + + static int UpdateBankerFlag(Database &db, uint32 char_id, bool status) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = {}", char_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.banker = status ? 1 : 0; + + return UpdateOne(db, m); + } + + static int UpdateAltFlag(Database &db, uint32 char_id, bool status) + { + const auto guild_members = GetWhere(db, fmt::format("char_id = {}", char_id)); + if (guild_members.empty()) { + return 0; + } + + auto m = guild_members[0]; + m.alt = status ? 1 : 0; + + return UpdateOne(db, m); + } +}; +#endif //EQEMU_GUILD_MEMBERS_REPOSITORY_H diff --git a/common/repositories/guild_permissions_repository.h b/common/repositories/guild_permissions_repository.h new file mode 100644 index 000000000..35c1c3da3 --- /dev/null +++ b/common/repositories/guild_permissions_repository.h @@ -0,0 +1,50 @@ +#ifndef EQEMU_GUILD_PERMISSIONS_REPOSITORY_H +#define EQEMU_GUILD_PERMISSIONS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_guild_permissions_repository.h" + +class GuildPermissionsRepository: public BaseGuildPermissionsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * GuildPermissionsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * GuildPermissionsRepository::GetWhereNeverExpires() + * GuildPermissionsRepository::GetWhereXAndY() + * GuildPermissionsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_GUILD_PERMISSIONS_REPOSITORY_H diff --git a/common/repositories/guild_ranks_repository.h b/common/repositories/guild_ranks_repository.h index 1a5a00455..8cfe0118d 100644 --- a/common/repositories/guild_ranks_repository.h +++ b/common/repositories/guild_ranks_repository.h @@ -45,6 +45,21 @@ public: // Custom extended repository methods here + static int UpdateTitle(Database &db, uint32 guild_id, uint32 rank, std::string title) + { + auto guild_rank = GetWhere(db, fmt::format("guild_id = '{}' AND rank = '{}'", guild_id, rank)); + if (guild_rank.empty()) { + return 0; + } + + auto r = guild_rank[0]; + r.title = title; + + DeleteWhere(db, fmt::format("guild_id = '{}' AND rank = '{}'", guild_id, rank)); + InsertOne(db, r); + + return 1; + } }; #endif //EQEMU_GUILD_RANKS_REPOSITORY_H diff --git a/common/repositories/guild_tributes_repository.h b/common/repositories/guild_tributes_repository.h new file mode 100644 index 000000000..a4db8cd1b --- /dev/null +++ b/common/repositories/guild_tributes_repository.h @@ -0,0 +1,77 @@ +#ifndef EQEMU_GUILD_TRIBUTES_REPOSITORY_H +#define EQEMU_GUILD_TRIBUTES_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_guild_tributes_repository.h" + +class GuildTributesRepository: public BaseGuildTributesRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * GuildTributesRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * GuildTributesRepository::GetWhereNeverExpires() + * GuildTributesRepository::GetWhereXAndY() + * GuildTributesRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + static int UpdateEnabled(Database& db, uint32 guild_id, uint32 enabled) + { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET `enabled` = '{}' WHERE `guild_id` = {}", + TableName(), + enabled, + guild_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateTimeRemaining(Database& db, uint32 guild_id, uint32 time_remaining) + { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET `time_remaining` = '{}' WHERE `guild_id` = {}", + TableName(), + time_remaining, + guild_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_GUILD_TRIBUTES_REPOSITORY_H diff --git a/common/repositories/guilds_repository.h b/common/repositories/guilds_repository.h index b55510156..83262e33e 100644 --- a/common/repositories/guilds_repository.h +++ b/common/repositories/guilds_repository.h @@ -5,46 +5,58 @@ #include "../strings.h" #include "base/base_guilds_repository.h" -class GuildsRepository: public BaseGuildsRepository { +class GuildsRepository : public BaseGuildsRepository { public: - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * GuildsRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * GuildsRepository::GetWhereNeverExpires() - * GuildsRepository::GetWhereXAndY() - * GuildsRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * GuildsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * GuildsRepository::GetWhereNeverExpires() + * GuildsRepository::GetWhereXAndY() + * GuildsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ - // Custom extended repository methods here + // Custom extended repository methods here + static int UpdateFavor(Database &db, uint32 guild_id, uint32 favor) + { + auto const guild = GetWhere(db, fmt::format("id = '{}'", guild_id)); + if (guild.empty()) { + return 0; + } + + auto g = guild[0]; + g.favor = favor; + + return UpdateOne(db, g); + } }; #endif //EQEMU_GUILDS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 9586434f6..838319b49 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -245,6 +245,10 @@ RULE_INT(Guild, PlayerCreationLimit, 1, "Only allow use of the UF+ window if the RULE_INT(Guild, PlayerCreationRequiredStatus, 0, "Required status to create a guild") RULE_INT(Guild, PlayerCreationRequiredLevel, 0, "Required level of the player attempting to create the guild") RULE_INT(Guild, PlayerCreationRequiredTime, 0, "Time needed online on the account to create a guild (in minutes)") +RULE_INT(Guild, TributeTime, 600000, "Time in ms for guild tributes. Default is 10 mins.") +RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.") +RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.") +RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.") RULE_CATEGORY_END() RULE_CATEGORY(Skills) diff --git a/common/servertalk.h b/common/servertalk.h index 1b0d98623..7eedbe893 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -5,6 +5,7 @@ #include "../common/packet_functions.h" #include "../common/eq_packet_structs.h" #include "../common/net/packet.h" +#include "../common/guilds.h" #include #include #include @@ -94,6 +95,23 @@ #define ServerOP_WebInterfaceEvent 0x0066 #define ServerOP_WebInterfaceSubscribe 0x0067 #define ServerOP_WebInterfaceUnsubscribe 0x0068 +#define ServerOP_GuildPermissionUpdate 0x0069 +#define ServerOP_GuildTributeUpdate 0x0070 +#define ServerOP_GuildRankNameChange 0x0071 +#define ServerOP_GuildTributeActivate 0x0072 +#define ServerOP_GuildTributeOptInToggle 0x0073 +#define ServerOP_GuildTributeFavAndTimer 0x0074 +#define ServerOP_RequestGuildActiveTributes 0x0075 +#define ServerOP_RequestGuildFavorAndTimer 0x0076 +#define ServerOP_GuildTributeUpdateDonations 0x0077 +#define ServerOP_GuildMemberLevelUpdate 0x0078 +#define ServerOP_GuildMemberPublicNote 0x0079 +#define ServerOP_GuildMemberRemove 0x007A +#define ServerOP_GuildMemberAdd 0x007B +#define ServerOP_GuildChannel 0x007C +#define ServerOP_GuildURL 0x007D +#define ServerOP_GuildSendGuildList 0x007E +#define ServerOP_GuildMembersList 0x007F #define ServerOP_RaidAdd 0x0100 //in use #define ServerOP_RaidRemove 0x0101 //in use @@ -567,6 +585,8 @@ struct ServerClientList_Struct { uint8 anon; bool tellsoff; uint32 guild_id; + uint32 guild_rank; + bool guild_tribute_opt_in; bool LFG; uint8 gm; uint8 ClientVersion; @@ -1006,19 +1026,34 @@ struct ServerGuildCharRefresh_Struct { uint32 char_id; }; -struct ServerGuildRankUpdate_Struct -{ - uint32 GuildID; - char MemberName[64]; - uint32 Rank; - uint32 Banker; +struct ServerGuildRankUpdate_Struct { + uint32 guild_id; + char member_name[64]; + uint32 rank; + uint32 banker; + uint32 alt; + bool no_update; }; struct ServerGuildMemberUpdate_Struct { - uint32 GuildID; - char MemberName[64]; - uint32 ZoneID; - uint32 LastSeen; + uint32 guild_id; + char member_name[64]; + uint32 zone_id; + uint32 last_seen; +}; + +struct ServerGuildPermissionUpdate_Struct { + uint32 guild_id; + char member_name[64]; + uint32 rank; + uint32 function_id; + uint32 function_value; +}; + +struct ServerGuildRankNameChange { + uint32 guild_id; + uint32 rank; + char rank_name[51]; //RoF2 has 51 max. }; struct SpawnPlayerCorpse_Struct { @@ -1857,6 +1892,47 @@ struct ServerZoneStatus_Struct { int16 admin; }; +struct GuildTributeUpdate { + uint32 guild_id; + uint32 tribute_id_1; + uint32 tribute_id_2; + uint32 tribute_id_1_tier; + uint32 tribute_id_2_tier; + uint32 enabled; + uint32 favor; + uint32 time_remaining; + uint32 member_favor; + uint32 member_time; + uint32 member_enabled; + char player_name[64]; +}; + +struct GuildTributeMemberToggle { + uint32 guild_id; + uint32 char_id; + char player_name[64]; + uint32 tribute_toggle; + uint32 command; + uint32 time_remaining; + uint32 no_donations; + uint32 member_last_donated; +}; + +struct ServerOP_GuildMessage_Struct { + uint32 guild_id{GUILD_NONE}; + char player_name[64]{0}; + uint32 player_level{0}; + uint32 player_class{0}; + uint32 player_rank{GUILD_RANK_NONE}; + uint32 player_zone_id{0}; + bool player_offline{false}; + char player_new_name[64]{0}; + char new_guild_name[64]{0}; + char note[256]{0}; + char channel[2048]{0}; + char url[2048]{0}; +}; + #pragma pack() #endif diff --git a/common/version.h b/common/version.h index 1259aa618..f28b693b5 100644 --- a/common/version.h +++ b/common/version.h @@ -42,8 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9259 - +#define CURRENT_BINARY_DATABASE_VERSION 9260 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9042 #endif diff --git a/queryserv/lfguild.cpp b/queryserv/lfguild.cpp index e1ec30257..5e80a8511 100644 --- a/queryserv/lfguild.cpp +++ b/queryserv/lfguild.cpp @@ -10,27 +10,27 @@ extern WorldServer *worldserver; extern QSDatabase database; -PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 Level, uint32 Class, uint32 AACount, uint32 Timezone, uint32 TimePosted) +PlayerLookingForGuild::PlayerLookingForGuild(char *name, char *comments, uint32 level, uint32 classes, uint32 aa_count, uint32 time_zone, uint32 time_posted) { - Name = Name; - Comments = Comments; - Level = Level; - Class = Class; - AACount = AACount; - TimeZone = Timezone; - TimePosted = TimePosted; + Name = std::string(name); + Comments = std::string(comments); + Level = level; + Class = classes; + AACount = aa_count; + TimeZone = time_zone; + TimePosted = time_posted; } -GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted) +GuildLookingForPlayers::GuildLookingForPlayers(char * name, char * comments, uint32 from_level, uint32 to_level, uint32 classes, uint32 aa_count, uint32 time_zone, uint32 time_posted) { - Name = Name; - Comments = Comments; - FromLevel = FromLevel; - ToLevel = ToLevel; - Classes = Classes; - AACount = AACount; - TimeZone = Timezone; - TimePosted = TimePosted; + Name = std::string(name); + Comments = std::string(comments); + FromLevel = from_level; + ToLevel = to_level; + Classes = classes; + AACount = aa_count; + TimeZone = time_zone; + TimePosted = time_posted; } bool LFGuildManager::LoadDatabase() diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index c1c2c3a15..f142cb699 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -82,7 +82,7 @@ OP_ChangeSize=0x4707 OP_TributeUpdate=0x5961 OP_TributeTimer=0x073d OP_SendTributes=0x729b -OP_SendGuildTributes=0x1877 +OP_RequestGuildTributes=0x4d5e OP_TributeInfo=0x4254 OP_Weather=0x661e OP_ReqClientSpawn=0x35fa @@ -133,16 +133,20 @@ OP_GuildManageBanker=0x3f35 OP_GuildBank=0x5134 OP_GuildBankItemList=0x7850 OP_SetGuildRank=0x0b9c -OP_GuildUpdateURLAndChannel=0x2958 +OP_GuildUpdate=0x2958 OP_GuildStatus=0x7326 OP_GuildCreate=0x76d9 -OP_GuildMemberLevelUpdate=0x0000 # Unused? -OP_ZoneGuildList=0x0000 # Unused? -OP_GetGuildsList=0x0000 # Unused? -OP_LFGuild=0x0000 -OP_GuildManageRemove=0x0000 -OP_GuildManageAdd=0x0000 -OP_GuildManageStatus=0x0000 +OP_GuildOpenGuildWindow=0x0276 +OP_GuildMemberLevel=0x1bd3 +OP_GuildMemberRankAltBanker=0x0b9c +OP_GuildMemberPublicNote=0x01f9 +OP_GuildMemberAdd=0x2925 +OP_GuildMemberRename=0x3b26 +OP_GuildMemberDelete=0x3141 +OP_GuildMemberDetails=0x69b9 +OP_GuildRenameGuild=0x61db +OP_LFGuild=0x3165 +OP_GuildDeleteGuild=0x6dab # GM/Guide Opcodes OP_GMServers=0x08c1 @@ -476,7 +480,6 @@ OP_RecipeAutoCombine=0x40d7 OP_TradeSkillCombine=0x579a # Tribute Packets: -OP_OpenGuildTributeMaster=0x378d OP_OpenTributeMaster=0x7666 OP_SelectTribute=0x79fc OP_TributeItem=0x4f3e @@ -487,6 +490,18 @@ OP_TributeNPC=0x0000 OP_GuildTributeInfo=0x0000 OP_OpenTributeReply=0x0000 OP_GuildTributeStatus=0x0000 +OP_GuildSaveActiveTributes=0x5f3f +OP_GuildSendActiveTributes=0x3771 +OP_GuildTributeToggleReq=0x7745 +OP_GuildTributeToggleReply=0x20ae +OP_GuildTributeFavorAndTimer=0x7fe0 +OP_GuildTributeDonateItem=0x5bcb +OP_GuildTributeDonatePlat=0x43d2 +OP_GuildSelectTribute=0x649f +OP_GuildModifyBenefits=0x34cd +OP_GuildOptInOut=0x0727 +OP_SendGuildTributes=0x1877 +OP_OpenGuildTributeMaster=0x49ea # Adventure packets: OP_LeaveAdventure=0x5d18 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index d53063157..8d39d3ec1 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -42,6 +42,7 @@ OP_WorldLoginFailed=0x8DA7 # world->client. reject. OP_WorldLogout=0x7718 # client->world OP_WorldLevelTooHigh=0x583b # world->client. Cancels zone in. OP_CharInacessable=0x436A # world->client. Cancels zone in. +OP_RequestGuildTributes=0x5e3a # request packet, 4 bytes #Zone in opcodes OP_ZoneEntry=0x7213 # ShowEQ 10/27/05 @@ -108,13 +109,20 @@ OP_GuildInvite=0x18b7 OP_GuildPublicNote=0x17a2 # ShowEQ 10/27/05 OP_GuildDelete=0x6cce OP_GuildInviteAccept=0x61d0 -#OP_GuildManageRemove=0x0000 -#OP_GuildManageAdd=0x0000 -#OP_GuildManageStatus=0x0000 +OP_GuildMemberPublicNote=0x17a2 OP_GuildManageBanker=0x3d1e OP_GuildBank=0xb4ab OP_SetGuildRank=0x6966 OP_LFGuild=0x1858 +OP_GuildMemberLevel=0x461a +OP_GuildMemberRankAltBanker=0x6966 +OP_GuildMemberPublicNote=0x32cf +OP_GuildMemberAdd=0x754e +OP_GuildMemberRename=0x7c59 +OP_GuildMemberDelete=0x712a +OP_GuildMemberDetails=0x0f4d +OP_GuildRenameGuild=0x4cc7 +OP_GuildDeleteGuild=0x3230 #GM/guide opcodes OP_GMServers=0x3387 #/servers @@ -370,7 +378,6 @@ OP_TrackTarget=0x7085 OP_TrackUnknown=0x6177 #size 0 right after OP_Track #Tribute Packets: -OP_OpenGuildTributeMaster=0x0000 OP_OpenTributeMaster=0x512e #open tribute master window OP_OpenTributeReply=0x27B3 #reply to open request OP_SelectTribute=0x625d #clicking on a tribute, and text reply @@ -383,9 +390,20 @@ OP_TributePointUpdate=0x6463 #16 byte point packet OP_TributeUpdate=0x5639 # ShowEQ 10/27/05 OP_GuildTributeInfo=0x5e3d # ShowEQ 10/27/05 OP_TributeInfo=0x152d -OP_SendGuildTributes=0x5e3a # request packet, 4 bytes OP_SendTributes=0x067a # request packet, 4 bytes, migth be backwards # 27b3 4665 +OP_OpenGuildTributeMaster=0x60b6 +OP_GuildSaveActiveTributes=0x12fb +OP_GuildSendActiveTributes=0x1a29 +OP_GuildTributeToggleReq=0x4b98 +OP_GuildTributeToggleReply=0x3312 +OP_GuildTributeFavorAndTimer=0x67ea +OP_GuildTributeDonateItem=0x32c0 +OP_GuildTributeDonatePlat=0x44c4 +OP_GuildSelectTribute=0x704f +OP_GuildModifyBenefits=0x4eef +OP_GuildOptInOut=0x0cfc +OP_SendGuildTributes=0x5e3d #Adventure packets: OP_LeaveAdventure=0x0c0d diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 88e8ad654..aa3beb3a2 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -83,6 +83,7 @@ OP_SpawnAppearance=0x3e17 # V OP_ChangeSize=0x6942 # OP_TributeUpdate=0x684c # V OP_TributeTimer=0x4895 # C +OP_RequestGuildTributes=0x6124 # OP_TaskDescription=0x156c # C OP_TaskActivity=0x31f3 # C OP_CompletedTasks=0x687f # C @@ -104,7 +105,7 @@ OP_ClearBlockedBuffs=0x5570 # C OP_SendExpZonein=0x47e7 # V OP_SendTributes=0x6bfb # V OP_TributeInfo=0x5a67 # V -OP_SendGuildTributes=0x4df0 # C 0x5a01 +#OP_SendGuildTributes=0x4df0 # C 0x5a01 OP_AAExpUpdate=0x4aa2 # V OP_ExpUpdate=0x0555 # V OP_HPUpdate=0x6145 # V @@ -137,16 +138,22 @@ OP_GuildPublicNote=0x2dbd # C OP_GuildManageBanker=0x1e4c # C OP_GuildBank=0x0d8a # C OP_SetGuildRank=0x4ffe # C -OP_GuildUpdateURLAndChannel=0x5232 # C +OP_GuildUpdate=0x5232 # C OP_GuildMemberLevelUpdate=0x0000 # OP_ZoneGuildList=0x0000 # OP_GetGuildsList=0x0000 # OP_GuildStatus=0x28c8 # OP_GuildCreate=0x192d # OP_LFGuild=0x7e23 # -# OP_GuildManageRemove=0x0000 # -# OP_GuildManageAdd=0x0000 # -# OP_GuildManageStatus=0x0000 # +OP_GuildMemberLevel=0x4d4e +OP_GuildMemberRankAltBanker=0x4ffe +OP_GuildMemberPublicNote=0x526d +OP_GuildMemberAdd=0x7337 +OP_GuildMemberRename=0x5fc7 +OP_GuildMemberDelete=0x190c +OP_GuildMemberDetails=0x0a53 +OP_GuildRenameGuild=0x480e +OP_GuildDeleteGuild=0x736 # GM/guide opcodes OP_GMServers=0x6989 # C @@ -480,6 +487,18 @@ OP_TributeNPC=0x0000 # OP_GuildTributeInfo=0x0000 # OP_OpenTributeReply=0x0000 # # OP_GuildTributeStatus=0x0000 # +OP_OpenGuildTributeMaster=0x4933 +OP_GuildSaveActiveTributes=0x322f +OP_GuildSendActiveTributes=0x5a01 # +OP_GuildTributeToggleReq=0x7880 +OP_GuildTributeToggleReply=0x7d66 # +OP_GuildTributeFavorAndTimer=0x4df0 +OP_GuildTributeDonateItem=0x3683 +OP_GuildTributeDonatePlat=0x5e79 +OP_GuildSelectTribute=0x3bef +OP_GuildModifyBenefits=0x3a1b +OP_GuildOptInOut=0x5a6f +OP_SendGuildTributes=0x45b3 # Adventure packets: OP_LeaveAdventure=0x3ed4 # C diff --git a/world/client.cpp b/world/client.cpp index 12d0130fa..838139208 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -2387,3 +2387,45 @@ void Client::RecordPossibleHack(const std::string& message) PlayerEventLogsRepository::InsertOne(database, e); } } + +void Client::SendGuildTributeFavorAndTimer(uint32 favor, uint32 time_remaining) +{ + auto cle = GetCLE(); + if (!cle) { + return; + } + + auto guild = guild_mgr.GetGuildByGuildID(GetCLE()->GuildID()); + if (guild) { + guild->tribute.favor = favor; + guild->tribute.time_remaining = time_remaining; + + auto outapp = new EQApplicationPacket(OP_GuildTributeFavorAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + auto gtsa = (GuildTributeFavorTimer_Struct *)outapp->pBuffer; + + gtsa->guild_id = GetCLE()->GuildID(); + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->trophy_timer = 0; //not yet implemented + + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::SendGuildTributeOptInToggle(const GuildTributeMemberToggle *in) +{ + auto outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct)); + auto data = (GuildTributeOptInOutReply_Struct *)outapp->pBuffer; + + data->guild_id = in->guild_id; + data->no_donations = in->no_donations; + data->tribute_toggle = in->tribute_toggle; + data->tribute_trophy_toggle = 0; //not yet implemented + data->time = time(nullptr); + data->command = in->command; + strn0cpy(data->player_name, in->player_name, sizeof(data->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} diff --git a/world/client.h b/world/client.h index 975350a01..b1f0afe8e 100644 --- a/world/client.h +++ b/world/client.h @@ -51,6 +51,8 @@ public: void SendLogServer(); void SendApproveWorld(); void SendPostEnterWorld(); + void SendGuildTributeFavorAndTimer(uint32 favor, uint32 time_remaining); + void SendGuildTributeOptInToggle(const GuildTributeMemberToggle* in); inline uint32 GetIP() { return ip; } inline uint16 GetPort() { return port; } diff --git a/world/cliententry.cpp b/world/cliententry.cpp index e8af366f2..66a506a7b 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -223,6 +223,8 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_ panon = scl->anon; ptellsoff = scl->tellsoff; pguild_id = scl->guild_id; + pguild_rank = scl->guild_rank; + pguild_tribute_opt_in = scl->guild_tribute_opt_in; pLFG = scl->LFG; gm = scl->gm; pClientVersion = scl->ClientVersion; @@ -280,6 +282,7 @@ void ClientListEntry::ClearVars(bool iAll) panon = 0; ptellsoff = 0; pguild_id = GUILD_NONE; + pguild_rank = 0; pLFG = 0; gm = 0; pClientVersion = 0; diff --git a/world/cliententry.h b/world/cliententry.h index 38bbaad9b..f11bdcc1a 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -95,29 +95,32 @@ public: inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; } // Character info - inline ZoneServer* Server() const { return pzoneserver; } - inline void ClearServer() { pzoneserver = 0; } - inline uint32 CharID() const { return pcharid; } - inline const char* name() const { return pname; } - inline uint32 zone() const { return pzone; } - inline uint16 instance() const { return pinstance; } - inline uint8 level() const { return plevel; } - inline uint8 class_() const { return pclass_; } - inline uint16 race() const { return prace; } - inline uint8 Anon() { return panon; } - inline uint8 TellsOff() const { return ptellsoff; } - inline uint32 GuildID() const { return pguild_id; } - inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; } - inline bool LFG() const { return pLFG; } - inline uint8 GetGM() const { return gm; } - inline void SetGM(uint8 igm) { gm = igm; } - inline void SetZone(uint32 zone) { pzone = zone; } - inline bool IsLocalClient() const { return plocal; } - inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; } - inline uint8 GetLFGToLevel() const { return pLFGToLevel; } - inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; } - inline const char* GetLFGComments() const { return pLFGComments; } - inline uint8 GetClientVersion() { return pClientVersion; } + inline ZoneServer *Server() const { return pzoneserver; } + inline void ClearServer() { pzoneserver = 0; } + inline uint32 CharID() const { return pcharid; } + inline const char *name() const { return pname; } + inline uint32 zone() const { return pzone; } + inline uint16 instance() const { return pinstance; } + inline uint8 level() const { return plevel; } + inline uint8 class_() const { return pclass_; } + inline uint16 race() const { return prace; } + inline uint8 Anon() { return panon; } + inline uint8 TellsOff() const { return ptellsoff; } + inline uint32 GuildID() const { return pguild_id; } + inline uint32 GuildRank() const { return pguild_rank; } + inline bool GuildTributeOptIn() const { return pguild_tribute_opt_in; } + inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; } + inline void SetGuildTributeOptIn(bool opt) { pguild_tribute_opt_in = opt; } + inline bool LFG() const { return pLFG; } + inline uint8 GetGM() const { return gm; } + inline void SetGM(uint8 igm) { gm = igm; } + inline void SetZone(uint32 zone) { pzone = zone; } + inline bool IsLocalClient() const { return plocal; } + inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; } + inline uint8 GetLFGToLevel() const { return pLFGToLevel; } + inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; } + inline const char *GetLFGComments() const { return pLFGComments; } + inline uint8 GetClientVersion() { return pClientVersion; } inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); } inline bool TellQueueEmpty() const { return tell_queue.empty(); } @@ -160,6 +163,8 @@ private: uint8 panon{}; uint8 ptellsoff{}; uint32 pguild_id{}; + uint32 pguild_rank; + bool pguild_tribute_opt_in{}; bool pLFG{}; uint8 gm{}; uint8 pClientVersion{}; diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 57b2c8f98..eb7b960db 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -384,6 +384,8 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s " anon [{}]" " tellsoff [{}]" " guild_id [{}]" + " guild_rank [{}]" + " guild_tribute_opt_in [{}]" " LFG [{}]" " gm [{}]" " ClientVersion [{}]" @@ -409,6 +411,8 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s scl->anon, scl->tellsoff, scl->guild_id, + scl->guild_rank, + scl->guild_tribute_opt_in, scl->LFG, scl->gm, scl->ClientVersion, @@ -1577,6 +1581,8 @@ void ClientList::GetClientList(Json::Value &response) row["client_version"] = cle->GetClientVersion(); row["gm"] = cle->GetGM(); row["guild_id"] = cle->GuildID(); + row["guild_rank"] = cle->GuildRank(); + row["guild_tribute_opt_in"] = cle->GuildTributeOptIn(); row["instance"] = cle->instance(); row["is_local_client"] = cle->IsLocalClient(); row["level"] = cle->level(); @@ -1667,3 +1673,97 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character, character->Server()->SendPacket(pack.get()); } + +void ClientList::GetGuildClientList(Json::Value& response, uint32 guild_id) +{ + LinkedListIterator Iterator(clientlist); + + Iterator.Reset(); + + while (Iterator.MoreElements()) { + ClientListEntry* cle = Iterator.GetData(); + if (cle->GuildID() != guild_id) { + Iterator.Advance(); + continue; + } + Json::Value row; + + row["account_id"] = cle->AccountID(); + row["account_name"] = cle->AccountName(); + row["admin"] = cle->Admin(); + row["id"] = cle->GetID(); + row["ip"] = cle->GetIP(); + row["loginserver_account_id"] = cle->LSAccountID(); + row["loginserver_id"] = cle->LSID(); + row["loginserver_name"] = cle->LSName(); + row["online"] = cle->Online(); + row["world_admin"] = cle->WorldAdmin(); + + auto server = cle->Server(); + if (server) { + row["server"]["client_address"] = server->GetCAddress(); + row["server"]["client_local_address"] = server->GetCLocalAddress(); + row["server"]["client_port"] = server->GetCPort(); + row["server"]["compile_time"] = server->GetCompileTime(); + row["server"]["id"] = server->GetID(); + row["server"]["instance_id"] = server->GetInstanceID(); + row["server"]["ip"] = server->GetIP(); + row["server"]["is_booting"] = server->IsBootingUp(); + row["server"]["launch_name"] = server->GetLaunchName(); + row["server"]["launched_name"] = server->GetLaunchedName(); + row["server"]["number_players"] = server->NumPlayers(); + row["server"]["port"] = server->GetPort(); + row["server"]["previous_zone_id"] = server->GetPrevZoneID(); + row["server"]["static_zone"] = server->IsStaticZone(); + row["server"]["uui"] = server->GetUUID(); + row["server"]["zone_id"] = server->GetZoneID(); + row["server"]["zone_long_name"] = server->GetZoneLongName(); + row["server"]["zone_name"] = server->GetZoneName(); + row["server"]["zone_os_pid"] = server->GetZoneOSProcessID(); + } + else { + row["server"] = Json::Value(); + } + row["anon"] = cle->Anon(); + row["character_id"] = cle->CharID(); + row["class"] = cle->class_(); + row["client_version"] = cle->GetClientVersion(); + row["gm"] = cle->GetGM(); + row["guild_id"] = cle->GuildID(); + row["guild_rank"] = cle->GuildRank(); + row["guild_tribute_opt_in"] = cle->GuildTributeOptIn(); + row["instance"] = cle->instance(); + row["is_local_client"] = cle->IsLocalClient(); + row["level"] = cle->level(); + row["lfg"] = cle->LFG(); + row["lfg_comments"] = cle->GetLFGComments(); + row["lfg_from_level"] = cle->GetLFGFromLevel(); + row["lfg_match_filter"] = cle->GetLFGMatchFilter(); + row["lfg_to_level"] = cle->GetLFGToLevel(); + row["name"] = cle->name(); + row["race"] = cle->race(); + row["tells_off"] = cle->TellsOff(); + row["zone"] = cle->zone(); + + response.append(row); + + Iterator.Advance(); + } +} + +std::map ClientList::GetGuildClientsWithTributeOptIn(uint32 guild_id) +{ + std::map guild_members; + + LinkedListIterator Iterator(clientlist); + Iterator.Reset(); + + while (Iterator.MoreElements()) { + auto c = Iterator.GetData(); + if (c->GuildID() == guild_id && c->GuildTributeOptIn()) { + guild_members.emplace(c->CharID(), c); + } + Iterator.Advance(); + } + return guild_members; +} diff --git a/world/clientlist.h b/world/clientlist.h index 50ea5ab69..f075691e5 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -33,7 +33,9 @@ public: void ZoneBootup(ZoneServer* zs); void RemoveCLEReferances(ClientListEntry* cle); - + std::map GetGuildClientsWithTributeOptIn(uint32 guild_id); + std::map GetGuildClientList(uint32 guild_id); + //Client* GetClient(uint32 char_id); //from ZSList @@ -65,6 +67,7 @@ public: void GetClients(const char *zone_name, std::vector &into); void GetClientList(Json::Value &response); + void GetGuildClientList(Json::Value& response, uint32 guild_id); void SendCharacterMessage(uint32_t character_id, int chat_type, const std::string& message); void SendCharacterMessage(const std::string& character_name, int chat_type, const std::string& message); diff --git a/world/eqemu_api_world_data_service.cpp b/world/eqemu_api_world_data_service.cpp index 25d348b19..02944e53b 100644 --- a/world/eqemu_api_world_data_service.cpp +++ b/world/eqemu_api_world_data_service.cpp @@ -27,10 +27,12 @@ #include "../common/database_schema.h" #include "../common/zone_store.h" #include "worlddb.h" +#include "wguild_mgr.h" #include "world_config.h" -extern ZSList zoneserver_list; -extern ClientList client_list; +extern ZSList zoneserver_list; +extern ClientList client_list; +extern WorldGuildManager guild_mgr; void callGetZoneList(Json::Value &response) { @@ -277,7 +279,63 @@ void EQEmuApiWorldDataService::get(Json::Value &r, const std::vectorLocked; } } + +void EQEmuApiWorldDataService::callGetGuildDetails(Json::Value &response, const std::vector &args) +{ + + std::string command = !args[1].empty() ? args[1] : ""; + if (command.empty()) { + return; + } + Json::Value row; + + auto guild_id = Strings::ToUnsignedInt(command); + if (!guild_id) { + row = "useage is: api get_guild_details ### where ### is a valid guild id."; + return; + } + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + if (!guild) { + row = fmt::format("Could not find guild id {}", guild_id); + return; + } + + row["guild_id"] = command; + row["guild_name"] = guild->name; + row["leader_id"] = guild->leader; + row["min_status"] = guild->minstatus; + row["motd"] = guild->motd; + row["motd_setter"] = guild->motd_setter; + row["url"] = guild->url; + row["channel"] = guild->channel; + + for (int i = GUILD_LEADER; i <= GUILD_RECRUIT; i++) { + row["Ranks"][i] = guild->rank_names[i].c_str(); + } + + for (int i = 1; i <= GUILD_MAX_FUNCTIONS; i++) { + row["functions"][i]["db_id"] = guild->functions[i].id; + row["functions"][i]["perm_id"] = guild->functions[i].perm_id; + row["functions"][i]["guild_id"] = guild->functions[i].guild_id; + row["functions"][i]["perm_value"] = guild->functions[i].perm_value; + } + + row["tribute"]["favor"] = guild->tribute.favor; + row["tribute"]["id1"] = guild->tribute.id_1; + row["tribute"]["id1_tier"] = guild->tribute.id_1_tier; + row["tribute"]["id2"] = guild->tribute.id_2; + row["tribute"]["id2_tier"] = guild->tribute.id_2_tier; + row["tribute"]["time_remaining"] = guild->tribute.time_remaining; + row["tribute"]["enabled"] = guild->tribute.enabled; + + client_list.GetGuildClientList(response, guild_id); + + response.append(row); +} diff --git a/world/eqemu_api_world_data_service.h b/world/eqemu_api_world_data_service.h index b1bc7bbbc..f0952969e 100644 --- a/world/eqemu_api_world_data_service.h +++ b/world/eqemu_api_world_data_service.h @@ -28,6 +28,7 @@ public: static void get(Json::Value &r, const std::vector &args); static void reload(Json::Value &r, const std::vector &args); static void message(Json::Value &r, const std::string &message); + static void callGetGuildDetails(Json::Value &response, const std::vector &args); }; diff --git a/world/main.cpp b/world/main.cpp index 95e6d0889..0cff3fc20 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -45,6 +45,7 @@ #include "../common/misc.h" #include "client.h" #include "worlddb.h" +#include "wguild_mgr.h" #ifdef _WINDOWS #include @@ -409,6 +410,7 @@ int main(int argc, char **argv) event_scheduler.Process(&zoneserver_list); client_list.Process(); + guild_mgr.Process(); if (player_event_process_timer.Check()) { player_event_logs.Process(); diff --git a/world/wguild_mgr.cpp b/world/wguild_mgr.cpp index 302dee616..7ac10012f 100644 --- a/world/wguild_mgr.cpp +++ b/world/wguild_mgr.cpp @@ -22,17 +22,25 @@ #include "../common/servertalk.h" #include "clientlist.h" #include "zonelist.h" +#include "zoneserver.h" +#include "cliententry.h" +#include "client.h" +#include "../common/repositories/guilds_repository.h" +#include "../common/repositories/guild_ranks_repository.h" +#include "../common/repositories/guild_permissions_repository.h" +#include "../common/repositories/guild_members_repository.h" +#include "../common/repositories/guild_bank_repository.h" +#include "../common/repositories/guild_tributes_repository.h" +#include "../common/repositories/tributes_repository.h" +#include "../common/repositories/tribute_levels_repository.h" extern ClientList client_list; extern ZSList zoneserver_list; - - +std::map tribute_list; WorldGuildManager guild_mgr; - - void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { LogGuilds("Broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", guild_id, name, motd, rank, relation); auto pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); @@ -89,14 +97,12 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { break; } - case ServerOP_GuildCharRefresh: { - if(pack->size != sizeof(ServerGuildCharRefresh_Struct)) { - LogGuilds("Received ServerOP_RefreshGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildCharRefresh_Struct)); - return; - } + case ServerOP_GuildCharRefresh: + { ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; LogGuilds("Received and broadcasting guild member refresh for char [{}] to all zones with members of guild [{}]", s->char_id, s->guild_id); + RefreshGuild(s->guild_id); //preform the local update client_list.UpdateClientGuild(s->char_id, s->guild_id); @@ -107,41 +113,325 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { break; } - case ServerOP_DeleteGuild: { + case ServerOP_DeleteGuild: + { if(pack->size != sizeof(ServerGuildID_Struct)) { LogGuilds("Received ServerOP_DeleteGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildID_Struct)); return; } + ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + + auto res = m_guilds.find(s->guild_id); + if (res != m_guilds.end()) { + delete res->second; + m_guilds.erase(res); + } + LogGuilds("Received and broadcasting guild delete for guild [{}]", s->guild_id); //broadcast this packet to all zones. zoneserver_list.SendPacket(pack); - //preform a local refresh. - if(!LocalDeleteGuild(s->guild_id)) { - LogGuilds("Unable to preform local delete on guild [{}]", s->guild_id); - //can we do anything? - } - break; } - case ServerOP_GuildMemberUpdate: { + case ServerOP_GuildMemberUpdate: + { if(pack->size != sizeof(ServerGuildMemberUpdate_Struct)) { LogGuilds("Received ServerOP_GuildMemberUpdate of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildMemberUpdate_Struct)); return; } + auto s = (ServerGuildID_Struct *)pack->pBuffer; + RefreshGuild(s->guild_id); + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_GuildPermissionUpdate: + { + if (pack->size != sizeof(ServerGuildPermissionUpdate_Struct)) + { + LogGuilds("Received ServerOP_GuildPermissionUpdate of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildPermissionUpdate_Struct)); + return; + } + + auto sg = (ServerGuildPermissionUpdate_Struct *)pack->pBuffer; + auto guild = GetGuildByGuildID(sg->guild_id); + if (!guild) { + guild_mgr.LoadGuilds(); + guild = GetGuildByGuildID(sg->guild_id); + } + + if (guild) { + if (sg->function_value) { + guild->functions[sg->function_id].perm_value |= (1UL << (8 - sg->rank)); + } + else { + guild->functions[sg->function_id].perm_value &= ~(1UL << (8 - sg->rank)); + } + + LogGuilds("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {}", + sg->guild_id, + sg->function_id, + sg->function_value + ); + + zoneserver_list.SendPacketToBootedZones(pack); + } + else { + LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.", + sg->guild_id, + sg->function_id, + sg->function_value + ); + } break; } + case ServerOP_GuildRankNameChange: + { + if (pack->size != sizeof(ServerGuildRankNameChange)) + { + LogGuilds("Received ServerOP_ServerGuildRankNameChange of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildPermissionUpdate_Struct)); + return; + } + auto rnc = (ServerGuildRankNameChange*)pack->pBuffer; + auto guild = GetGuildByGuildID(rnc->guild_id); + if (!guild) { + guild_mgr.LoadGuilds(); + guild = GetGuildByGuildID(rnc->guild_id); + } + + if (guild) { + guild->rank_names[rnc->rank] = rnc->rank_name; + LogGuilds("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {}", + rnc->guild_id, + rnc->rank, + rnc->rank_name + ); + zoneserver_list.SendPacketToBootedZones(pack); + } + else { + LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.", + rnc->guild_id, + rnc->rank, + rnc->rank_name + ); + } + + break; + } + case ServerOP_GuildMemberLevelUpdate: + case ServerOP_GuildMemberPublicNote: + case ServerOP_GuildChannel: + case ServerOP_GuildURL: + case ServerOP_GuildMemberRemove: + case ServerOP_GuildMemberAdd: + case ServerOP_GuildSendGuildList: + case ServerOP_GuildMembersList: + { + zoneserver_list.SendPacketToBootedZones(pack); + break; + } default: LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode); break; } } +void WorldGuildManager::Process() +{ + for (auto &g: m_guilds) { + if (!g.second->tribute.enabled) { + continue; + } + else if (g.second->tribute.enabled && !g.second->tribute.timer.Enabled()) { + g.second->tribute.timer.Start(g.second->tribute.time_remaining); + LogGuilds( + "Found a Guild Tribute Timer for guild [{}]. that was not started. Started it with [{}] time remaining before restart.", + g.first, + g.second->tribute.time_remaining + ); + } + else if (g.second->tribute.enabled && g.second->tribute.timer.Check()) { + g.second->tribute.favor -= GetGuildTributeCost(g.first); + g.second->tribute.time_remaining = RuleI(Guild, TributeTime); + g.second->tribute.timer.Start(RuleI(Guild, TributeTime)); + guild_mgr.UpdateDbGuildFavor(g.first, g.second->tribute.favor); + guild_mgr.UpdateDbTributeTimeRemaining(g.first, RuleI(Guild, TributeTime)); + SendGuildTributeFavorAndTimer(g.first, g.second->tribute.favor, g.second->tribute.timer.GetRemainingTime()); + } + else if (g.second->tribute.send_timer && + ((g.second->tribute.timer.GetRemainingTime() / 1000) % + (RuleI(Guild, TributeTimeRefreshInterval) / 1000)) == 0 && + !g.second->tribute.timer.Check() + ) { + g.second->tribute.send_timer = false; + g.second->tribute.time_remaining = g.second->tribute.timer.GetRemainingTime(); + SendGuildTributeFavorAndTimer(g.first, g.second->tribute.favor, g.second->tribute.time_remaining); + guild_mgr.UpdateDbTributeTimeRemaining(g.first, g.second->tribute.time_remaining); + LogGuilds( + "Timer Frequency [{}] ms hit sending time [{}] to guild clients", + RuleI(Guild, TributeTimeRefreshInterval), + g.second->tribute.time_remaining + ); + } + else if (!g.second->tribute.send_timer && + ((g.second->tribute.timer.GetRemainingTime() / 1000) % + (RuleI(Guild, TributeTimeRefreshInterval) / 1000)) > 0 && + !g.second->tribute.timer.Check() + ) { + g.second->tribute.send_timer = true; + } + } +} + +uint32 WorldGuildManager::GetGuildTributeCost(uint32 guild_id) +{ + auto guild_members = client_list.GetGuildClientsWithTributeOptIn(guild_id); + auto total = guild_members.size(); + auto total_cost = 0; + + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + if (guild) + { + TributeData &d1 = tribute_list[guild->tribute.id_1]; + TributeData &d2 = tribute_list[guild->tribute.id_2]; + uint32 cost_id1 = d1.tiers[guild->tribute.id_1_tier].cost; + uint32 cost_id2 = d2.tiers[guild->tribute.id_2_tier].cost; + uint32 level_id1 = d2.tiers[guild->tribute.id_1_tier].level; + uint32 level_id2 = d2.tiers[guild->tribute.id_2_tier].level; + + for (auto const &m: guild_members) { + if (m.second->level() >= level_id1) { + total_cost += cost_id1; + } + if (m.second->level() >= level_id2) { + total_cost += cost_id2; + } + } + } + return total_cost; +} + +bool WorldGuildManager::LoadTributes() +{ + TributeData td{}; + td.tier_count = 0; + + tribute_list.clear(); + + auto tributes = TributesRepository::All(*m_db); + for (auto const& t : tributes) { + td.name = t.name; + td.description = t.descr; + td.unknown = t.unknown; + td.is_guild = t.isguild == 0 ? false : true; + tribute_list[t.id] = td; + } + + LogInfo("Loaded [{}] tributes", Strings::Commify(tributes.size())); + + auto tribute_levels = TributeLevelsRepository::GetWhere(*m_db, "TRUE ORDER BY tribute_id, level"); + + for (auto const& t : tribute_levels) { + uint32 id = t.tribute_id; + + if (tribute_list.count(id) != 1) { + LogError("Error in LoadTributes: unknown tribute [{}] in tribute_levels", (unsigned long)id); + continue; + } + + TributeData& cur = tribute_list[id]; + if (cur.tier_count >= MAX_TRIBUTE_TIERS) { + LogError("Error in LoadTributes: on tribute [{}] more tiers defined than permitted", (unsigned long)id); + continue; + } + + TributeLevel_Struct& s = cur.tiers[cur.tier_count]; + + s.level = t.level; + s.cost = t.cost; + s.tribute_item_id = t.item_id; + + cur.tier_count++; + } + + LogInfo("Loaded [{}] tribute levels", Strings::Commify(tribute_levels.size())); + + return true; +} + +bool WorldGuildManager::RefreshGuild(uint32 guild_id) +{ + auto temp_guild = GetGuildByGuildID(guild_id); + BaseGuildManager::GuildInfo temp_guild_detail; + + if (temp_guild) { + temp_guild_detail.tribute = temp_guild->tribute; + } + + if (guild_id <= 0) { + LogError("Requested to refresh guild id [{}] but id must be greater than 0.", guild_id); + return false; + } + + auto db_guild = GuildsRepository::FindOne(*m_db, guild_id); + if (!db_guild.id) { + LogGuilds("Guild ID [{}] not found in database.", db_guild.id); + return false; + } + + LogGuilds("Found guild id [{}]. Loading details.....", db_guild.id); + _CreateGuild(db_guild.id, db_guild.name, db_guild.leader, db_guild.minstatus, db_guild.motd, db_guild.motd_setter, db_guild.channel, db_guild.url, db_guild.favor); + auto guild = GetGuildByGuildID(guild_id); + auto where_filter = fmt::format("guild_id = '{}'", guild_id); + auto guild_ranks = GuildRanksRepository::GetWhere(*m_db, where_filter); + for (auto const& r : guild_ranks) { + guild->rank_names[r.rank_] = r.title; + } + + where_filter = fmt::format("guild_id = '{}'", guild_id); + auto guild_permissions = GuildPermissionsRepository::GetWhere(*m_db, where_filter); + for (auto const& p : guild_permissions) { + guild->functions[p.perm_id].id = p.id; + guild->functions[p.perm_id].guild_id = p.guild_id; + guild->functions[p.perm_id].perm_id = p.perm_id; + guild->functions[p.perm_id].perm_value = p.permission; + } + + auto guild_tributes = GuildTributesRepository::FindOne(*m_db, guild_id); + if (guild_tributes.guild_id) { + guild->tribute.id_1 = guild_tributes.tribute_id_1; + guild->tribute.id_2 = guild_tributes.tribute_id_2; + guild->tribute.id_1_tier = guild_tributes.tribute_id_1_tier; + guild->tribute.id_2_tier = guild_tributes.tribute_id_2_tier; + guild->tribute.enabled = guild_tributes.enabled; + guild->tribute.time_remaining = guild_tributes.time_remaining; + } + if (temp_guild_detail.tribute.enabled == 1) { + guild->tribute = temp_guild_detail.tribute; + } + + LogGuilds("Successfully refreshed guild id [{}] from the [WORLD] database", guild_id); + LogGuilds("Timer has [{}] time remaining from the [WORLD] refresh.", guild->tribute.time_remaining); + + return true; +} + +void WorldGuildManager::SendGuildTributeFavorAndTimer(uint32 guild_id, uint32 favor, uint32 time) +{ + auto sp = new ServerPacket(ServerOP_GuildTributeFavAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + auto data = (GuildTributeFavorTimer_Struct *)sp->pBuffer; + data->guild_id = guild_id; + data->guild_favor = favor; + data->tribute_timer = time; + data->trophy_timer = 0; + + zoneserver_list.SendPacketToBootedZones(sp); + safe_delete(sp) +} diff --git a/world/wguild_mgr.h b/world/wguild_mgr.h index 9bc22cb94..9a0204760 100644 --- a/world/wguild_mgr.h +++ b/world/wguild_mgr.h @@ -1,5 +1,5 @@ -#ifndef GUILD_MGR_H_ -#define GUILD_MGR_H_ +#ifndef WGUILD_MGR_H_ +#define WGUILD_MGR_H_ #include "../common/types.h" #include "../common/guild_base.h" @@ -12,6 +12,12 @@ public: //called by zoneserver when it receives a guild message from zone. void ProcessZonePacket(ServerPacket *pack); + void Process(); + bool LoadTributes(); + void SendGuildTributeFavorAndTimer(uint32 guild_id); + void SendGuildTributeFavorAndTimer(uint32 guild_id, uint32 favor, uint32 time); + uint32 GetGuildTributeCost(uint32 guild_id); + virtual bool RefreshGuild(uint32 guild_id); protected: virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); @@ -19,11 +25,12 @@ protected: virtual void SendRankUpdate(uint32 CharID) { return; } virtual void SendGuildDelete(uint32 guild_id); + //map m_tribute; //map from guild ID to current tribute ammount }; extern WorldGuildManager guild_mgr; -#endif /*GUILD_MGR_H_*/ +#endif /*WGUILD_MGR_H_*/ diff --git a/world/world_boot.cpp b/world/world_boot.cpp index 719c217b3..761aeedda 100644 --- a/world/world_boot.cpp +++ b/world/world_boot.cpp @@ -297,6 +297,7 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv) } guild_mgr.LoadGuilds(); + guild_mgr.LoadTributes(); //rules: { diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 96c30c9d0..31427552a 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -857,3 +857,15 @@ const std::list> &ZSList::getZoneServerList() const { return zone_server_list; } + +bool ZSList::SendPacketToBootedZones(ServerPacket* pack) +{ + for (auto const& z : zone_server_list) { + auto r = z.get(); + if (r && r->GetZoneID() > 0) { + r->SendPacket(pack); + } + } + + return true; +} diff --git a/world/zonelist.h b/world/zonelist.h index 5066d4e6d..71d364c9b 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -28,6 +28,7 @@ public: bool SendPacket(ServerPacket *pack); bool SendPacket(uint32 zoneid, ServerPacket *pack); bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack); + bool SendPacketToBootedZones(ServerPacket* pack); bool SetLockedZone(uint16 iZoneID, bool iLock); EQTime worldclock; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 865d24b7a..c15006d54 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1,3 +1,4 @@ + /* EQEMu: Everquest Server Emulator Copyright (C) 2001-2005 EQEMu Development Team (http://eqemulator.net) @@ -47,6 +48,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/events/player_event_logs.h" #include "../common/patches/patches.h" #include "../zone/data_bucket.h" +#include "../common/repositories/guild_tributes_repository.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -978,7 +980,18 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_DeleteGuild: case ServerOP_GuildCharRefresh: case ServerOP_GuildMemberUpdate: - case ServerOP_RefreshGuild: { + case ServerOP_GuildPermissionUpdate: + case ServerOP_GuildRankNameChange: + case ServerOP_RefreshGuild: + case ServerOP_GuildMemberLevelUpdate: + case ServerOP_GuildMemberPublicNote: + case ServerOP_GuildChannel: + case ServerOP_GuildURL: + case ServerOP_GuildMemberRemove: + case ServerOP_GuildMemberAdd: + case ServerOP_GuildSendGuildList: + case ServerOP_GuildMembersList: + { guild_mgr.ProcessZonePacket(pack); break; } @@ -1552,6 +1565,169 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } + case ServerOP_GuildTributeUpdate: { + auto data = (GuildTributeUpdate *)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(data->guild_id); + + if (guild) { + guild->tribute.enabled = 0; + guild->tribute.id_1 = data->tribute_id_1; + guild->tribute.id_2 = data->tribute_id_2; + guild->tribute.id_1_tier = data->tribute_id_1_tier; + guild->tribute.id_2_tier = data->tribute_id_2_tier; + guild->tribute.time_remaining = RuleI(Guild, TributeTime); + + guild->tribute.timer.Disable(); + + zoneserver_list.SendPacketToBootedZones(pack); + } + break; + } + case ServerOP_GuildTributeActivate: { + auto data = (GuildTributeUpdate *)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(data->guild_id); + + if (guild) { + guild->tribute.enabled = data->enabled; + + if (guild->tribute.enabled) { + data->enabled = 1; + if (guild->tribute.time_remaining == RuleI(Guild, TributeTime)) { + data->favor = (guild->tribute.favor -= guild_mgr.GetGuildTributeCost(data->guild_id)); + } + data->time_remaining = guild->tribute.time_remaining; + data->tribute_id_1 = guild->tribute.id_1; + data->tribute_id_2 = guild->tribute.id_2; + data->tribute_id_1_tier = guild->tribute.id_1_tier; + data->tribute_id_2_tier = guild->tribute.id_2_tier; + + guild->tribute.timer.Start(guild->tribute.time_remaining); + LogInfo("Guild Tribute Timer Started."); + } + else { + if (guild->tribute.timer.Enabled()) { + guild->tribute.time_remaining = guild->tribute.timer.GetRemainingTime(); + } + data->enabled = 0; + data->favor = guild->tribute.favor; + data->time_remaining = guild->tribute.time_remaining; + data->tribute_id_1 = guild->tribute.id_1; + data->tribute_id_2 = guild->tribute.id_2; + data->tribute_id_1_tier = guild->tribute.id_1_tier; + data->tribute_id_2_tier = guild->tribute.id_2_tier; + LogInfo("Guild Tribute Timer Stopped with {} ms remaining.", data->time_remaining); + guild->tribute.timer.Disable(); + } + guild_mgr.UpdateDbGuildTributeEnabled(data->guild_id, data->enabled); + guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor); + guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining); + + zoneserver_list.SendPacketToBootedZones(pack); + } + break; + } + case ServerOP_GuildTributeOptInToggle: + { + auto in = (GuildTributeMemberToggle *)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + auto c = client_list.FindCharacter(in->player_name); + if (c) { + c->SetGuildTributeOptIn(in->tribute_toggle ? true : false); + } + + auto cle = client_list.FindCLEByCharacterID(in->char_id); + if (cle) { + cle->SetGuildTributeOptIn(in->tribute_toggle ? true : false); + } + + if (guild) { + CharGuildInfo gci; + guild_mgr.GetCharInfo(in->char_id, gci); + + auto out = new ServerPacket(ServerOP_GuildTributeOptInToggle, sizeof(GuildTributeMemberToggle)); + auto data = (GuildTributeMemberToggle *)out->pBuffer; + + data->char_id = in->char_id; + data->command = in->command; + data->tribute_toggle = in->tribute_toggle; + data->no_donations = gci.total_tribute; + data->member_last_donated = gci.last_tribute; + data->guild_id = in->guild_id; + data->time_remaining = in->time_remaining; + strn0cpy(data->player_name, in->player_name, sizeof(data->player_name)); + + zoneserver_list.SendPacketToBootedZones(out); + safe_delete(out); + } + break; + } + case ServerOP_RequestGuildActiveTributes: + { + auto in = (GuildTributeUpdate *)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + + if (guild) { + auto sp = new ServerPacket(ServerOP_RequestGuildActiveTributes, sizeof(GuildTributeUpdate)); + auto out = (GuildTributeUpdate *)sp->pBuffer; + + out->guild_id = in->guild_id; + out->enabled = guild->tribute.enabled; + out->favor = guild->tribute.favor; + out->tribute_id_1 = guild->tribute.id_1; + out->tribute_id_2 = guild->tribute.id_2; + out->tribute_id_1_tier = guild->tribute.id_1_tier; + out->tribute_id_2_tier = guild->tribute.id_2_tier; + out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id); + + zoneserver_list.SendPacketToBootedZones(sp); + safe_delete(sp); + } + + break; + } + case ServerOP_RequestGuildFavorAndTimer: + { + auto in = (GuildTributeUpdate *)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + + if (guild) { + auto sp = new ServerPacket(ServerOP_RequestGuildFavorAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + auto out = (GuildTributeFavorTimer_Struct *) sp->pBuffer; + + out->guild_id = in->guild_id; + out->guild_favor = guild->tribute.favor; + out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id); + out->trophy_timer = 0; + + zoneserver_list.SendPacketToBootedZones(sp); + safe_delete(sp); + } + + break; + } + case ServerOP_GuildTributeUpdateDonations: + { + auto in = (GuildTributeUpdate*)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + + if (guild) { + guild->tribute.favor = in->favor; + guild_mgr.SendGuildTributeFavorAndTimer(in->guild_id, guild->tribute.favor, guild_mgr.GetGuildTributeTimeRemaining(in->guild_id)/*guild->tribute.timer.GetRemainingTime()*/); + + auto sp = new ServerPacket(ServerOP_GuildTributeUpdateDonations, sizeof(GuildTributeUpdate)); + auto out = (GuildTributeUpdate *) sp->pBuffer; + + out->guild_id = in->guild_id; + out->member_favor = in->member_favor; + out->member_enabled = in->member_enabled ? true : false; + out->member_time = in->member_time; + strn0cpy(out->player_name, in->player_name, sizeof(out->player_name)); + + zoneserver_list.SendPacketToBootedZones(sp); + safe_delete(sp) + } + break; + } default: { LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size); DumpPacket(pack->pBuffer, pack->size); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index c5d5f93b9..86ccde198 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -193,6 +193,17 @@ void Mob::CalcItemBonuses(StatBonuses* b) { } } + if (IsOfClientBot()) { + for (i = EQ::invslot::GUILD_TRIBUTE_BEGIN; i <= EQ::invslot::GUILD_TRIBUTE_END; i++) { + const auto* inst = GetInv().GetItem(i); + if (!inst) { + continue; + } + + AddItemBonuses(inst, b, false, true); + } + } + if ( RuleI(Spells, AdditiveBonusWornType) && RuleI(Spells, AdditiveBonusWornType) != EQ::item::ItemEffectWorn diff --git a/zone/client.cpp b/zone/client.cpp index 0faf3da31..4ee20c2f7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -209,6 +209,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( lsaccountid = 0; guild_id = GUILD_NONE; guildrank = 0; + guild_tribute_opt_in = 0; GuildBanker = false; memset(lskey, 0, sizeof(lskey)); strcpy(account_name, ""); @@ -378,6 +379,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( SetBotPrecombat(false); AI_Init(); + } Client::~Client() { @@ -535,7 +537,8 @@ void Client::SendZoneInPackets() safe_delete(outapp); if (IsInAGuild()) { - SendGuildMembers(); + guild_mgr.UpdateDbMemberOnline(CharacterID(), true); + //SendGuildMembers(); SendGuildURL(); SendGuildChannel(); SendGuildLFGuildStatus(); @@ -744,7 +747,7 @@ bool Client::Save(uint8 iCommitNow) { SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); } } - + database.SaveCharacterData(this, &m_pp, &m_epp); /* Save Character Data */ database.SaveCharacterEXPModifier(this); @@ -951,12 +954,13 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s switch(chan_num) { case ChatChannel_Guild: { /* Guild Chat */ - if (!IsInAGuild()) + if (!IsInAGuild()) { MessageString(Chat::DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild. - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK)) - Message(0, "Error: You dont have permission to speak to the guild."); - else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, lang_skill, message)) - Message(0, "Error: World server disconnected"); + } else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_GUILD_CHAT_SPEAK_IN)) { + MessageString(Chat::EchoGuild, NO_PROPER_ACCESS); + } else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, lang_skill, message)) { + Message(Chat::White, "Error: World server disconnected"); + } break; } case ChatChannel_Group: { /* Group Chat */ @@ -1776,6 +1780,8 @@ void Client::UpdateWho(uint8 remove) s->ClientVersion = static_cast(ClientVersion()); s->tellsoff = tellsoff; s->guild_id = guild_id; + s->guild_rank = guildrank; + s->guild_tribute_opt_in = guild_tribute_opt_in; s->LFG = LFG; if (LFG) { s->LFGFromLevel = LFGFromLevel; @@ -2160,7 +2166,8 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) if (!IsInAGuild()) { ns->spawn.guildrank = 0xFF; } else { - ns->spawn.guildrank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), AccountID()); + ns->spawn.guildrank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); + ns->spawn.guild_show = guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_DISPLAY_GUILD_NAME); } ns->spawn.size = 0; // Changing size works, but then movement stops! (wth?) ns->spawn.runspeed = (gmspeed == 0) ? runspeed : 3.125f; @@ -10178,6 +10185,7 @@ int Client::CountItem(uint32 item_id) { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, @@ -10330,6 +10338,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, diff --git a/zone/client.h b/zone/client.h index 375025406..838d85875 100644 --- a/zone/client.h +++ b/zone/client.h @@ -718,8 +718,16 @@ public: inline bool IsInAGuild() const { return(guild_id != GUILD_NONE && guild_id != 0); } inline bool IsInGuild(uint32 in_gid) const { return(in_gid == guild_id && IsInAGuild()); } + inline bool GetGuildListDirty() { return guild_dirty; } + inline void SetGuildListDirty(bool state) { guild_dirty = state; } inline uint32 GuildID() const { return guild_id; } inline uint8 GuildRank() const { return guildrank; } + inline bool GuildTributeOptIn() const { return guild_tribute_opt_in; } + void SetGuildTributeOptIn(bool state); + void SendGuildTributeDonateItemReply(GuildTributeDonateItemRequest_Struct* in, uint32 favor); + void SendGuildTributeDonatePlatReply(GuildTributeDonatePlatRequest_Struct* in, uint32 favor); + void SetGuildRank(uint32 rank); + void SetGuildID(uint32 guild_id); void SendGuildMOTD(bool GetGuildMOTDReply = false); void SendGuildURL(); void SendGuildChannel(); @@ -729,6 +737,24 @@ public: void SendGuildList(); void SendGuildJoin(GuildJoin_Struct* gj); void RefreshGuildInfo(); + void SendGuildRankNames(); + void SendGuildTributeDetails(uint32 tribute_id, uint32 tier); + void DoGuildTributeUpdate(); + void SendGuildActiveTributes(uint32 guild_id); + void SendGuildFavorAndTimer(uint32 guild_id); + void SendGuildTributeOptInToggle(const GuildTributeMemberToggle* in); + void RequestGuildActiveTributes(uint32 guild_id); + void RequestGuildFavorAndTimer(uint32 guild_id); + void SendGuildMembersList(); + void SendGuildMemberAdd(uint32 guild_id, uint32 level, uint32 _class, uint32 rank, uint32 guild_show, uint32 zone_id, std::string player_name); + void SendGuildMemberRename(uint32 guild_id, std::string player_name, std::string new_player_name); + void SendGuildMemberDelete(uint32 guild_id, std::string player_name); + void SendGuildMemberLevel(uint32 guild_id, uint32 level, std::string player_name); + void SendGuildMemberRankAltBanker(uint32 guild_id, uint32 rank, std::string player_name, bool alt, bool banker); + void SendGuildMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note); + void SendGuildMemberDetails(uint32 guild_id, uint32 zone_id, uint32 offline_mode, std::string player_name); + void SendGuildRenameGuild(uint32 guild_id, std::string new_guild_name); + void SendGuildDeletePacket(uint32 guild_id); uint8 GetClientMaxLevel() const { return client_max_level; } void SetClientMaxLevel(uint8 max_level) { client_max_level = max_level; } @@ -1481,6 +1507,7 @@ public: void GuildBankAck(); void GuildBankDepositAck(bool Fail, int8 action); inline bool IsGuildBanker() { return GuildBanker; } + inline void SetGuildBanker(bool banker) { GuildBanker = banker; } void ClearGuildBank(); void SendGroupCreatePacket(); void SendGroupLeaderChangePacket(const char *LeaderName); @@ -1782,7 +1809,9 @@ private: char lskey[30]; int16 admin; uint32 guild_id; - uint8 guildrank; // player's rank in the guild, 0-GUILD_MAX_RANK + uint8 guildrank; // player's rank in the guild, 1- Leader 8 Recruit + bool guild_tribute_opt_in; + bool guild_dirty{ true }; //used to control add/delete opcodes due to client bug in Ti thru RoF2 bool GuildBanker; uint16 duel_target; bool duelaccepted; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c7b691419..82c1faae8 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -64,6 +64,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "client.h" #include "../common/repositories/account_repository.h" #include "../common/repositories/character_corpses_repository.h" +#include "../common/repositories/guild_tributes_repository.h" #include "../common/events/player_event_logs.h" #include "../common/repositories/character_stats_record_repository.h" @@ -101,8 +102,7 @@ void MapOpcodes() ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; // I guess it didn't believe us with the first assignment? + ConnectingOpcodes[OP_RequestGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; @@ -260,8 +260,15 @@ void MapOpcodes() ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; - ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; + ConnectedOpcodes[OP_GuildUpdate] = &Client::Handle_OP_GuildUpdate; ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; + ConnectedOpcodes[OP_GuildSelectTribute] = &Client::Handle_OP_GuildTributeSelect; + ConnectedOpcodes[OP_GuildModifyBenefits] = &Client::Handle_OP_GuildTributeModifyBenefits; + ConnectedOpcodes[OP_GuildOptInOut] = &Client::Handle_OP_GuildTributeOptInOut; + ConnectedOpcodes[OP_GuildSaveActiveTributes] = &Client::Handle_OP_GuildTributeSaveActiveTributes; + ConnectedOpcodes[OP_GuildTributeToggleReq] = &Client::Handle_OP_GuildTributeToggle; + ConnectedOpcodes[OP_GuildTributeDonateItem] = &Client::Handle_OP_GuildTributeDonateItem; + ConnectedOpcodes[OP_GuildTributeDonatePlat] = &Client::Handle_OP_GuildTributeDonatePlat; ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; @@ -538,21 +545,6 @@ void Client::CompleteConnect() // Task Packets LoadClientTaskState(); - if (IsInAGuild()) { - uint8 rank = GuildRank(); - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - switch (rank) { - case 0: { rank = 5; break; } // GUILD_MEMBER 0 - case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AppearanceType::GuildID, GuildID(), false); - SendAppearancePacket(AppearanceType::GuildRank, rank, false); - } - // moved to dbload and translators since we iterate there also .. keep m_pp values whatever they are when they get here /*const auto sbs = EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize; for (uint32 spellInt = 0; spellInt < sbs; ++spellInt) { @@ -857,9 +849,45 @@ void Client::CompleteConnect() entity_list.SendFindableNPCList(this); if (IsInAGuild()) { - SendGuildRanks(); + if (firstlogon == 1) { + guild_mgr.UpdateDbMemberOnline(CharacterID(), true); + guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + } + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); - guild_mgr.RequestOnlineGuildMembers(CharacterID(), GuildID()); + + SendGuildList(); + if (GetGuildListDirty()) { + SendGuildMembersList(); + } + + if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { + SendGuildRanks(); + SendGuildRankNames(); + } + + SendGuildActiveTributes(GuildID()); + SendGuildFavorAndTimer(GuildID()); + DoGuildTributeUpdate(); + + auto guild = guild_mgr.GetGuildByGuildID(GuildID()); + if (guild) { + ServerPacket* out = new ServerPacket(ServerOP_GuildTributeOptInToggle, sizeof(GuildTributeMemberToggle)); + GuildTributeMemberToggle* data = (GuildTributeMemberToggle*)out->pBuffer; + CharGuildInfo gci; + guild_mgr.GetCharInfo(CharacterID(), gci); + + data->char_id = CharacterID(); + data->command = 0x2fb; + data->tribute_toggle = GuildTributeOptIn(); + data->guild_id = GuildID(); + data->no_donations = gci.total_tribute; + data->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(GuildID()); + strncpy(data->player_name, GetCleanName(), sizeof(data->player_name)); + + worldserver.SendPacket(out); + safe_delete(out) + } } SendDynamicZoneUpdates(); @@ -1244,7 +1272,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Load Character Data */ query = fmt::format( - "SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank`, `exp_enabled` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = {}", + "SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank`, `exp_enabled`, `tribute_enable` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = {}", cid ); auto results = database.QueryDatabase(query); @@ -1252,6 +1280,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (row[4] && Strings::ToInt(row[4]) > 0) { guild_id = Strings::ToInt(row[4]); guildrank = row[5] ? Strings::ToInt(row[5]) : GUILD_RANK_NONE; + guild_tribute_opt_in = row[7] ? Strings::ToBool(row[7]) : 0; } SetEXPEnabled(atobool(row[6])); @@ -1398,26 +1427,16 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) } else { m_pp.guild_id = GuildID(); - uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); - // FIXME: RoF guild rank - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { - switch (rank) { - case 0: - rank = 5; - break; - case 1: - rank = 3; - break; - case 2: - rank = 1; - break; - default: - break; - } + CharGuildInfo cgi; + if (guild_mgr.GetCharInfo(CharacterID(), cgi)) { + m_pp.guildrank = cgi.rank; + } + if (zone->GetZoneID() == Zones::GUILDHALL) { + GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || + guild_mgr.GetBankerFlag(CharacterID()) || + ClientVersion() >= EQ::versions::ClientVersion::RoF ? true : false + ); } - m_pp.guildrank = rank; - if (zone->GetZoneID() == Zones::GUILDHALL) - GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); } m_pp.guildbanker = GuildBanker; @@ -7791,6 +7810,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) MessageString(Chat::Red, GUILD_BANK_CANNOT_DEPOSIT); GuildBankDepositAck(true, sentAction); + if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { + PushItemOnCursor(CursorItem, true); + } return; } @@ -7839,6 +7861,14 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) break; } + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_BANK_WITHDRAW_ITEMS)) + { + Message(Chat::Red, "You do not have permission to withdraw."); + GuildBankAck(); + safe_delete(inst); + break; + } + if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) { LogError("Suspected attempted hack on the guild bank from [{}]", GetName()); @@ -7919,15 +7949,16 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) { - if (IsInAGuild()) - { + if (IsInAGuild()) { Message(Chat::Red, "You are already in a guild!"); return; } - if (!RuleB(Guild, PlayerCreationAllowed)) - { - Message(Chat::Red, "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild."); + if (!RuleB(Guild, PlayerCreationAllowed)) { + Message( + Chat::Red, + "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild." + ); return; } @@ -7958,10 +7989,8 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) return; } - for (unsigned int i = 0; i < strlen(GuildName); ++i) - { - if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) - { + for (unsigned int i = 0; i < strlen(GuildName); ++i) { + if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) { Message(Chat::Red, "Invalid character in Guild name."); return; } @@ -7969,38 +7998,39 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); - if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) - { - Message(Chat::Red, "You cannot create this guild because this account may only be leader of %i guilds.", RuleI(Guild, PlayerCreationLimit)); + if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) { + Message( + Chat::Red, + "You cannot create this guild because this account may only be leader of %i guilds.", + RuleI(Guild, PlayerCreationLimit)); return; } - if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) - { + if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) { MessageString(Chat::Red, GUILD_NAME_IN_USE); return; } - uint32 NewGuildID = guild_mgr.CreateGuild(GuildName, CharacterID()); - - LogGuilds("[{}]: Creating guild [{}] with leader [{}] via UF+ GUI. It was given id [{}]", GetName(), - GuildName, CharacterID(), (unsigned long)NewGuildID); - - if (NewGuildID == GUILD_NONE) - Message(Chat::Red, "Guild creation failed."); - else - { - if (!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) - Message(Chat::Red, "Unable to set guild leader's guild in the database. Contact a GM."); - else - { - Message(Chat::Yellow, "You are now the leader of %s", GuildName); - - if (zone->GetZoneID() == Zones::GUILDHALL && GuildBanks) - GuildBanks->SendGuildBank(this); - SendGuildRanks(); - } + uint32 new_guild_id = guild_mgr.CreateGuild(GuildName, CharacterID()); + if (new_guild_id == GUILD_NONE) { + Message(Chat::Red, "Guild creation failed. An account can only have one Guild Leader."); + return; } + + SetGuildID(new_guild_id); + SendGuildList(); + guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName()); + guild_mgr.SendToWorldSendGuildList(); + SendGuildSpawnAppearance(); + + Message(Chat::Yellow, "You are now the leader of %s", GuildName); + + LogGuilds("[{}]: Creating guild [{}] with leader [{}] via UF+ GUI. It was given id [{}]", + GetName(), + GuildName, + CharacterID(), + new_guild_id + ); } void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) @@ -8012,7 +8042,7 @@ void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) else { LogGuilds("Deleting guild [{}] ([{}])", guild_mgr.GetGuildName(GuildID()), GuildID()); if (!guild_mgr.DeleteGuild(GuildID())) - Message(Chat::Red, "Guild delete failed."); + Message(Chat::Red, "Guild delete failed. Do you have items in the Guild Bank?"); else { Message(Chat::White, "Guild successfully deleted."); } @@ -8021,53 +8051,91 @@ void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) { - LogGuilds("Received OP_GuildDemote"); - if (app->size != sizeof(GuildDemoteStruct)) { LogGuilds("Error: app size of [{}] != size of GuildDemoteStruct of [{}]\n", app->size, sizeof(GuildDemoteStruct)); return; } - if (!IsInAGuild()) + if (!IsInAGuild()) { Message(Chat::Red, "Error: You aren't in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) - Message(Chat::Red, "You don't have permission to demote."); - else if (!worldserver.Connected()) - Message(Chat::Red, "Error: World server disconnected"); - else { - GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; + return; + } - CharGuildInfo gci; - if (!guild_mgr.GetCharInfo(demote->target, gci)) { - Message(Chat::Red, "Unable to find '%s'", demote->target); - return; - } - if (gci.guild_id != GuildID()) { - Message(Chat::Red, "You aren't in the same guild, what do you think you are doing?"); + GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; + auto rank = demote->rank; + auto target = demote->target; + auto target_client = entity_list.GetClientByName(target); + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(demote->target, gci)) { + Message(Chat::Red, "Unable to find '%s'", demote->target); + return; + } + + if (gci.rank == GUILD_LEADER) { + Message(Chat::Red, "You cannot demote the guild leader."); + return; + } + + if (gci.guild_id != GuildID()) { + Message(Chat::Red, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (rank > GUILD_RECRUIT) { + Message(Chat::Red, "%s cannot be demoted any further!", demote->target); + return; + } + + if (((strcasecmp(GetCleanName(), target) == 0 && + guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_DEMOTE_SELF)) || + ((guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_DEMOTE)) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() <= GUILD_OFFICER)) && + gci.rank > GuildRank())) + { + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) + { + Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, demote->target); + LogGuilds("Demoting [{}] ([{}]) from rank [{}] ([{}]) to [{}] ([{}]) in [{}] ([{}]) FAILED.", + demote->target, + gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), + gci.rank, + guild_mgr.GetRankName(GuildID(), rank), + rank, + guild_mgr.GetGuildName(GuildID()), + GuildID() + ); return; } - if (gci.rank < 1) { - Message(Chat::Red, "%s cannot be demoted any further!", demote->target); - return; + auto c = entity_list.GetClientByName(demote->target); + if (c) { + c->SetGuildRank(rank); + c->SendAppearancePacket(AppearanceType::GuildRank, rank, false); } - uint8 rank = gci.rank - 1; + bool banker_status = guild_mgr.GetGuildBankerStatus(gci.guild_id, rank); + if (gci.banker != banker_status) { + gci.banker = banker_status; + } + guild_mgr.UpdateDbBankerFlag(gci.char_id, gci.banker); + guild_mgr.MemberRankUpdate(gci.guild_id, rank, gci.banker, gci.alt, false, demote->target); LogGuilds("Demoting [{}] ([{}]) from rank [{}] ([{}]) to [{}] ([{}]) in [{}] ([{}])", - demote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, demote->target); - return; - } - Message(Chat::White, "Successfully demoted %s to rank %d", demote->target, rank); + demote->target, + gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), + gci.rank, + guild_mgr.GetRankName(GuildID(), rank), + rank, + guild_mgr.GetGuildName(GuildID()), + GuildID() + ); + } + else { + Message(Chat::Red, "You do not have sufficient privileges to demote '%s'.", target); } - // SendGuildMembers(GuildID(), true); - return; } void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) @@ -8080,124 +8148,169 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) } GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; - - if (!IsInAGuild()) - Message(Chat::Red, "Error: You are not in a guild!"); - else if (gc->officer > GUILD_MAX_RANK) - Message(Chat::Red, "Invalid rank."); - else if (!worldserver.Connected()) - Message(Chat::Red, "Error: World server disconnected"); - else { - - //ok, the invite is also used for changing rank as well. - Mob* invitee = entity_list.GetMob(gc->othername); - - if (!invitee) { - Message(Chat::Red, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); - return; - } - - if (invitee->IsClient()) { - Client* client = invitee->CastToClient(); - - //ok, figure out what they are trying to do. - if (client->GuildID() == GuildID()) { - //they are already in this guild, must be a promotion or demotion - if (gc->officer < client->GuildRank()) { - //demotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { - Message(Chat::Red, "You don't have permission to demote."); - return; - } - - //we could send this to the member and prompt them to see if they want to - //be demoted (I guess), but I dont see a point in that. - - LogGuilds("[{}] ([{}]) is demoting [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { - Message(Chat::Red, "There was an error during the demotion, DB may now be inconsistent."); - return; - } - - } - else if (gc->officer > client->GuildRank()) { - //promotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { - Message(Chat::Red, "You don't have permission to demote."); - return; - } - - LogGuilds("[{}] ([{}]) is asking to promote [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the promotion with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if (gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - LogGuilds("Sending OP_GuildInvite for promotion to [{}], length [{}]", client->GetName(), app->size); - client->QueuePacket(app); - - } - else { - Message(Chat::Red, "That member is already that rank."); - return; - } + auto rank = gc->officer; + if (ClientVersion() < EQ::versions::ClientVersion::RoF) { + switch (gc->officer) { + case GUILD_MEMBER_TI: { + rank = GUILD_MEMBER; + break; } - else if (!client->IsInAGuild()) { - //they are not in this or any other guild, this is an invite - // - if (client->GetPendingGuildInvitation()) - { - Message(Chat::Red, "That person is already considering a guild invitation."); + case GUILD_OFFICER_TI: { + rank = GUILD_OFFICER; + break; + } + case GUILD_LEADER_TI: { + rank = GUILD_LEADER; + break; + } + } + } + + if (!IsInAGuild()) { + Message(Chat::Red, "Error: You are not in a guild!"); + return; + } + + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_INVITE) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) { + Message(Chat::Red, "Invalid rank."); + return; + } + //ok, the invite is also used for changing rank as well. + Mob *invitee = entity_list.GetMob(gc->othername); + + if (!invitee) { + Message( + Chat::Red, + "Prospective guild member %s must be in zone to preform guild operations on them.", + gc->othername + ); + return; + } + + if (invitee->IsClient()) { + Client *client = invitee->CastToClient(); + + //ok, figure out what they are trying to do. + if (client && client->GuildID() == GuildID()) { + //they are already in this guild, must be a promotion or demotion + if (rank > client->GuildRank()) { + //demotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_DEMOTE) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) { + Message(Chat::Red, "You don't have permission to demote."); return; } - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { - Message(Chat::Red, "You don't have permission to invite."); + //we could send this to the member and prompt them to see if they want to + //be demoted (I guess), but I dont see a point in that. + + LogGuilds("[{}] ([{}]) is demoting [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(client->CharacterID(), rank)) { + Message(Chat::Red, "There was an error during the demotion, DB may now be inconsistent."); return; } - LogGuilds("Inviting [{}] ([{}]) into guild [{}] ([{}])", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); + } + else if (rank < client->GuildRank()) { + //promotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_PROMOTE) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER) + ) { + Message(Chat::Red, "You don't have permission to demote."); + return; + } - //record the invite with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + LogGuilds("[{}] ([{}]) is asking to promote [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); - if (gc->guildeqid == 0) + //record the promotion with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), rank); + + if (gc->guildeqid == 0) { gc->guildeqid = GuildID(); + } + + LogGuilds("Sending OP_GuildInvite for promotion to [{}], length [{}]", client->GetName(), app->size); // Convert Membership Level between RoF and previous clients. - if (client->ClientVersion() < EQ::versions::ClientVersion::RoF && ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - gc->officer = 0; + if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF) { + gc->officer = rank; + if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF && + ClientVersion() < EQ::versions::ClientVersion::RoF) { + auto outapp = new EQApplicationPacket( + OP_GuildPromote, + sizeof(GuildPromoteStruct)); + GuildPromoteStruct *gps = (GuildPromoteStruct *) outapp->pBuffer; + strn0cpy(gps->name, gc->myname, sizeof(gps->name)); + strn0cpy(gps->target, gc->othername, sizeof(gps->target)); + gps->myrank = GuildRank(); + gps->rank = rank; + Handle_OP_GuildPromote(outapp); + safe_delete(outapp); + return; + } } - if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF && ClientVersion() < EQ::versions::ClientVersion::RoF) - { - gc->officer = 8; - } - - LogGuilds("Sending OP_GuildInvite for invite to [{}], length [{}]", client->GetName(), app->size); - client->SetPendingGuildInvitation(true); client->QueuePacket(app); - } else { - //they are in some other guild - Message(Chat::Red, "Player is in a guild."); + Message(Chat::Red, "That member is already that rank."); return; } } + else if (client && !client->IsInAGuild()) { + //they are not in this or any other guild, this is an invite + // + if (client->GetPendingGuildInvitation()) { + Message(Chat::Red, "That person is already considering a guild invitation."); + return; + } + + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_INVITE) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) { + Message(Chat::Red, "You don't have permission to invite."); + return; + } + + LogGuilds("Inviting [{}] ([{}]) into guild [{}] ([{}])", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the invite with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), rank); + + if (gc->guildeqid == 0) { + gc->guildeqid = GuildID(); + } + + // Convert Membership Level between RoF and previous clients. + if (client->ClientVersion() < EQ::versions::ClientVersion::RoF && + ClientVersion() >= EQ::versions::ClientVersion::RoF) { + gc->officer = GUILD_MEMBER_TI; + } + if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF && + ClientVersion() < EQ::versions::ClientVersion::RoF) { + gc->officer = GUILD_RECRUIT; + } + + LogGuilds("Sending OP_GuildInvite for invite to [{}], length [{}]", client->GetName(), app->size); + client->SetPendingGuildInvitation(true); + client->QueuePacket(app); + + } + else { + //they are in some other guild + Message(Chat::Red, "Player is in a guild."); + return; + } } } @@ -8208,169 +8321,123 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) SetPendingGuildInvitation(false); if (app->size != sizeof(GuildInviteAccept_Struct)) { - std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; + LogGuilds("Wrong size: OP_GuildInviteAccept, size={}, expected {}.", app->size, sizeof(GuildJoin_Struct)); return; } GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + auto invitor = gj->inviter; + auto invitee = gj->new_member; + auto guild_id = gj->guild_id; + auto response = gj->response; - uint32 guildrank = gj->response; - - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - if (gj->response > 9) + if (ClientVersion() < EQ::versions::ClientVersion::RoF) { + switch (response) { + case GUILD_MEMBER_TI: { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage( - gj->inviter, - 0, - Chat::White, - fmt::format( - "{} has declined to join the guild.", - GetCleanName() - ).c_str() - ); - return; + response = GUILD_RECRUIT; + break; + } + case GUILD_OFFICER_TI: + { + response = GUILD_OFFICER; + break; + } + default: + { + response = GUILD_RANK_NONE; + break; + } } } - if (gj->response == 5 || gj->response == 4) { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage( - gj->inviter, - 0, - Chat::White, - fmt::format( - "{} has declined to join the guild.", - GetCleanName() - ).c_str() - ); + + Client* c_invitor = entity_list.GetClientByName(invitor); + Client* c_invitee = entity_list.GetClientByName(invitee); + if (!c_invitee) { return; } - //uint32 tmpeq = gj->guildeqid; - if (IsInAGuild() && gj->response == GuildRank()) - Message(Chat::Red, "Error: You're already in a guild!"); - else if (!worldserver.Connected()) - Message(Chat::Red, "Error: World server disconnected"); - else { - LogGuilds("Guild Invite Accept: guild [{}], response [{}], inviter [{}], person [{}]", - gj->guildeqid, gj->response, gj->inviter, gj->newmember); - - //ok, the invite is also used for changing rank as well. - Mob* inviter = entity_list.GetMob(gj->inviter); - - if (inviter && inviter->IsClient()) - { - Client* client = inviter->CastToClient(); - // Convert Membership Level between RoF and previous clients. - if (client->ClientVersion() < EQ::versions::ClientVersion::RoF && ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - guildrank = 0; - } - if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF && ClientVersion() < EQ::versions::ClientVersion::RoF) - { - guildrank = 8; - } - } - //we dont really care a lot about what this packet means, as long as - //it has been authorized with the guild manager - if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, guildrank)) { - worldserver.SendEmoteMessage( - gj->inviter, - 0, - Chat::White, - fmt::format( - "{} has sent an invalid response to your invite!", - GetCleanName() - ).c_str() - ); - Message(Chat::Red, "Invalid invite response packet!"); - return; - } - - if (gj->guildeqid == GuildID()) { - //only need to change rank. - - LogGuilds("Changing guild rank of [{}] ([{}]) to rank [{}] in guild [{}] ([{}])", - GetName(), CharacterID(), - gj->response, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { - Message(Chat::Red, "There was an error during the rank change, DB may now be inconsistent."); - return; - } - } - else { - - LogGuilds("Adding [{}] ([{}]) to guild [{}] ([{}]) at rank [{}]", - GetName(), CharacterID(), - guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, - gj->response); - - //change guild and rank - - guildrank = gj->response; - - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - if (gj->response == 8) - { - guildrank = 0; - } - } - - if (!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { - Message(Chat::Red, "There was an error during the invite, DB may now be inconsistent."); - return; - } - if (zone->GetZoneID() == Zones::GUILDHALL && GuildBanks) - GuildBanks->SendGuildBank(this); + if (!c_invitee || !c_invitor) { + guild_mgr.VerifyAndClearInvite(CharacterID(), guild_id, gj->response); + Message(Chat::Yellow, "Both players must be in the same zone."); + if (c_invitor) { + c_invitor->Message(Chat::Yellow, "Both players must be in the same zone."); } + return; } + + if (response >= GUILD_INVITE_DECLINE) { + guild_mgr.VerifyAndClearInvite(CharacterID(), guild_id, gj->response); + Message(Chat::Yellow, "You declined the guild invite."); + if (c_invitor) { + c_invitor->Message(Chat::Yellow, "The player declined the guild invite."); + } + return; + } + + if (c_invitor && IsInAGuild() && GuildID() != c_invitor->GuildID()) { + guild_mgr.VerifyAndClearInvite(CharacterID(), guild_id, gj->response); + Message(Chat::Yellow, "You are already in a guild. Please leave that guild first."); + c_invitor->Message(Chat::Yellow, "Player is already in a guild."); + return; + } + + guild_mgr.VerifyAndClearInvite(CharacterID(), guild_id, gj->response); + c_invitee->SetGuildID(guild_id); + c_invitee->SetGuildListDirty(false); + guild_mgr.MemberAdd(guild_id, c_invitee->CharacterID(), c_invitee->GetLevel(), c_invitee->GetClass(), response, c_invitee->GetZoneID(), std::string(c_invitee->GetCleanName())); + SendGuildSpawnAppearance(); + + LogGuilds("Adding [{}] ([{}]) to guild [{}] ([{}]) at rank [{}]", + GetName(), + CharacterID(), + guild_mgr.GetGuildName(guild_id), + guild_id, + response + ); } void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) { - LogGuilds("Received OP_GuildLeader"); - - if (app->size < 2) { - LogGuilds("Invalid length [{}] on OP_GuildLeader", app->size); + if (app->size != sizeof(GuildMakeLeader_Struct)) { + LogError("Received OP_Handle_GuildLeader packet of size {} which should be {}", app->size, sizeof(GuildMakeLeader_Struct)); return; } - app->pBuffer[app->size - 1] = 0; - GuildMakeLeader* gml = (GuildMakeLeader*)app->pBuffer; - if (!IsInAGuild()) - Message(Chat::Red, "Error: You aren't in a guild!"); - else if (GuildRank() != GUILD_LEADER) - Message(Chat::Red, "Error: You aren't the guild leader!"); - else if (!worldserver.Connected()) - Message(Chat::Red, "Error: World server disconnected"); - else { - - //NOTE: we could do cross-zone lookups here... - - Client* newleader = entity_list.GetClientByName(gml->target); - if (newleader) { - - LogGuilds("Transfering leadership of [{}] ([{}]) to [{}] ([{}])", - guild_mgr.GetGuildName(GuildID()), GuildID(), - newleader->GetName(), newleader->CharacterID()); - - if (guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())) { - Message(Chat::White, "Successfully Transfered Leadership to %s.", gml->target); - newleader->Message(Chat::Yellow, "%s has transfered the guild leadership into your hands.", GetName()); - } - else - Message(Chat::Red, "Could not change leadership at this time."); - } - else - Message(Chat::Red, "Failed to change leader, could not find target."); + GuildMakeLeader_Struct* gcl = (GuildMakeLeader_Struct*)app->pBuffer; + auto new_leader = entity_list.GetClientByName(gcl->new_leader); + if (!new_leader) { + Message(Chat::Red, "Both players must be in the same zone to transfer guild leadership."); + return; + } + + if (GuildRank() != GUILD_LEADER) { + Message(Chat::Red, "You must be the guild leader to reassign guild leadership."); + return; + } + + if (guild_mgr.SetGuildLeader(GuildID(), new_leader->CharacterID())) { + Message(Chat::White, "Successfully Transfered Leadership to %s.", new_leader->GetCleanName()); + new_leader->Message(Chat::Yellow, "%s has transfered the guild leadership into your hands.", GetName()); + + SetGuildRank(GUILD_OFFICER); + guild_mgr.MemberRankUpdate(guild_id, GUILD_OFFICER, false, false, false, GetName()); + + new_leader->SetGuildRank(GUILD_LEADER); + guild_mgr.MemberRankUpdate(guild_id, GUILD_LEADER, true, false, false, new_leader->GetName()); + + LogGuilds("Transfering leadership of [{}] ([{}]) to [{}] ([{}])", + guild_mgr.GetGuildName(GuildID()), + GuildID(), + new_leader->GetCleanName(), + new_leader->CharacterID() + ); + } + else + { + Message(Chat::Red, "Could not change leadership at this time."); + LogGuilds("Tranfer of guild leadership for guild id {} failed.", GuildID()); } - // SendGuildMembers(GuildID(), true); return; } @@ -8396,7 +8463,7 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) Message(Chat::Red, "Unable to find '%s'", gmb->member); return; } - bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); + bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id, true); bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); @@ -8412,7 +8479,8 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) if (IsCurrentlyAnAlt != NewAltStatus) { - bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); + bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || + guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_CHANGE_ALT_FLAG_FOR_OTHER); if (!IsAllowed) { @@ -8428,6 +8496,7 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) if (IsCurrentlyABanker != NewBankerStatus) { + gci.banker = NewBankerStatus; if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { Message(Chat::Red, "Error setting guild banker flag."); return; @@ -8440,6 +8509,7 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) } if (IsCurrentlyAnAlt != NewAltStatus) { + gci.alt = NewAltStatus; if (!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { Message(Chat::Red, "Error setting guild alt flag."); return; @@ -8450,6 +8520,7 @@ void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) else Message(Chat::White, "%s is no longer marked as an alt.", gmb->member); } + guild_mgr.MemberRankUpdate(gci.guild_id, gci.rank, gci.banker, gci.alt, false, gmb->member); } void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) @@ -8460,21 +8531,41 @@ void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) { - LogGuilds("Received OP_GuildPromote"); - if (app->size != sizeof(GuildPromoteStruct)) { LogGuilds("Error: app size of [{}] != size of GuildDemoteStruct of [{}]\n", app->size, sizeof(GuildPromoteStruct)); return; } - if (!IsInAGuild()) + if (!IsInAGuild()) { Message(Chat::Red, "Error: You aren't in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) + } + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_PROMOTE) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) { Message(Chat::Red, "You don't have permission to promote."); - else if (!worldserver.Connected()) + } + else if (!worldserver.Connected()) { Message(Chat::Red, "Error: World server disconnected"); + } else { - GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; + auto promote = (GuildPromoteStruct *) app->pBuffer; + + auto rank = promote->myrank; + if (ClientVersion() < EQ::versions::ClientVersion::RoF) { + switch (rank) { + case GUILD_MEMBER_TI: { + rank = GUILD_MEMBER; + break; + } + case GUILD_OFFICER_TI: { + rank = GUILD_OFFICER; + break; + } + case GUILD_LEADER_TI: { + rank = GUILD_LEADER; + break; + } + } + } CharGuildInfo gci; if (!guild_mgr.GetCharInfo(promote->target, gci)) { @@ -8486,15 +8577,12 @@ void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) return; } - uint8 rank = gci.rank + 1; - - if (rank > GUILD_OFFICER) + if (rank == GUILD_LEADER) { Message(Chat::Red, "You cannot promote someone to be guild leader. You must use /guildleader."); return; } - LogGuilds("Promoting [{}] ([{}]) from rank [{}] ([{}]) to [{}] ([{}]) in [{}] ([{}])", promote->target, gci.char_id, guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, @@ -8505,7 +8593,17 @@ void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, promote->target); return; } - Message(Chat::White, "Successfully promoted %s to rank %d", promote->target, rank); + + auto c = entity_list.GetClientByName(promote->target); + if (c) { + c->SetGuildRank(rank); + c->SendAppearancePacket(AppearanceType::GuildRank, rank, false); + } + + bool banker_status = guild_mgr.GetGuildBankerStatus(gci.guild_id, rank); + + guild_mgr.UpdateDbBankerFlag(gci.char_id, gci.banker); + guild_mgr.MemberRankUpdate(gci.guild_id, rank, gci.banker, gci.alt, false, promote->target); } return; } @@ -8514,11 +8612,11 @@ void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) { LogGuilds("Received OP_GuildPublicNote"); - if (app->size < sizeof(GuildUpdate_PublicNote)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n", app->size, sizeof(GuildUpdate_PublicNote)); + if (app->size != sizeof(GuildUpdate_PublicNote)) { + LogGuilds("Received packet to update a guild public note for {} of wrong size.", GetCleanName()); return; } + GuildUpdate_PublicNote* gpn = (GuildUpdate_PublicNote*)app->pBuffer; CharGuildInfo gci; @@ -8531,38 +8629,48 @@ void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) return; } + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_EDIT_PUBLIC_NOTES) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) + { + Message(Chat::Red, "You do not have access to update public guild notes."); + return; + } + LogGuilds("Setting public note on [{}] ([{}]) in guild [{}] ([{}]) to: [{}]", gpn->target, gci.char_id, guild_mgr.GetGuildName(GuildID()), GuildID(), gpn->note); - if (!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { + if (!guild_mgr.SetPublicNote(gci.char_id, std::string(gpn->note))) { Message(Chat::Red, "Failed to set public note on %s", gpn->target); } else { Message(Chat::White, "Successfully changed public note on %s", gpn->target); + guild_mgr.SendToWorldMemberPublicNote(GuildID(), gpn->target, gpn->note); } - // SendGuildMembers(GuildID(), true); - return; } void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) { - LogGuilds("Received OP_GuildRemove"); - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + LogGuilds("Wrong size: OP_GuildRemove, size = {} expected {}.", + app->size, + sizeof(GuildCommand_Struct) + ); return; } + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; - if (!IsInAGuild()) + + if (!IsInAGuild()) { Message(Chat::Red, "Error: You aren't in a guild!"); - // we can always remove ourself, otherwise, our rank needs remove permissions - else if (strcasecmp(gc->othername, GetName()) != 0 && - !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) + } + else if ((strcasecmp(gc->othername, GetName()) != 0 && + !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_MEMBERS_REMOVE)) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER) && + strcasecmp(gc->othername, GetName()) != 0) { Message(Chat::Red, "You don't have permission to remove guild members."); - else if (!worldserver.Connected()) - Message(Chat::Red, "Error: World server disconnected"); + } else { uint32 char_id; Client* client = entity_list.GetClientByName(gc->othername); @@ -8572,8 +8680,7 @@ void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) Message(Chat::Red, "You aren't in the same guild, what do you think you are doing?"); return; } - char_id = client->CharacterID(); - + guild_mgr.RemoveMember(client->GuildID(), client->CharacterID(), std::string(client->GetCleanName())); LogGuilds("Removing [{}] ([{}]) from guild [{}] ([{}])", client->GetName(), client->CharacterID(), guild_mgr.GetGuildName(GuildID()), GuildID()); @@ -8593,21 +8700,9 @@ void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) LogGuilds("Removing remote/offline [{}] ([{}]) into guild [{}] ([{}])", gci.char_name.c_str(), gci.char_id, guild_mgr.GetGuildName(GuildID()), GuildID()); + guild_mgr.RemoveMember(gci.guild_id, gci.char_id, gci.char_name); } - - if (!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { - auto outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); - GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*)outapp->pBuffer; - gm->guildeqid = GuildID(); - strcpy(gm->member, gc->othername); - Message(Chat::White, "%s successfully removed from your guild.", gc->othername); - entity_list.QueueClientsGuild(this, outapp, false, GuildID()); - safe_delete(outapp); } - else - Message(Chat::Red, "Unable to remove %s from your guild.", gc->othername); - } - // SendGuildMembers(GuildID(), true); return; } @@ -8643,56 +8738,107 @@ void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) if (!GuildName) return; - bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); - bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); - - if ((TargetGuildID == GuildID()) && (c != this)) - { - if (IsLeader) - MessageString(Chat::LightGray, LEADER_OF_YOUR_GUILD, c->GetName()); - else if (IsOfficer) - MessageString(Chat::LightGray, OFFICER_OF_YOUR_GUILD, c->GetName()); - else - MessageString(Chat::LightGray, MEMBER_OF_YOUR_GUILD, c->GetName()); - - return; + auto rank = c->GuildRank(); + switch (rank) { + case GUILD_LEADER: { + MessageString(Chat::LightGray, LEADER_OF_X_GUILD, c->GetName(), GuildName); + break; + } + case GUILD_SENIOR_OFFICER: + case GUILD_OFFICER: { + MessageString(Chat::LightGray, OFFICER_OF_X_GUILD, c->GetName(), GuildName); + break; + } + case GUILD_SENIOR_MEMBER: + case GUILD_MEMBER: + case GUILD_JUNIOR_MEMBER: + case GUILD_INITIATE: + case GUILD_RECRUIT: { + MessageString(Chat::LightGray, MEMBER_OF_X_GUILD, c->GetName(), GuildName); + break; + } } - - if (IsLeader) - MessageString(Chat::LightGray, LEADER_OF_X_GUILD, c->GetName(), GuildName); - else if (IsOfficer) - MessageString(Chat::LightGray, OFFICER_OF_X_GUILD, c->GetName(), GuildName); - else - MessageString(Chat::LightGray, MEMBER_OF_X_GUILD, c->GetName(), GuildName); } -void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) +void Client::Handle_OP_GuildUpdate(const EQApplicationPacket *app) { - if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) - { - LogDebug("Size mismatch in OP_GuildUpdateURLAndChannel expected [{}] got [{}]", sizeof(GuildUpdateURLAndChannel_Struct), app->size); - - DumpPacket(app); - + if (!IsInAGuild()) { return; } - GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; + GuildUpdateUCPStruct *gup = (GuildUpdateUCPStruct *) app->pBuffer; - if (!IsInAGuild()) - return; + switch (gup->action) { + case GuildUpdateURL: { + if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) { + LogDebug("Size mismatch in OP_GuildUpdateURLAndChannel expected [{}] got [{}]", + sizeof(GuildUpdateURLAndChannel_Struct), + app->size); + DumpPacket(app); + return; + } - if (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(Chat::Red, "Only the guild leader can change the Channel or URL.!"); - return; + guild_mgr.SetGuildURL(GuildID(), gup->payload.url_channel.text); + guild_mgr.SendToWorldGuildURL(GuildID(), gup->payload.url_channel.text); + + break; + } + case GuildUpdateChannel: { + if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) { + LogDebug("Size mismatch in OP_GuildUpdateURLAndChannel expected [{}] got [{}]", + sizeof(GuildUpdateURLAndChannel_Struct), + app->size); + DumpPacket(app); + return; + } + + guild_mgr.SetGuildChannel(GuildID(), gup->payload.url_channel.text); + guild_mgr.SendToWorldGuildChannel(GuildID(), gup->payload.url_channel.text); + break; + } + case GuildUpdateRanks: { + if (!guild_mgr.CheckPermission(guild_id, guildrank, GUILD_ACTION_RANKS_CHANGE_RANK_NAMES)) { + MessageString(Chat::Yellow, GUILD_PERMISSION_FAILED); + return; + } + auto rank = gup->payload.rank_name.rank; + std::string rank_name(gup->payload.rank_name.rank_name); + + if (rank > GUILD_MAX_RANK || rank < 0 || rank_name.empty()) { + LogGuilds("Received packet to update rank though rank {} or rank name {} was incorrect.", + rank, + rank_name.c_str()); + return; + } + guild_mgr.UpdateRankName(guild_id, rank, rank_name); + guild_mgr.SendRankName(guild_id, rank, rank_name); + + break; + } + case GuildUpdatePermissions: { + if (!guild_mgr.CheckPermission(guild_id, guildrank, GUILD_ACTION_RANKS_CHANGE_PERMISSIONS)) { + MessageString(Chat::Yellow, GUILD_PERMISSION_FAILED); + return; + } + guild_mgr.UpdateRankPermission( + guild_id, + character_id, + gup->payload.permissions.function_id, + gup->payload.permissions.rank, + gup->payload.permissions.value + ); + guild_mgr.SendPermissionUpdate( + guild_id, + gup->payload.permissions.rank, + gup->payload.permissions.function_id, + gup->payload.permissions.value + ); + + break; + } + default: + break; } - - if (guuacs->Action == 0) - guild_mgr.SetGuildURL(GuildID(), guuacs->Text); - else - guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); - } void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) @@ -10725,7 +10871,6 @@ void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) st->response = 1; QueuePacket(app); tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); } else { st->response = 0; @@ -13041,6 +13186,9 @@ void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) { SendGuildRanks(); SendGuildMembers(); + if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { + SendGuildRankNames(); + } } return; } @@ -13460,7 +13608,9 @@ void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) Message(Chat::Red, "You are not in a guild!"); return; } - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_CHANGE_THE_MOTD) || + (ClientVersion() < EQ::versions::ClientVersion::RoF && GuildRank() > GUILD_OFFICER)) + { Message(Chat::Red, "You do not have permissions to edit your guild's MOTD."); return; } @@ -16683,3 +16833,300 @@ void Client::ReloadExpansionProfileSetting() m_pp.expansions = RuleI(World, ExpansionSettings); } } + +void Client::Handle_OP_GuildTributeSelect(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeSelectReq_Struct)) { + LogError("Invalid size on OP_GuildSelectTribute packet"); + return; + } + + GuildTributeSelectReq_Struct *t = (GuildTributeSelectReq_Struct *) app->pBuffer; + SendGuildTributeDetails(t->tribute_id, t->tier); + return; +} + +void Client::Handle_OP_GuildTributeModifyBenefits(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeModifyBenefits_Struct)) { + LogError("Invalid size on Handle_OP_GuildModifyBenefits packet"); + return; + } + + GuildTributeModifyBenefits_Struct *t = (GuildTributeModifyBenefits_Struct *) app->pBuffer; + + EQApplicationPacket *outapp = new EQApplicationPacket( + OP_GuildModifyBenefits, + sizeof(GuildTributeModifyBenefits_Struct)); + GuildTributeModifyBenefits_Struct *gmbs = (GuildTributeModifyBenefits_Struct *) outapp->pBuffer; + + switch (t->command) { + case GUILD_TRIBUTES_SAVE: { + gmbs->command = GUILD_TRIBUTES_SAVE; + gmbs->data = 1; + gmbs->tribute_id_1 = t->tribute_id_1; + gmbs->tribute_id_2 = t->tribute_id_2; + gmbs->tribute_id_1_tier = t->tribute_id_1_tier; + gmbs->tribute_id_2_tier = t->tribute_id_2_tier; + gmbs->tribute_master_id = t->tribute_master_id; + QueuePacket(outapp); + + auto sp = new ServerPacket(ServerOP_RequestGuildActiveTributes, sizeof(GuildTributeUpdate)); + auto out = (GuildTributeUpdate *) sp->pBuffer; + out->guild_id = GuildID(); + out->enabled = false; + worldserver.SendPacket(sp); + safe_delete(sp); + break; + } + case GUILD_TRIBUTES_MODIFY: { + gmbs->command = GUILD_TRIBUTES_MODIFY; + gmbs->data = 1; + gmbs->tribute_id_1 = t->tribute_id_1; + gmbs->tribute_id_2 = t->tribute_id_2; + gmbs->tribute_id_1_tier = t->tribute_id_1_tier; + gmbs->tribute_id_2_tier = t->tribute_id_2_tier; + gmbs->tribute_master_id = t->tribute_master_id; + QueuePacket(outapp); + break; + } + default: { + gmbs->command = GUILD_TRIBUTES_SAVE; + gmbs->data = 1; + gmbs->tribute_id_1 = 0; + gmbs->tribute_id_2 = 0; + gmbs->tribute_id_1_tier = 0; + gmbs->tribute_id_2_tier = 0; + gmbs->tribute_master_id = 0; + QueuePacket(outapp); + break; + } + } + safe_delete(outapp) + return; +} + +void Client::Handle_OP_GuildTributeOptInOut(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeOptInOutReq_Struct)) { + LogError("Invalid size on Handle_OP_GuildTributeOptInOut packet"); + return; + } + + GuildTributeOptInOutReq_Struct *in = (GuildTributeOptInOutReq_Struct *) app->pBuffer; + + CharGuildInfo gci; + guild_mgr.GetCharInfo(in->player, gci); + guild_mgr.UpdateDbMemberTributeEnabled(GuildID(), gci.char_id, in->tribute_toggle); + + auto *sout = new ServerPacket( + ServerOP_GuildTributeOptInToggle, + sizeof(GuildTributeMemberToggle)); + auto *out = (GuildTributeMemberToggle *) sout->pBuffer; + + out->guild_id = GuildID(); + out->char_id = gci.char_id; + out->tribute_toggle = in->tribute_toggle; + out->command = in->command; + strn0cpy(out->player_name, in->player, sizeof(out->player_name)); + + worldserver.SendPacket(sout); + safe_delete(sout) +} + +void Client::Handle_OP_GuildTributeSaveActiveTributes(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeSaveActive_Struct)) { + LogError("Invalid size on Handle_OP_GuildTributeOptInOut packet"); + return; + } + + GuildTributeSaveActive_Struct *data = (GuildTributeSaveActive_Struct *) app->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(GuildID()); + if (guild) { + GuildTributesRepository::GuildTributes gt{}; + + gt.guild_id = GuildID(); + gt.tribute_id_1 = data->tribute_id_1; + gt.tribute_id_2 = data->tribute_id_2; + gt.tribute_id_1_tier = data->tribute_1_tier; + gt.tribute_id_2_tier = data->tribute_2_tier; + gt.enabled = 0; + gt.time_remaining = RuleI(Guild, TributeTime); + GuildTributesRepository::ReplaceOne(database, gt); + + guild->tribute.enabled = 0; + guild->tribute.id_1 = data->tribute_id_1; + guild->tribute.id_2 = data->tribute_id_2; + guild->tribute.id_1_tier = data->tribute_1_tier; + guild->tribute.id_2_tier = data->tribute_2_tier; + guild->tribute.time_remaining = RuleI(Guild, TributeTime); + + ServerPacket *sp = new ServerPacket(ServerOP_GuildTributeUpdate, sizeof(GuildTributeUpdate)); + GuildTributeUpdate *gtu = (GuildTributeUpdate *) sp->pBuffer; + + gtu->guild_id = GuildID(); + gtu->tribute_id_1 = data->tribute_id_1; + gtu->tribute_id_2 = data->tribute_id_2; + gtu->tribute_id_1_tier = data->tribute_1_tier; + gtu->tribute_id_2_tier = data->tribute_2_tier; + gtu->enabled = 0; + gtu->favor = guild->tribute.favor; + gtu->time_remaining = RuleI(Guild, TributeTime); + + worldserver.SendPacket(sp); + safe_delete(sp) + } +} + +void Client::Handle_OP_GuildTributeToggle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeToggleReq_Struct)) { + LogError("Invalid size on Handle_OP_GuildModifyBenefits packet"); + return; + } + + GuildTributeToggleReq_Struct *gt = (GuildTributeToggleReq_Struct *) app->pBuffer; + + ServerPacket *sp = new ServerPacket(ServerOP_GuildTributeActivate, sizeof(GuildTributeUpdate)); + GuildTributeUpdate *gtu = (GuildTributeUpdate *) sp->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(GuildID()); + if (guild) { + switch (gt->command) { + case GUILD_TRIBUTES_OFF: { + gtu->guild_id = GuildID(); + gtu->enabled = GUILD_TRIBUTES_OFF; + gtu->favor = guild->tribute.favor; + gtu->tribute_id_1 = guild->tribute.id_1; + gtu->tribute_id_2 = guild->tribute.id_2; + gtu->tribute_id_1_tier = guild->tribute.id_1_tier; + gtu->tribute_id_2_tier = guild->tribute.id_2_tier; + gtu->time_remaining = guild->tribute.time_remaining; + + worldserver.SendPacket(sp); + break; + } + case GUILD_TRIBUTES_ON: { + gtu->guild_id = GuildID(); + gtu->enabled = GUILD_TRIBUTES_ON; + gtu->favor = guild->tribute.favor; + gtu->tribute_id_1 = guild->tribute.id_1; + gtu->tribute_id_2 = guild->tribute.id_2; + gtu->tribute_id_1_tier = guild->tribute.id_1_tier; + gtu->tribute_id_2_tier = guild->tribute.id_2_tier; + gtu->time_remaining = guild->tribute.time_remaining; + + worldserver.SendPacket(sp); + break; + } + default: { + LogError("Unknown GuildTributeUpdate to WorldServer command received."); + break; + } + } + } + safe_delete(sp) + + RequestGuildFavorAndTimer(GuildID()); + + return; +} + +void Client::Handle_OP_GuildTributeDonateItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeDonateItemRequest_Struct)) { + LogError("Invalid size on Handle_OP_GuildTributeDonateItemRequest packet"); + return; + } + + auto in = (GuildTributeDonateItemRequest_Struct *) app->pBuffer; + + const auto *inst = GetInv().GetItem(in->slot); + auto favor = inst->GetItemGuildFavor() * in->quantity; + + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + if (guild) { + guild->tribute.favor += favor; + guild_mgr.UpdateDbGuildFavor(GuildID(), guild->tribute.favor); + auto member_favor = guild_mgr.UpdateDbMemberFavor(GuildID(), CharacterID(), favor); + + if (inst->IsStackable()) { + if (inst->GetCharges() < (int32) in->quantity) { + favor = 0; + } + DeleteItemInInventory(in->slot, in->quantity, false, true); + } + else { + DeleteItemInInventory(in->slot, 0, false, true); + } + + SendGuildTributeDonateItemReply(in, favor); + + if(player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM)) { + auto e = PlayerEvent::GuildTributeDonateItem { + .item_id = inst->GetID(), + .guild_favor = favor + }; + + RecordPlayerEventLog(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM, e); + } + + auto outapp = new ServerPacket(ServerOP_GuildTributeUpdateDonations, sizeof(GuildTributeUpdate)); + GuildTributeUpdate *out = (GuildTributeUpdate *) outapp->pBuffer; + out->guild_id = GuildID(); + out->favor = guild->tribute.favor; + strn0cpy(out->player_name, GetCleanName(), sizeof(out->player_name)); + out->member_time = time(nullptr); + out->member_enabled = GuildTributeOptIn(); + out->member_favor = member_favor; + worldserver.SendPacket(outapp); + safe_delete(outapp) + + RequestGuildFavorAndTimer(GuildID()); + } +} + +void Client::Handle_OP_GuildTributeDonatePlat(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildTributeDonatePlatRequest_Struct)) { + LogError("Invalid size on Handle_OP_GuildTributeDonatePlatRequest_Struct packet"); + return; + } + + auto in = (GuildTributeDonatePlatRequest_Struct *) app->pBuffer; + + auto quantity = in->quantity; + + auto favor = quantity * RuleI(Guild, TributePlatConversionRate); + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + if (guild) { + guild->tribute.favor += favor; + guild_mgr.UpdateDbGuildFavor(GuildID(), guild->tribute.favor); + auto member_favor = guild_mgr.UpdateDbMemberFavor(GuildID(), CharacterID(), favor); + + TakePlatinum(quantity, false); + SendGuildTributeDonatePlatReply(in, favor); + + if(player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_PLAT)) { + auto e = PlayerEvent::GuildTributeDonatePlat { + .plat = quantity, + .guild_favor = favor + }; + + RecordPlayerEventLog(PlayerEvent::GUILD_TRIBUTE_DONATE_PLAT, e); + } + + auto outapp = new ServerPacket(ServerOP_GuildTributeUpdateDonations, sizeof(GuildTributeUpdate)); + GuildTributeUpdate *out = (GuildTributeUpdate *) outapp->pBuffer; + out->guild_id = GuildID(); + out->favor = guild->tribute.favor; + strncpy(out->player_name, GetCleanName(), sizeof(out->player_name)); + out->member_time = time(nullptr); + out->member_enabled = GuildTributeOptIn(); + out->member_favor = member_favor; + worldserver.SendPacket(outapp); + safe_delete(outapp) + + RequestGuildFavorAndTimer(GuildID()); + } +} diff --git a/zone/client_packet.h b/zone/client_packet.h index 68d1a3f94..bb84b3c00 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -166,7 +166,14 @@ void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); void Handle_OP_GuildRemove(const EQApplicationPacket *app); void Handle_OP_GuildStatus(const EQApplicationPacket *app); - void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); + void Handle_OP_GuildTributeModifyBenefits(const EQApplicationPacket* app); + void Handle_OP_GuildTributeOptInOut(const EQApplicationPacket* app); + void Handle_OP_GuildTributeSaveActiveTributes(const EQApplicationPacket* app); + void Handle_OP_GuildTributeSelect(const EQApplicationPacket* app); + void Handle_OP_GuildTributeToggle(const EQApplicationPacket* app); + void Handle_OP_GuildUpdate(const EQApplicationPacket *app); + void Handle_OP_GuildTributeDonateItem(const EQApplicationPacket* app); + void Handle_OP_GuildTributeDonatePlat(const EQApplicationPacket* app); void Handle_OP_GuildWar(const EQApplicationPacket *app); void Handle_OP_Heartbeat(const EQApplicationPacket *app); void Handle_OP_Hide(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 75947a5f4..a35be86f0 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -177,6 +177,10 @@ bool Client::Process() { if (myraid) { myraid->MemberZoned(this); } + if (IsInAGuild()) { + guild_mgr.UpdateDbMemberOnline(CharacterID(), false); + guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + } SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); @@ -196,6 +200,11 @@ bool Client::Process() { } LeaveGroup(); Save(); + if (IsInAGuild()) { + guild_mgr.UpdateDbMemberOnline(CharacterID(), false); + guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + } + if (GetMerc()) { GetMerc()->Save(); @@ -561,6 +570,10 @@ bool Client::Process() { GetMerc()->Save(); GetMerc()->Depop(); } + if (IsInAGuild()) { + guild_mgr.UpdateDbMemberOnline(CharacterID(), false); + guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + } return false; } else if (!linkdead_timer.Enabled()) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 29a0e80a1..24bf38ae7 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2307,8 +2307,10 @@ void EntityList::ChannelMessageFromWorld(const char *from, const char *to, if (chan_num == ChatChannel_Guild) { if (!client->IsInGuild(guild_id)) continue; - if (!guild_mgr.CheckPermission(guild_id, client->GuildRank(), GUILD_HEAR)) - continue; + if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF) { + if (!guild_mgr.CheckPermission(guild_id, client->GuildRank(), GUILD_ACTION_GUILD_CHAT_SEE)) + continue; + } if (client->GetFilter(FilterGuildChat) == FilterHide) continue; } else if (chan_num == ChatChannel_OOC) { @@ -2337,14 +2339,13 @@ void EntityList::Message(uint32 to_guilddbid, uint32 type, const char *message, } } -void EntityList::QueueClientsGuild(Mob *sender, const EQApplicationPacket *app, - bool ignore_sender, uint32 guild_id) +void EntityList::QueueClientsGuild(const EQApplicationPacket *app, uint32 guild_id) { auto it = client_list.begin(); while (it != client_list.end()) { Client *client = it->second; if (client->IsInGuild(guild_id)) - client->QueuePacket(app); + client->QueuePacket(app, true); ++it; } } diff --git a/zone/entity.h b/zone/entity.h index bd3b07f6a..e81c173f0 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -240,11 +240,24 @@ public: bool IsMobInZone(Mob *who); void ClearClientPetitionQueue(); bool CanAddHateForMob(Mob *p); - void SendGuildMOTD(uint32 guild_id); - void SendGuildSpawnAppearance(uint32 guild_id); - void SendGuildMembers(uint32 guild_id); + void SendGuildMOTD(uint32 guild_id); + void SendGuildChannel(uint32 guild_id); + void SendGuildURL(uint32 guild_id); + void SendGuildSpawnAppearance(uint32 guild_id); + void SendGuildMembers(uint32 guild_id); + void SendGuildMembersList(uint32 guild_id); + void SendGuildMemberAdd(uint32 guild_id, uint32 level, uint32 _class, uint32 rank, uint32 spirit, uint32 zone_id, std::string player_name); + void SendGuildMemberRename(uint32 guild_id, std::string player_name, std::string new_player_name); + void SendGuildMemberRemove(uint32 guild_id, std::string player_name); + void SendGuildMemberLevel(uint32 guild_id, uint32 level, std::string player_name); + void SendGuildMemberRankAltBanker(uint32 guild_id, uint32 rank, std::string player_name, bool alt, bool banker); + void SendGuildMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note); + void SendGuildMemberDetails(uint32 guild_id, uint32 zone_id, uint32 offline_mode, std::string player_name); + void SendGuildRenameGuild(uint32 guild_id, std::string new_guild_name); + void RefreshAllGuildInfo(uint32 guild_id); void SendGuildList(); + void GuildSetPreRoFBankerFlag(uint32 guild_id, uint32 guild_rank, bool banker_status); void CheckGroupList (const char *fname, const int fline); void GroupProcess(); void RaidProcess(); @@ -402,6 +415,7 @@ public: void QuestJournalledSayClose(Mob *sender, float dist, const char* mobname, const char* message, Journal::Options &opts); void GroupMessage(uint32 gid, const char *from, const char *message); void ExpeditionWarning(uint32 minutes_left); + void UpdateGuildTributes(uint32 guild_id); void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false); void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0); @@ -411,7 +425,7 @@ public: void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = AccountStatus::Player, uint8 maxstatus = AccountStatus::Player); - void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); + void QueueClientsGuild(const EQApplicationPacket* app, uint32 guildeqid = 0); void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID); void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true, bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF, bool inspect_buffs = false, bool clear_target_window = false); diff --git a/zone/exp.cpp b/zone/exp.cpp index 65d65e217..a112bd6e7 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -969,6 +969,11 @@ void Client::SetLevel(uint8 set_level, bool command) SetPVP(true); } + if (IsInAGuild()) { + guild_mgr.SendToWorldMemberLevelUpdate(GuildID(), GetLevel(), std::string(GetCleanName())); + DoGuildTributeUpdate(); + } + DoTributeUpdate(); SendHPUpdate(); SetMana(CalcMaxMana()); diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp index 1860ef2ba..6e1f66d1c 100755 --- a/zone/gm_commands/guild.cpp +++ b/zone/gm_commands/guild.cpp @@ -6,7 +6,7 @@ extern WorldServer worldserver; #include "../guild_mgr.h" #include "../doors.h" -void command_guild(Client *c, const Seperator *sep) +void command_guild(Client* c, const Seperator* sep) { const auto arguments = sep->argnum; if (!arguments) { @@ -30,6 +30,8 @@ void command_guild(Client *c, const Seperator *sep) bool is_set_leader = !strcasecmp(sep->arg[1], "setleader"); bool is_set_rank = !strcasecmp(sep->arg[1], "setrank"); bool is_status = !strcasecmp(sep->arg[1], "status"); + bool is_details = !strcasecmp(sep->arg[1], "details"); + bool is_test = !strcasecmp(sep->arg[1], "test"); if ( !is_create && !is_delete && @@ -41,8 +43,10 @@ void command_guild(Client *c, const Seperator *sep) !is_set && !is_set_leader && !is_set_rank && - !is_status - ) { + !is_status && + !is_details && + !is_test + ) { SendGuildSubCommands(c); return; } @@ -50,12 +54,13 @@ void command_guild(Client *c, const Seperator *sep) if (is_create) { if (arguments < 3) { c->Message(Chat::White, "Usage: #guild create [Character ID|Character Name] [Guild Name]"); - } else { + } + else { auto leader_id = ( sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : database.GetCharacterID(sep->arg[2]) - ); + ); auto leader_name = database.GetCharNameByID(leader_id); if (!leader_id || leader_name.empty()) { c->Message( @@ -80,7 +85,8 @@ void command_guild(Client *c, const Seperator *sep) guild_id ).c_str() ); - } else { + } + else { auto guild_name = sep->argplus[3]; auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id); @@ -94,7 +100,8 @@ void command_guild(Client *c, const Seperator *sep) if (guild_id == GUILD_NONE) { c->Message(Chat::White, "Guild creation failed."); - } else { + } + else { c->Message( Chat::White, fmt::format( @@ -123,10 +130,12 @@ void command_guild(Client *c, const Seperator *sep) } } } - } else if (is_delete) { + } + else if (is_delete) { if (!sep->IsNumber(2)) { c->Message(Chat::White, "Usage: #guild delete [Guild ID]"); - } else { + } + else { auto guild_id = Strings::ToUnsignedInt(sep->arg[2]); if (!guild_mgr.GuildExists(guild_id)) { c->Message( @@ -156,12 +165,15 @@ void command_guild(Client *c, const Seperator *sep) ).c_str() ); } - } else if (is_help) { + } + else if (is_help) { SendGuildSubCommands(c); - } else if (is_info) { + } + else if (is_info) { if (arguments != 2 && c->IsInAGuild()) { c->Message(Chat::White, "#guild info [Guild ID]"); - } else { + } + else { auto guild_id = GUILD_NONE; if (sep->IsNumber(2)) { guild_id = Strings::ToUnsignedInt(sep->arg[2]); @@ -171,12 +183,15 @@ void command_guild(Client *c, const Seperator *sep) guild_mgr.DescribeGuild(c, guild_id); } } - } else if (is_list) { + } + else if (is_list) { guild_mgr.ListGuilds(c, std::string()); - } else if (is_rename) { + } + else if (is_rename) { if (!sep->IsNumber(2)) { c->Message(Chat::White, "Usage: #guild rename [Guild ID] [New Guild Name]"); - } else { + } + else { auto guild_id = Strings::ToUnsignedInt(sep->arg[2]); if (!guild_mgr.GuildExists(guild_id)) { c->Message( @@ -208,28 +223,33 @@ void command_guild(Client *c, const Seperator *sep) ).c_str() ); } - } else if (is_search) { + } + else if (is_search) { if (Strings::IsNumber(sep->arg[2])) { const auto guild_id = Strings::ToUnsignedInt(sep->arg[2]); guild_mgr.ListGuilds(c, guild_id); - } else { + } + else { const std::string search_criteria = sep->argplus[2]; guild_mgr.ListGuilds(c, search_criteria); } - } else if (is_set) { + } + else if (is_set) { if ( arguments != 3 || !sep->IsNumber(3) - ) { + ) { c->Message(Chat::White, "#guild set [Character ID|Character Name] [Guild ID] (Guild ID 0 is Guildless)"); return; - } else { + } + else { auto guild_id = Strings::ToUnsignedInt(sep->arg[3]); if (!guild_id) { guild_id = GUILD_NONE; - } else if (!guild_mgr.GuildExists(guild_id)) { + } + else if (!guild_mgr.GuildExists(guild_id)) { c->Message( Chat::White, fmt::format( @@ -244,7 +264,7 @@ void command_guild(Client *c, const Seperator *sep) sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : database.GetCharacterID(sep->arg[2]) - ); + ); auto character_name = database.GetCharNameByID(character_id); if (!character_id || character_name.empty()) { c->Message( @@ -264,7 +284,8 @@ void command_guild(Client *c, const Seperator *sep) character_name, character_id ); - } else { + } + else { LogGuilds( "[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", c->GetName(), @@ -287,7 +308,8 @@ void command_guild(Client *c, const Seperator *sep) guild_id ).c_str() ); - } else { + } + else { guild_mgr.SetGuild(character_id, GUILD_NONE, 0); c->Message( Chat::White, @@ -299,18 +321,20 @@ void command_guild(Client *c, const Seperator *sep) ); } } - } else if (is_set_leader) { + } + else if (is_set_leader) { if ( arguments != 3 || !sep->IsNumber(2) - ) { + ) { c->Message(Chat::White, "Usage: #guild setleader [Guild ID] [Character ID|Character Name]"); - } else { + } + else { auto leader_id = ( sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : database.GetCharacterID(sep->arg[2]) - ); + ); auto leader_name = database.GetCharNameByID(leader_id); if (!leader_id || leader_name.empty()) { c->Message( @@ -368,11 +392,13 @@ void command_guild(Client *c, const Seperator *sep) ); } } - } else if (is_set_rank) { + } + else if (is_set_rank) { auto rank = static_cast(Strings::ToUnsignedInt(sep->arg[3])); if (!sep->IsNumber(3)) { c->Message(Chat::White, "#guild setrank [Character ID|Character Name] [Rank]"); - } else if (rank < 0 || rank > GUILD_MAX_RANK) { + } + else if (rank < 0 || rank > GUILD_MAX_RANK) { c->Message( Chat::White, fmt::format( @@ -380,12 +406,13 @@ void command_guild(Client *c, const Seperator *sep) GUILD_MAX_RANK ).c_str() ); - } else { + } + else { auto character_id = ( sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : database.GetCharacterID(sep->arg[2]) - ); + ); auto character_name = database.GetCharNameByID(character_id); if (!character_id || character_name.empty()) { c->Message( @@ -430,7 +457,8 @@ void command_guild(Client *c, const Seperator *sep) ).c_str() ); } - } else if (is_status) { + } + else if (is_status) { auto client = ( t ? t : @@ -438,11 +466,12 @@ void command_guild(Client *c, const Seperator *sep) arguments == 2 ? entity_list.GetClientByName(sep->arg[2]) : c - ) - ); + ) + ); if (!client) { c->Message(Chat::White, "You must target someone or specify a character name."); - } else { + } + else { if (!client->IsInAGuild()) { c->Message( Chat::White, @@ -452,7 +481,8 @@ void command_guild(Client *c, const Seperator *sep) c == t ? "are" : "is" ).c_str() ); - } else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { + } + else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { c->Message( Chat::White, fmt::format( @@ -462,7 +492,8 @@ void command_guild(Client *c, const Seperator *sep) guild_mgr.GetGuildNameByID(client->GuildID()) ).c_str() ); - } else { + } + else { c->Message( Chat::White, fmt::format( @@ -476,12 +507,109 @@ void command_guild(Client *c, const Seperator *sep) } } } + else if (is_details) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild details [Guild ID]"); + } + else { + auto guild_id = Strings::ToUnsignedInt(sep->arg[2]); + if (!guild_mgr.GuildExists(guild_id)) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); + return; + } + + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + if (!guild) { + c->Message( + Chat::Yellow, + fmt::format( + "Guild {} not found. #guild list can be used to get guild ids.", + guild_id + ).c_str()); + return; + } + + if (guild) { + c->Message(Chat::Yellow, fmt::format("Guild ID: {}.", sep->arg[2]).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild Name: {}.", guild->name.c_str()).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild Leader ID: {}.", guild->leader).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild MinStatus: {}.", guild->minstatus).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild MOTD: {}.", guild->motd.c_str()).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild MOTD Setter:{}.", guild->motd_setter.c_str()).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild Channel: {}.", guild->channel.c_str()).c_str()); + c->Message(Chat::Yellow, fmt::format("Guild URL: {}.", guild->url.c_str()).c_str()); + + for (int i = 1; i <= GUILD_MAX_RANK; i++) { + c->Message( + Chat::Yellow, + fmt::format("Guild Rank: {} - {}.", i, guild->rank_names[i].c_str()).c_str()); + } + + c->Message(Chat::Yellow, "Guild Functions: {db_id} - {guild_id} - {perm_id} - {perm_value}."); + for (int i = 1; i <= GUILD_MAX_FUNCTIONS; i++) { + c->Message( + Chat::Yellow, fmt::format( + "Guild Function: {} - {} - {} - {}.", + guild->functions[i].id, + guild->functions[i].guild_id, + guild->functions[i].perm_id, + guild->functions[i].perm_value + ).c_str()); + } + c->Message(Chat::Yellow, fmt::format("Guild Tribute: Favor {}", guild->tribute.favor).c_str()); + c->Message( + Chat::Yellow, fmt::format( + "Guild Tribute: Tribute 1 {}/{} - Tribute 2 {}/{}", + guild->tribute.id_1, + guild->tribute.id_1_tier, + guild->tribute.id_2, + guild->tribute.id_2_tier + ).c_str()); + c->Message( + Chat::Yellow, fmt::format( + "Guild Tribute: Time Remaining {} - Enabled {}", + guild->tribute.time_remaining, + guild->tribute.enabled + ).c_str()); + } + for (auto &c1: entity_list.GetClientList()) { + if (c1.second->GuildID() == guild_id) { + c->Message( + Chat::Yellow, fmt::format( + "PlayerName: {} ID: {} Rank: {} OptIn: {} DirtyList: {}.", + c1.second->GetCleanName(), + c1.second->GuildID(), + c1.second->GuildRank(), + c1.second->GuildTributeOptIn(), + c1.second->GetGuildListDirty() + ).c_str()); + } + } + } + } + else if (is_test) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild test [Guild ID]"); + } + else { + auto guild_id = Strings::ToUnsignedInt(sep->arg[2]); + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + c->SendGuildMembersList(); + } + } } -void SendGuildSubCommands(Client *c) +void SendGuildSubCommands(Client* c) { c->Message(Chat::White, "#guild create [Character ID|Character Name] [Guild Name]"); c->Message(Chat::White, "#guild delete [Guild ID]"); + c->Message(Chat::White, "#guild details [Guild ID]"); c->Message(Chat::White, "#guild help"); c->Message(Chat::White, "#guild info [Guild ID]"); c->Message(Chat::White, "#guild list"); diff --git a/zone/guild.cpp b/zone/guild.cpp index 1d849704f..72fd2bb1d 100644 --- a/zone/guild.cpp +++ b/zone/guild.cpp @@ -69,13 +69,13 @@ void Client::SendGuildURL() if(IsInAGuild()) { auto outapp = - new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateURLAndChannel_Struct)); + new EQApplicationPacket(OP_GuildUpdate, sizeof(GuildUpdateURLAndChannel_Struct)); GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*) outapp->pBuffer; if(guild_mgr.GetGuildURL(GuildID(), guuacs->Text)) { - guuacs->Action = 0; + guuacs->Action = GuildUpdateURL; FastQueuePacket(&outapp); } else @@ -91,13 +91,13 @@ void Client::SendGuildChannel() if(IsInAGuild()) { auto outapp = - new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateURLAndChannel_Struct)); + new EQApplicationPacket(OP_GuildUpdate, sizeof(GuildUpdateURLAndChannel_Struct)); GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*) outapp->pBuffer; if(guild_mgr.GetGuildChannel(GuildID(), guuacs->Text)) { - guuacs->Action = 1; + guuacs->Action = GuildUpdateChannel; FastQueuePacket(&outapp); } @@ -111,8 +111,8 @@ void Client::SendGuildRanks() if (ClientVersion() < EQ::versions::ClientVersion::RoF) return; - int permissions = 30 + 1; //Static number of permissions in all EQ clients as of May 2014 - int ranks = 8 + 1; // Static number of RoF+ ranks as of May 2014 + int permissions = GUILD_MAX_FUNCTIONS + 1; //Static number of permissions in all EQ clients as of May 2014 + int ranks = GUILD_RECRUIT + 1; // Static number of RoF+ ranks as of May 2014 int j = 1; int i = 1; if(IsInAGuild()) @@ -121,21 +121,20 @@ void Client::SendGuildRanks() { while(i < permissions) { - auto outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, - sizeof(GuildUpdateRanks_Struct)); - GuildUpdateRanks_Struct *guuacs = (GuildUpdateRanks_Struct*) outapp->pBuffer; - //guuacs->Unknown0008 = GuildID(); - strncpy(guuacs->Unknown0012, GetCleanName(), 64); - guuacs->Action = 5; - guuacs->RankID = j; - guuacs->GuildID = GuildID(); - guuacs->PermissionID = i; - guuacs->PermissionVal = 1; - guuacs->Unknown0089[0] = 0x2c; - guuacs->Unknown0089[1] = 0x01; - guuacs->Unknown0089[2] = 0x00; - FastQueuePacket(&outapp); - i++; + auto outapp = new EQApplicationPacket(OP_GuildUpdate, + sizeof(GuildUpdateRanks_Struct)); + GuildUpdateRanks_Struct* guuacs = (GuildUpdateRanks_Struct*)outapp->pBuffer; + strncpy(guuacs->Unknown0012, GetCleanName(), 64); + guuacs->Action = GuildUpdatePermissions; + guuacs->RankID = j; + guuacs->GuildID = GuildID(); + guuacs->PermissionID = i; + guuacs->PermissionVal = guild_mgr.CheckPermission(GuildID(), j, (GuildAction)i); + guuacs->Unknown0089[0] = 0x2c; + guuacs->Unknown0089[1] = 0x01; + guuacs->Unknown0089[2] = 0x00; + FastQueuePacket(&outapp); + i++; } j++; i = 1; @@ -143,25 +142,42 @@ void Client::SendGuildRanks() } } +void Client::SendGuildRankNames() +{ + if (IsInAGuild() && (ClientVersion() >= EQ::versions::ClientVersion::RoF)) { + auto guild = guild_mgr.GetGuildByGuildID(GuildID()); + + for (int i = 1; i <= GUILD_MAX_RANK; i++) { + + auto outapp = new EQApplicationPacket(OP_GuildUpdate, sizeof(GuildUpdateUCPStruct)); + GuildUpdateUCPStruct *gucp = (GuildUpdateUCPStruct *) outapp->pBuffer; + + 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; + + QueuePacket(outapp); + safe_delete(outapp); + } + } +} + void Client::SendGuildSpawnAppearance() { if (!IsInAGuild()) { // clear guildtag SendAppearancePacket(AppearanceType::GuildID, GUILD_NONE); LogGuilds("Sending spawn appearance for no guild tag"); } else { - uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); + uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); + uint32 display = guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_ACTION_DISPLAY_GUILD_NAME) ? 1 : 0; LogGuilds("Sending spawn appearance for guild [{}] at rank [{}]", GuildID(), rank); - SendAppearancePacket(AppearanceType::GuildID, GuildID()); - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - switch (rank) { - case 0: { rank = 5; break; } // GUILD_MEMBER 0 - case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AppearanceType::GuildRank, rank); + SendAppearancePacket(AppearanceType::GuildID, GuildID(), true); + SendAppearancePacket(AppearanceType::GuildRank, rank, true); + SendAppearancePacket(AppearanceType::GuildShow, display, true); } UpdateWho(); } @@ -234,7 +250,10 @@ void Client::RefreshGuildInfo() guildrank = info.rank; guild_id = info.guild_id; - GuildBanker = info.banker || guild_mgr.IsGuildLeader(GuildID(), CharacterID()); + GuildBanker = info.banker || + guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || + guild_mgr.GetBankerFlag(CharacterID()) || + ClientVersion() >= EQ::versions::ClientVersion::RoF ? true : false; if(zone->GetZoneID() == Zones::GUILDHALL) { @@ -244,9 +263,9 @@ void Client::RefreshGuildInfo() GuildSetRank_Struct *gsrs = (GuildSetRank_Struct*)outapp->pBuffer; - gsrs->Rank = guildrank; - strn0cpy(gsrs->MemberName, GetName(), sizeof(gsrs->MemberName)); - gsrs->Banker = GuildBanker; + gsrs->rank = guildrank; + strn0cpy(gsrs->member_name, GetName(), sizeof(gsrs->member_name)); + gsrs->banker = GuildBanker; FastQueuePacket(&outapp); } @@ -257,7 +276,7 @@ void Client::RefreshGuildInfo() if (ClientVersion() < EQ::versions::ClientVersion::RoF) ClearGuildBank(); - if(guild_id != GUILD_NONE) + if (guild_id != GUILD_NONE) GuildBanks->SendGuildBank(this); } } @@ -265,24 +284,50 @@ void Client::RefreshGuildInfo() SendGuildSpawnAppearance(); } -void EntityList::SendGuildMOTD(uint32 guild_id) { - if(guild_id == GUILD_NONE) +void EntityList::SendGuildMOTD(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { return; - auto it = client_list.begin(); - while (it != client_list.end()) { - Client *client = it->second; - if (client->GuildID() == guild_id) { - client->SendGuildMOTD(); - client->SendGuildURL(); - client->SendGuildChannel(); + } + + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + c.second->SendGuildMOTD(); } - ++it; } } -void EntityList::SendGuildSpawnAppearance(uint32 guild_id) { - if(guild_id == GUILD_NONE) +void EntityList::SendGuildChannel(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { return; + } + + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + c.second->SendGuildChannel(); + } + } +} + +void EntityList::SendGuildURL(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { + return; + } + + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + c.second->SendGuildURL(); + } + } +} + +void EntityList::SendGuildSpawnAppearance(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { + return; + } auto it = client_list.begin(); while (it != client_list.end()) { Client *client = it->second; @@ -293,9 +338,11 @@ void EntityList::SendGuildSpawnAppearance(uint32 guild_id) { } } -void EntityList::RefreshAllGuildInfo(uint32 guild_id) { - if(guild_id == GUILD_NONE) +void EntityList::RefreshAllGuildInfo(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { return; + } auto it = client_list.begin(); while (it != client_list.end()) { Client *client = it->second; @@ -306,9 +353,11 @@ void EntityList::RefreshAllGuildInfo(uint32 guild_id) { } } -void EntityList::SendGuildMembers(uint32 guild_id) { - if(guild_id == GUILD_NONE) +void EntityList::SendGuildMembers(uint32 guild_id) +{ + if (guild_id == GUILD_NONE) { return; + } //this could be optimized a bit to only build the member's packet once //and then keep swapping out the name in the packet on each send. @@ -317,36 +366,92 @@ void EntityList::SendGuildMembers(uint32 guild_id) { while (it != client_list.end()) { Client *client = it->second; if (client->GuildID() == guild_id) { - client->SendGuildMembers(); + //client->SendGuildMembers(); } ++it; } } -void EntityList::SendGuildList() { - auto it = client_list.begin(); - while (it != client_list.end()) { - Client *client = it->second; - client->SendGuildList(); - ++it; +void EntityList::SendGuildList() +{ + for (auto const& c : client_list) { + c.second->SendGuildList(); } } -void Client::SendGuildJoin(GuildJoin_Struct* gj){ +void Client::SendGuildJoin(GuildJoin_Struct *gj) +{ auto outapp = new EQApplicationPacket(OP_GuildManageAdd, sizeof(GuildJoin_Struct)); - GuildJoin_Struct* outgj=(GuildJoin_Struct*)outapp->pBuffer; - outgj->class_ = gj->class_; + auto outgj = (GuildJoin_Struct *) outapp->pBuffer; + outgj->class_ = gj->class_; outgj->guild_id = gj->guild_id; - outgj->level = gj->level; + outgj->level = gj->level; + outgj->rank = gj->rank; + outgj->zoneid = gj->zoneid; strcpy(outgj->name, gj->name); - outgj->rank = gj->rank; - outgj->zoneid = gj->zoneid; LogGuilds("Sending OP_GuildManageAdd for join of length [{}]", outapp->size); FastQueuePacket(&outapp); +} -// SendGuildMembers(gj->guild_id, true); +void EntityList::GuildSetPreRoFBankerFlag(uint32 guild_id, uint32 guild_rank, bool bank_status) +{ + auto guild_members = [&]() -> std::vector { + std::vector members = {}; + for (auto const &c: entity_list.GetClientList()) { + if (c.second->GuildID() == guild_id && c.second->GuildRank() == guild_rank) { + members.push_back(c.second); + } + } + return members; + }; + + for (auto const &c: guild_members()) { + auto outapp = new ServerPacket( + ServerOP_GuildRankUpdate, + sizeof(ServerGuildRankUpdate_Struct) + ); + + guild_mgr.UpdateDbBankerFlag(c->CharacterID(), bank_status); + c->SetGuildBanker(bank_status); + CharGuildInfo cgi; + guild_mgr.GetCharInfo(c->CharacterID(), cgi); + + auto *sgrus = (ServerGuildRankUpdate_Struct *) outapp->pBuffer; + sgrus->guild_id = guild_id; + sgrus->rank = guild_rank; + sgrus->banker = (bank_status ? 1 : 0) + (cgi.alt ? 2 : 0); + sgrus->no_update = true; + strn0cpy(sgrus->member_name, c->GetCleanName(), sizeof(sgrus->member_name)); + + worldserver.SendPacket(outapp); + safe_delete(outapp); + } +} + +void Client::SetGuildRank(uint32 rank) +{ + guildrank = rank; +} + +void Client::SetGuildTributeOptIn(bool state) +{ + guild_tribute_opt_in = state; +} + +void Client::SetGuildID(uint32 g_id) +{ + guild_id = g_id; +} + +void EntityList::UpdateGuildTributes(uint32 guild_id) +{ + for (auto const &c: entity_list.GetClientList()) { + if (c.second->GuildID() == guild_id) { + c.second->DoGuildTributeUpdate(); + } + } } /* @@ -409,3 +514,353 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank, SendGuildMembers(guild_id, true); }*/ +void Client::SendGuildMembersList() +{ + auto guild_name = guild_mgr.GetGuildName(GuildID()); + uint32 len; + uint8 *data = guild_mgr.MakeGuildMembers(GuildID(), guild_name, len); + if (data == nullptr) { + return; + } + + auto outapp = new EQApplicationPacket(OP_GuildMemberList); + outapp->size = len; + outapp->pBuffer = data; + data = nullptr; + + LogGuilds("Sending OP_GuildMemberList of length [{}]", outapp->size); + + FastQueuePacket(&outapp); + + SendGuildMOTD(); + SendGuildChannel(); + SendGuildURL(); + + SetGuildListDirty(false); +} + +void Client::SendGuildMemberAdd( + uint32 guild_id, + uint32 level, + uint32 class_, + uint32 rank_, + uint32 guild_show, + uint32 zone_id, + std::string player_name +) +{ + if (GetGuildListDirty()) { + SendGuildMembersList(); + return; + } + + auto outapp = new EQApplicationPacket(OP_GuildMemberAdd, sizeof(GuildMemberAdd_Struct)); + auto out = (GuildMemberAdd_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->last_on = time(nullptr); + out->level = level; + out->zone_id = zone_id; + out->rank_ = rank_; + out->guild_show = guild_show; + out->class_ = class_; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendGuildMemberRename(uint32 guild_id, std::string player_name, std::string new_player_name) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberRename, sizeof(GuildRenameMember_Struct)); + auto *out = (GuildRenameMember_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + strn0cpy(out->new_player_name, new_player_name.c_str(), sizeof(out->new_player_name)); + + QueuePacket(outapp); + safe_delete(outapp); + +} + +void Client::SendGuildMemberDelete(uint32 guild_id, std::string player_name) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberDelete, sizeof(GuildMemberDelete_Struct)); + auto out = (GuildMemberDelete_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); + + SetGuildListDirty(true); + +} + +void Client::SendGuildMemberLevel(uint32 guild_id, uint32 level, std::string player_name) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberLevel, sizeof(GuildMemberLevel_Struct)); + auto out = (GuildMemberLevel_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->level = level; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendGuildMemberRankAltBanker(uint32 guild_id, uint32 rank, std::string player_name, bool alt, bool banker) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberRankAltBanker, sizeof(GuildMemberRank_Struct)); + auto out = (GuildMemberRank_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->rank_ = rank; + out->alt_banker = (alt << 1) | banker; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendGuildMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberPublicNote, sizeof(GuildMemberPublicNote_Struct)); + auto out = (GuildMemberPublicNote_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + strn0cpy(out->public_note, public_note.c_str(), sizeof(out->public_note)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendGuildMemberDetails(uint32 guild_id, uint32 zone_id, uint32 offline_mode, std::string player_name) +{ + auto outapp = new EQApplicationPacket(OP_GuildMemberDetails, sizeof(GuildMemberDetails_Struct)); + auto out = (GuildMemberDetails_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->zone_id = zone_id; + out->last_on = time(nullptr); + out->offline_mode = offline_mode; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendGuildRenameGuild(uint32 guild_id, std::string new_guild_name) +{ + auto outapp = new EQApplicationPacket(OP_GuildRenameGuild, sizeof(GuildRenameGuild_Struct)); + auto out = (GuildRenameGuild_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->new_guild_name, new_guild_name.c_str(), sizeof(out->new_guild_name)); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void EntityList::SendGuildMembersList(uint32 guild_id) +{ + for (auto const& c : client_list) { + if (c.second->GuildID() == guild_id) { + c.second->SendGuildMembersList(); + c.second->SetGuildListDirty(false); + } + } +} + +void EntityList::SendGuildMemberAdd( + uint32 guild_id, + uint32 level, + uint32 class_, + uint32 rank_, + uint32 spirit, + uint32 zone_id, + std::string player_name +) +{ + for (auto &c: client_list) { + if (c.second->GuildID() == guild_id) { + c.second->SendGuildMemberAdd(guild_id, level, class_, rank_, 0, zone_id, player_name); + } + + if (player_name.compare(c.second->GetCleanName()) == 0) { + c.second->SetGuildID(guild_id); + c.second->SetGuildRank(rank_); + c.second->SendGuildList(); + c.second->SetGuildTributeOptIn(false); + c.second->SendGuildMembersList(); + if (c.second->ClientVersion() >= EQ::versions::ClientVersion::RoF) { + c.second->SendGuildRanks(); + c.second->SendGuildRankNames(); + } + + if (zone->GetZoneID() == Zones::GUILDHALL && GuildBanks) { + GuildBanks->SendGuildBank(c.second); + } + + c.second->SendGuildActiveTributes(guild_id); + c.second->SendAppearancePacket(AppearanceType::GuildID, guild_id, true, false, c.second); + c.second->SendAppearancePacket(AppearanceType::GuildRank, rank_, true, false, c.second); + c.second->SendAppearancePacket(AppearanceType::GuildShow, guild_mgr.CheckPermission(guild_id, rank_, GUILD_ACTION_DISPLAY_GUILD_NAME) ? 1 : 0); + c.second->DoGuildTributeUpdate(); + } + } +} + +void EntityList::SendGuildMemberRename(uint32 guild_id, std::string player_name, std::string new_player_name) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberRename, sizeof(GuildRenameMember_Struct)); + auto out = (GuildRenameMember_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + strn0cpy(out->new_player_name, new_player_name.c_str(), sizeof(out->new_player_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void EntityList::SendGuildMemberRemove(uint32 guild_id, std::string player_name) +{ + for (auto &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberDelete, sizeof(GuildMemberDelete_Struct)); + auto out = (GuildMemberDelete_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + + c.second->SetGuildListDirty(true); + + if (player_name.compare(c.second->GetCleanName()) == 0) { + c.second->SetGuildID(GUILD_NONE); + c.second->SetGuildRank(GUILD_RANK_NONE); + c.second->SetGuildTributeOptIn(false); + c.second->SendGuildActiveTributes(0); + c.second->SetGuildListDirty(false); + c.second->SendGuildList(); + c.second->SendAppearancePacket(AppearanceType::GuildID, GUILD_NONE, true); + c.second->SendAppearancePacket(AppearanceType::GuildRank, GUILD_RANK_NONE, true); + } + } + } +} + +void EntityList::SendGuildMemberLevel(uint32 guild_id, uint32 level, std::string player_name) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberLevel, sizeof(GuildMemberLevel_Struct)); + GuildMemberLevel_Struct *out = (GuildMemberLevel_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->level = level; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void EntityList::SendGuildMemberRankAltBanker(uint32 guild_id, uint32 rank_, std::string player_name, bool alt, bool banker) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberRankAltBanker, sizeof(GuildMemberRank_Struct)); + auto out = (GuildMemberRank_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->rank_ = rank_; + out->alt_banker = (alt << 1) | banker; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + + if (player_name.compare(c.second->GetName()) == 0) { + c.second->SetGuildRank(rank_); + c.second->SendAppearancePacket(AppearanceType::GuildRank, rank_, false); + c.second->SendAppearancePacket(AppearanceType::GuildShow, + guild_mgr.CheckPermission(c.second->GuildID(), c.second->GuildRank(), GUILD_ACTION_DISPLAY_GUILD_NAME) ? 1 : 0, + true); + } + } +} + +void EntityList::SendGuildMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberPublicNote, sizeof(GuildMemberPublicNote_Struct)); + auto out = (GuildMemberPublicNote_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + strn0cpy(out->public_note, public_note.c_str(), sizeof(out->public_note)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void EntityList::SendGuildMemberDetails(uint32 guild_id, uint32 zone_id, uint32 offline_mode, std::string player_name) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildMemberDetails, sizeof(GuildMemberDetails_Struct)); + auto out = (GuildMemberDetails_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + out->zone_id = zone_id; + out->last_on = time(nullptr); + out->offline_mode = offline_mode; + strn0cpy(out->player_name, player_name.c_str(), sizeof(out->player_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void EntityList::SendGuildRenameGuild(uint32 guild_id, std::string new_guild_name) +{ + for (auto const &c: client_list) { + if (c.second->GuildID() == guild_id) { + auto outapp = new EQApplicationPacket(OP_GuildRenameGuild, sizeof(GuildRenameGuild_Struct)); + auto out = (GuildRenameGuild_Struct *) outapp->pBuffer; + + out->guild_id = guild_id; + strn0cpy(out->new_guild_name, new_guild_name.c_str(), sizeof(out->new_guild_name)); + + c.second->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void Client::SendGuildDeletePacket(uint32 guild_id) +{ + auto outapp = new EQApplicationPacket(OP_GuildDeleteGuild, sizeof(GuildDelete_Struct)); + auto data = (GuildDelete_Struct*)outapp->pBuffer; + + data->guild_id = guild_id; + FastQueuePacket(&outapp); +} diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index 6058c873c..549006ee0 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -18,17 +18,21 @@ #include "../common/servertalk.h" #include "../common/strings.h" - +#include "string_ids.h" #include "client.h" #include "guild_mgr.h" #include "worldserver.h" #include "zonedb.h" +#include "../common/emu_versions.h" +#include "../common/repositories/guild_ranks_repository.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); @@ -69,124 +73,32 @@ void ZoneGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uin void ZoneGuildManager::SendRankUpdate(uint32 CharID) { CharGuildInfo gci; - - if(!GetCharInfo(CharID, gci)) + if(!GetCharInfo(CharID, gci)) { return; + } - auto pack = new ServerPacket(ServerOP_GuildRankUpdate, sizeof(ServerGuildRankUpdate_Struct)); + auto pack = new ServerPacket(ServerOP_GuildRankUpdate, sizeof(ServerGuildRankUpdate_Struct)); + auto sgrus = (ServerGuildRankUpdate_Struct *) pack->pBuffer; - ServerGuildRankUpdate_Struct *sgrus = (ServerGuildRankUpdate_Struct*)pack->pBuffer; - - sgrus->GuildID = gci.guild_id; - strn0cpy(sgrus->MemberName, gci.char_name.c_str(), sizeof(sgrus->MemberName)); - sgrus->Rank = gci.rank; - sgrus->Banker = gci.banker + (gci.alt * 2); + 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)); - ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + auto s = (ServerGuildID_Struct *) pack->pBuffer; s->guild_id = guild_id; worldserver.SendPacket(pack); safe_delete(pack); } -//makes a guild member list packet (internal format), returns ownership of the buffer. -uint8 *ZoneGuildManager::MakeGuildMembers(uint32 guild_id, const char *prefix_name, uint32 &length) { - uint8 *retbuffer; - - //hack because we dont have the "remove from guild" packet right now. - if(guild_id == GUILD_NONE) { - length = sizeof(Internal_GuildMembers_Struct); - retbuffer = new uint8[length]; - Internal_GuildMembers_Struct *gms = (Internal_GuildMembers_Struct *) retbuffer; - strcpy(gms->player_name, prefix_name); - gms->count = 0; - gms->name_length = 0; - gms->note_length = 0; - return(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]; - - Internal_GuildMembers_Struct *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_); - PutField(rank); - PutField(time_last_on); - PutField(tribute_enable); - PutField(total_tribute); - PutField(last_tribute); - SlideStructString( note_buf, ci->public_note ); - e->zoneinstance = 0; - e->zone_id = 0; // Flag them as offline (zoneid 0) as world will update us with their online status afterwards. -#undef SlideStructString -#undef PutFieldN - - delete *cur; - - e++; - } - - return(retbuffer); -} - void ZoneGuildManager::ListGuilds(Client *c, uint32 guild_id) const { if (m_guilds.size()) { const auto& g = m_guilds.find(guild_id); @@ -201,7 +113,7 @@ void ZoneGuildManager::ListGuilds(Client *c, uint32 guild_id) const { return; } - const auto leader_name = database.GetCharNameByID(g->second->leader_char_id); + const auto leader_name = database.GetCharNameByID(g->second->leader); c->Message( Chat::White, fmt::format( @@ -212,7 +124,7 @@ void ZoneGuildManager::ListGuilds(Client *c, uint32 guild_id) const { fmt::format( "Leader: {} ({}) ", leader_name, - g->second->leader_char_id + g->second->leader ) : "" ), @@ -250,7 +162,7 @@ void ZoneGuildManager::ListGuilds(Client *c, std::string search_criteria) const continue; } - const auto leader_name = database.GetCharNameByID(guild.second->leader_char_id); + const auto leader_name = database.GetCharNameByID(guild.second->leader); c->Message( Chat::White, fmt::format( @@ -261,7 +173,7 @@ void ZoneGuildManager::ListGuilds(Client *c, std::string search_criteria) const fmt::format( "Leader: {} ({}) ", leader_name, - guild.second->leader_char_id + guild.second->leader ) : "" ), @@ -294,8 +206,9 @@ void ZoneGuildManager::ListGuilds(Client *c, std::string search_criteria) const } -void ZoneGuildManager::DescribeGuild(Client *c, uint32 guild_id) const { - std::map::const_iterator res; +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( @@ -309,72 +222,46 @@ void ZoneGuildManager::DescribeGuild(Client *c, uint32 guild_id) const { } const GuildInfo *info = res->second; + auto membership = GuildMembersRepository::GetGuildMembershipStats(*m_db, guild_id); - auto leader_name = database.GetCharNameByID(info->leader_char_id); + auto leader_name = database.GetCharNameByID(info->leader); std::string popup_text = ""; popup_text += fmt::format( - "", - info->name, + "", + info->name, guild_id ); popup_text += fmt::format( - "", + "", leader_name, - info->leader_char_id + info->leader ); - popup_text += "

"; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - popup_text += ""; - for (uint8 guild_rank = 0; guild_rank <= GUILD_MAX_RANK; guild_rank++) { - auto can_hear_guild_chat = info->ranks[guild_rank].permissions[GUILD_HEAR] ? "Y" : "N"; - auto can_speak_guild_chat = info->ranks[guild_rank].permissions[GUILD_SPEAK] ? "Y" : "N"; - auto can_invite = info->ranks[guild_rank].permissions[GUILD_INVITE] ? "Y" : "N"; - auto can_remove = info->ranks[guild_rank].permissions[GUILD_REMOVE] ? "Y" : "N"; - auto can_promote = info->ranks[guild_rank].permissions[GUILD_PROMOTE] ? "Y" : "N"; - auto can_demote = info->ranks[guild_rank].permissions[GUILD_DEMOTE] ? "Y" : "N"; - auto can_set_motd = info->ranks[guild_rank].permissions[GUILD_MOTD] ? "Y" : "N"; - auto can_war_peace = info->ranks[guild_rank].permissions[GUILD_WARPEACE] ? "Y" : "N"; - popup_text += fmt::format( - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "", - !info->ranks[guild_rank].name.empty() ? info->ranks[guild_rank].name : "Nameless", - guild_rank, - can_demote, - can_hear_guild_chat, - can_invite, - can_promote, - can_remove, - can_set_motd, - can_speak_guild_chat, - can_war_peace - ); - } + 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 += "
Name{}Guild ID{}
Name{}Guild ID{}
Leader{}Character ID{}
Leader{}Character ID{}
RankDemoteHear Guild ChatInvitePromoteRemoveSet MOTDSpeak Guild ChatWar/Peace
{} ({}){}{}{}{}{}{}{}{}
.
RanksQuantity
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
{}{}
"; + popup_text += "."; + popup_text += fmt::format("Tribute On{}", membership.tribute_enabled); + popup_text += fmt::format("Total Tribute{}", info->tribute.favor); + + popup_text += ""; + + auto text = new char[4096]; + strn0cpy(text, popup_text.c_str(), 4096); c->SendPopupToClient( "Guild Information", - popup_text.c_str() + text ); + safe_delete(text); } //in theory, we could get a pile of unused entries in this array, but only if @@ -396,230 +283,366 @@ bool ZoneGuildManager::VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uin 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; - - LogGuilds("Received guild refresh from world for [{}], changes: name=[{}], motd=[{}], rank=[{}], relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); - - //reload all the guild details from the database. - 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; - - LogGuilds("Received guild member refresh from world for char [{}] from guild [{}]", s->char_id, s->guild_id); - - Client *c = entity_list.GetClientByCharID(s->char_id); - - if(c != nullptr) { - //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 != nullptr) - c->SendGuildMembers(); //only need to update this player's list (trying to clear it) - } 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 != nullptr && 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: - { - if(is_zone_loaded) - { - if(pack->size != sizeof(ServerGuildRankUpdate_Struct)) - { - LogError("Received ServerOP_RankUpdate of incorrect size [{}], expected [{}]", - pack->size, sizeof(ServerGuildRankUpdate_Struct)); - +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; } - ServerGuildRankUpdate_Struct *sgrus = (ServerGuildRankUpdate_Struct*)pack->pBuffer; + 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 + ); - auto outapp = new EQApplicationPacket(OP_SetGuildRank, sizeof(GuildSetRank_Struct)); + RefreshGuild(s->guild_id); - GuildSetRank_Struct *gsrs = (GuildSetRank_Struct*)outapp->pBuffer; - - gsrs->Rank = sgrus->Rank; - strn0cpy(gsrs->MemberName, sgrus->MemberName, sizeof(gsrs->MemberName)); - gsrs->Banker = sgrus->Banker; - - entity_list.QueueClientsGuild(nullptr, outapp, false, sgrus->GuildID); - - safe_delete(outapp); - } - - break; - } - - case ServerOP_DeleteGuild: { - if(pack->size != sizeof(ServerGuildID_Struct)) { - LogError("Received ServerOP_DeleteGuild of incorrect size [{}], expected [{}]", pack->size, sizeof(ServerGuildID_Struct)); - return; - } - ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; - - LogGuilds("Received guild delete from world for guild [{}]", s->guild_id); - - //clear all the guild tags. - entity_list.RefreshAllGuildInfo(s->guild_id); - - //remove the guild data from the local guild manager - guild_mgr.LocalDeleteGuild(s->guild_id); - - //if we stop forcing guild list to send on guild create, we need to do this: - //in the case that we delete a guild and add a new one. - //entity_list.SendGuildList(); - - break; - } - - case ServerOP_GuildMemberUpdate: - { - ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer; - - if(is_zone_loaded) - { - auto outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); - - GuildMemberUpdate_Struct *gmus = (GuildMemberUpdate_Struct*)outapp->pBuffer; - - gmus->GuildID = sgmus->GuildID; - strn0cpy(gmus->MemberName, sgmus->MemberName, sizeof(gmus->MemberName)); - gmus->ZoneID = sgmus->ZoneID; - gmus->InstanceID = 0; // I don't think we care what Instance they are in, for the Guild Management Window. - gmus->LastSeen = sgmus->LastSeen; - - entity_list.QueueClientsGuild(nullptr, outapp, false, sgmus->GuildID); - - 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; + if (s->motd_change) { + //resend guild MOTD to all guild members in this zone. + entity_list.SendGuildMOTD(s->guild_id); } - 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 = 0; - 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); + + if (s->name_change) { + //until we figure out the guild update packet, we resend the whole guild list. + entity_list.SendGuildList(); } - safe_delete(outapp); - } - break; + 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); + } - case ServerOP_LFGuildUpdate: - { - if(is_zone_loaded) - { - char GuildName[33]; - char Comments[257]; - uint32 FromLevel, ToLevel, Classes, AACount, TimeZone, TimePosted, Toggle; + if (s->relation_change) { + //unknown until we implement guild relations. + } - 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)); - - LFGuild_GuildToggle_Struct *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(nullptr, outapp, false, GuildID); - safe_delete(outapp); 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); + } + } + + 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 res = m_guilds.find(sgpus->guild_id); + if (sgpus->function_value) { + res->second->functions[sgpus->function_id].perm_value |= (1UL << (8 - sgpus->rank)); + } + else { + res->second->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; + } } } @@ -628,10 +651,10 @@ void ZoneGuildManager::SendGuildMemberUpdateToWorld(const char *MemberName, uint auto pack = new ServerPacket(ServerOP_GuildMemberUpdate, sizeof(ServerGuildMemberUpdate_Struct)); ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer; - sgmus->GuildID = GuildID; - strn0cpy(sgmus->MemberName, MemberName, sizeof(sgmus->MemberName)); - sgmus->ZoneID = ZoneID; - sgmus->LastSeen = LastSeen; + 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); @@ -1430,27 +1453,371 @@ bool GuildBankManager::AllowedToWithdraw(uint32 GuildID, uint16 Area, uint16 Slo // Is a none-Guild Banker allowed to withdraw the item at this slot ? // This is really here for anti-hacking measures, as the client should not request an item it does not have permission to withdraw. // - if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + if (SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) { return false; + } auto Iterator = GetGuildBank(GuildID); - if(Iterator == Banks.end()) + if (Iterator == Banks.end()) { return false; + } - if(Area != GuildBankMainArea) + if (Area != GuildBankMainArea) { return false; + } uint8 Permissions = (*Iterator)->Items.MainArea[SlotID].Permissions; - if(Permissions == GuildBankBankerOnly) + if (Permissions == GuildBankBankerOnly) { return false; + } - if(Permissions != GuildBankSingleMember) // Public or Public-If-Useable (should really check if item is useable) + if (Permissions != + GuildBankSingleMember) { // Public or Public-If-Useable (should really check if item is useable) return true; + } - if(!strncmp((*Iterator)->Items.MainArea[SlotID].WhoFor, Name, sizeof((*Iterator)->Items.MainArea[SlotID].WhoFor))) + if (!strncmp((*Iterator)->Items.MainArea[SlotID].WhoFor, + Name, + sizeof((*Iterator)->Items.MainArea[SlotID].WhoFor))) { return true; + } return false; } + +void ZoneGuildManager::UpdateRankPermission(uint32 gid, uint32 charid, uint32 fid, uint32 rank, uint32 value) +{ + auto res = m_guilds.find(gid); + if (value) { + res->second->functions[fid].perm_value |= (1UL << (8 - rank)); + } + else { + res->second->functions[fid].perm_value &= ~(1UL << (8 - rank)); + } + auto query = fmt::format("UPDATE guild_permissions SET permission = {} WHERE perm_id = {} AND guild_id = {};", res->second->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 = m_guilds.find(guild_id); + auto c = entity_list.GetClientByCharID(char_id); + if (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->second->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: { + 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) +} diff --git a/zone/guild_mgr.h b/zone/guild_mgr.h index ae21c55c3..3256d82d2 100644 --- a/zone/guild_mgr.h +++ b/zone/guild_mgr.h @@ -57,16 +57,32 @@ public: void ListGuilds(Client *c, std::string search_criteria = std::string()) const; void ListGuilds(Client *c, uint32 guild_id = 0) const; void DescribeGuild(Client *c, uint32 guild_id) const; + bool IsActionABankAction(GuildAction action); - -// bool DonateTribute(uint32 charid, uint32 guild_id, uint32 tribute_amount); - - uint8 *MakeGuildMembers(uint32 guild_id, const char *prefix_name, uint32 &length); //make a guild member list packet, returns ownership of the buffer. + uint8 *MakeGuildMembers(uint32 guild_id, const char* prefix_name, uint32& length); + void SendToWorldMemberLevelUpdate(uint32 guild_id, uint32 level, std::string player_name); + void SendToWorldMemberPublicNote(uint32 guild_id, std::string player_name, std::string public_note); + void SendToWorldMemberRemove(uint32 guild_id, std::string player_name); + void SendToWorldMemberAdd(uint32 guild_id, uint32 char_id, uint32 level, uint32 _class, uint32 rank, uint32 zone_id, std::string player_name); + void SendToWorldGuildChannel(uint32 guild_id, std::string channel); + void SendToWorldGuildURL(uint32 guild_id, std::string url); + void SendToWorldSendGuildList(); + void SendToWorldMemberRankUpdate(uint32 guild_id, uint32 rank, uint32 banker, uint32 alt, bool no_update, const char *player_name); + void SendToWorldSendGuildMembersList(uint32 guild_id); + bool RemoveMember(uint32 guild_id, uint32 char_id, std::string player_name); + void MemberAdd(uint32 guild_id, uint32 char_id, uint32 level, uint32 _class, uint32 rank, uint32 zone_id, std::string player_name); + bool MemberRankUpdate(uint32 guild_id, uint32 rank, uint32 banker, uint32 alt, bool no_update, const char *player_name); void RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank); bool VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank); void SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen); void RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID); + void UpdateRankPermission(uint32 gid, uint32 charid, uint32 fid, uint32 rank, uint32 value); + void SendPermissionUpdate(uint32 guild_id, uint32 rank, uint32 function_id, uint32 value); + void UpdateRankName(uint32 gid, uint32 rank, std::string rank_name); + void SendRankName(uint32 guild_id, uint32 rank, std::string rank_name); + void SendAllRankNames(uint32 guild_id, uint32 char_id); + BaseGuildManager::GuildInfo* GetGuildByGuildID(uint32 guild_id); protected: virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 5b84eb2a8..c1f74f140 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1105,7 +1105,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up if(update_db) database.SaveInventory(character_id, inst, slot_id); } - + if(client_update && IsValidSlot(slot_id)) { EQApplicationPacket* outapp = nullptr; if(inst) { @@ -1674,6 +1674,7 @@ bool Client::IsValidSlot(uint32 slot) { (slot == (uint32)EQ::invslot::slotCursor) || (slot <= EQ::invbag::CURSOR_BAG_END && slot >= EQ::invbag::CURSOR_BAG_BEGIN) || (slot <= EQ::invslot::TRIBUTE_END && slot >= EQ::invslot::TRIBUTE_BEGIN) || + (slot <= EQ::invslot::GUILD_TRIBUTE_END && slot >= EQ::invslot::GUILD_TRIBUTE_BEGIN) || (slot <= EQ::invslot::SHARED_BANK_END && slot >= EQ::invslot::SHARED_BANK_BEGIN) || (slot <= EQ::invbag::SHARED_BANK_BAGS_END && slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) || (slot <= EQ::invslot::TRADE_END && slot >= EQ::invslot::TRADE_BEGIN) || @@ -3578,6 +3579,11 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool if (inst == nullptr) { continue; } instmap[index] = inst; } + for (int16 index = EQ::invslot::GUILD_TRIBUTE_BEGIN; index <= EQ::invslot::GUILD_TRIBUTE_END; ++index) { + auto inst = m_inv[index]; + if (inst == nullptr) { continue; } + instmap[index] = inst; + } for (int16 index = EQ::invslot::BANK_BEGIN; index <= EQ::invslot::BANK_END; ++index) { auto inst = m_inv[index]; if (inst == nullptr) { continue; } @@ -3714,6 +3720,7 @@ bool Client::InterrogateInventory_error(int16 head, int16 index, const EQ::ItemI if ( (head >= EQ::invslot::EQUIPMENT_BEGIN && head <= EQ::invslot::EQUIPMENT_END) || (head >= EQ::invslot::TRIBUTE_BEGIN && head <= EQ::invslot::TRIBUTE_END) || + (head >= EQ::invslot::GUILD_TRIBUTE_BEGIN && head <= EQ::invslot::GUILD_TRIBUTE_END) || (head >= EQ::invslot::WORLD_BEGIN && head <= EQ::invslot::WORLD_END) || (head >= 8000 && head <= 8101)) { switch (depth) diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 46e3d0190..c6a1ab6e2 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -83,6 +83,11 @@ int Lua_Packet::GetRawOpcode() { return static_cast(self->GetOpcodeBypass()); } +int Lua_Packet::GetProtocolOpcode() { + Lua_Safe_Call_Int(); + return static_cast(self->GetProtocolOpcode()); +} + void Lua_Packet::SetRawOpcode(int op) { Lua_Safe_Call_Void(); self->SetOpcode(static_cast(op)); @@ -329,6 +334,7 @@ luabind::scope lua_register_packet() { .property("valid", &Lua_Packet::Valid) .def("GetOpcode", &Lua_Packet::GetOpcode) .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) + .def("GetProtocolOpcode", &Lua_Packet::GetProtocolOpcode) .def("GetSize", &Lua_Packet::GetSize) .def("GetWritePosition", &Lua_Packet::GetWritePosition) .def("ReadDouble", &Lua_Packet::ReadDouble) @@ -837,7 +843,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("HideCorpse", static_cast(OP_HideCorpse)), luabind::value("TargetBuffs", static_cast(OP_TargetBuffs)), luabind::value("TradeBusy", static_cast(OP_TradeBusy)), - luabind::value("GuildUpdateURLAndChannel", static_cast(OP_GuildUpdateURLAndChannel)), + luabind::value("GuildUpdate", static_cast(OP_GuildUpdate)), luabind::value("CameraEffect", static_cast(OP_CameraEffect)), luabind::value("SpellEffect", static_cast(OP_SpellEffect)), luabind::value("DzQuit", static_cast(OP_DzQuit)), diff --git a/zone/lua_packet.h b/zone/lua_packet.h index 9558b00f7..ede02c81f 100644 --- a/zone/lua_packet.h +++ b/zone/lua_packet.h @@ -30,6 +30,7 @@ public: int GetOpcode(); void SetOpcode(int op); int GetRawOpcode(); + int GetProtocolOpcode(); void SetRawOpcode(int op); int GetWritePosition(); void SetWritePosition(int offset); diff --git a/zone/main.cpp b/zone/main.cpp index 6897c00c6..c3f303137 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -371,7 +371,7 @@ int main(int argc, char **argv) LogError("Loading items failed!"); LogError("Failed. But ignoring error and going on.."); } - + if (!content_db.LoadSkillCaps(std::string(hotfix_name))) { LogError("Loading skill caps failed!"); return 1; @@ -381,6 +381,7 @@ int main(int argc, char **argv) return 1; } + guild_mgr.LoadGuilds(); content_db.LoadFactionData(); title_manager.LoadTitles(); diff --git a/zone/queryserv.h b/zone/queryserv.h index 5cb12332e..69392037c 100644 --- a/zone/queryserv.h +++ b/zone/queryserv.h @@ -21,6 +21,8 @@ enum PlayerGenericLogEventTypes { Player_Log_Issued_Commands, Player_Log_Money_Transactions, Player_Log_Alternate_Currency_Transactions, + Player_Log_Guild_Tribute_Item_Donation, + Player_Log_Guild_Tribute_Plat_Donation }; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4cef42be2..32a226302 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6596,7 +6596,49 @@ int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool fro focus_max_real = focus_max; UsedItem = TempItem; UsedFocusID = TempItem->Focus.Effect; - } else if (focus_max < 0 && focus_max < focus_max_real) { + } + else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + else { + Total = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + } + } + } + + if (IsClient()) { + //Client Guild Tribute Focus + for (int x = EQ::invslot::GUILD_TRIBUTE_BEGIN; x <= EQ::invslot::GUILD_TRIBUTE_END; ++x) { + TempItem = nullptr; + EQ::ItemInstance* ins = GetInv().GetItem(x); + if (!ins) { + continue; + } + + TempItem = ins->GetItem(); + if (TempItem && IsValidSpell(TempItem->Focus.Effect)) { + if (rand_effectiveness) { + focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + else if (focus_max < 0 && focus_max < focus_max_real) { focus_max_real = focus_max; UsedItem = TempItem; UsedFocusID = TempItem->Focus.Effect; diff --git a/zone/string_ids.h b/zone/string_ids.h index 1ca16e69f..53fbcdaae 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -278,6 +278,7 @@ #define GROWS_DIM 1237 //Your %1 grows dim. #define BEGINS_TO_SHINE 1238 //Your %1 begins to shine. #define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. +#define GUILD_DISBANDED 1377 //Your guild has been disbanded! You are no longer a member of any guild. #define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. #define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else. #define DUEL_CONSIDERING 1385 //%1 is considering a duel with someone else. @@ -384,6 +385,7 @@ #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. #define SUCCOR_FAIL 5169 //The portal collapes before you can escape! +#define NO_PROPER_ACCESS 5410 //You don't have the proper access rights. #define AUGMENT_RESTRICTED 5480 //The item does not satisfy the augment's restrictions. #define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.' #define AVOID_STUNNING_BLOW 5753 //You avoid the stunning blow. @@ -408,6 +410,7 @@ #define TRANSFORM_FAILED 6326 //This mold cannot be applied to your %1. #define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1. #define DETRANSFORM_FAILED 6341 //%1 has no transformation that can be removed. +#define GUILD_PERMISSION_FAILED 6418 //You do not have permission to change access options. #define GENERIC_STRING 6688 //%1 (used to any basic message) #define SENTINEL_TRIG_YOU 6724 //You have triggered your sentinel. #define SENTINEL_TRIG_OTHER 6725 //%1 has triggered your sentinel. diff --git a/zone/tribute.cpp b/zone/tribute.cpp index 18e2bf664..49d93b0ff 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -19,6 +19,10 @@ #include "../common/global_define.h" #include "../common/eq_packet_structs.h" #include "../common/features.h" +#include "../common/repositories/guild_tributes_repository.h" +#include "../common/guild_base.h" +#include "guild_mgr.h" +#include "worldserver.h" #include "client.h" @@ -43,19 +47,7 @@ The server periodicly sends tribute timer updates to the client on live, but I dont see a point to that right now, so I dont do it. */ - - -class TributeData { -public: - //this level data stored in regular byte order and must be flipped before sending - TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; - uint8 tier_count; - uint32 unknown; - std::string name; - std::string description; - bool is_guild; //is a guild tribute item -}; - +extern WorldServer worldserver; std::map tribute_list; void Client::ToggleTribute(bool enabled) { @@ -229,8 +221,8 @@ void Client::SendTributeDetails(uint32 client_id, uint32 tribute_id) { t->client_id = client_id; t->tribute_id = tribute_id; - memcpy(t->desc, td.description.c_str(), len); - t->desc[len] = '\0'; + memcpy(t->description, td.description.c_str(), len); + t->description[len] = '\0'; QueuePacket(&outapp); } @@ -334,47 +326,6 @@ void Client::SendTributes() { } } -void Client::SendGuildTributes() { - - std::map::iterator cur,end; - cur = tribute_list.begin(); - end = tribute_list.end(); - - for(; cur != end; ++cur) { - if(!cur->second.is_guild) - continue; //skip guild tributes here - int len = cur->second.name.length(); - - //guild tribute has an unknown uint32 at its begining, guild ID? - EQApplicationPacket outapp(OP_TributeInfo, sizeof(TributeAbility_Struct) + len + 1 + 4); - uint32 *unknown = (uint32 *) outapp.pBuffer; - TributeAbility_Struct* tas = (TributeAbility_Struct*) (outapp.pBuffer+4); - - //this is prolly wrong in general, prolly for one specific guild - *unknown = 0x8A110000; - - tas->tribute_id = htonl(cur->first); - tas->tier_count = htonl(cur->second.unknown); - - //gotta copy over the data from tiers, and flip all the - //byte orders, no idea why its flipped here - uint32 r, c; - c = cur->second.tier_count; - TributeLevel_Struct *dest = tas->tiers; - TributeLevel_Struct *src = cur->second.tiers; - for(r = 0; r < c; r++, dest++, src++) { - dest->cost = htonl(src->cost); - dest->level = htonl(src->level); - dest->tribute_item_id = htonl(src->tribute_item_id); - } - - memcpy(tas->name, cur->second.name.c_str(), len); - tas->name[len] = '\0'; - - QueuePacket(&outapp); - } -} - bool ZoneDatabase::LoadTributes() { TributeData tributeData; @@ -435,9 +386,243 @@ bool ZoneDatabase::LoadTributes() { return true; } +void Client::SendGuildTributes() +{ + for (auto const& t : tribute_list) { + if (!t.second.is_guild) + continue; //skip non guild tributes here + + //guild tribute has an unknown uint32 at its begining, guild ID? + int len = t.second.name.length() + 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendGuildTributes, sizeof(GuildTributeAbility_Struct) + len); + GuildTributeAbility_Struct* gtas = (GuildTributeAbility_Struct*)outapp->pBuffer; + + auto tier_count = t.second.tier_count; + for (int ti = 0; ti < t.second.tier_count; ti++) { + if (RuleB(Guild, UseCharacterMaxLevelForGuildTributes) && t.second.tiers[ti].level > RuleI(Character, MaxLevel)) { + tier_count -= 1; + continue; + } + gtas->guild_id = GuildID(); + gtas->ability.tier_count = htonl(tier_count); + gtas->ability.tribute_id = htonl(t.first); + gtas->ability.tiers[ti].cost = htonl(t.second.tiers[ti].cost); + gtas->ability.tiers[ti].tribute_item_id = htonl(t.second.tiers[ti].tribute_item_id); + gtas->ability.tiers[ti].level = htonl(t.second.tiers[ti].level); + } + strcpy(gtas->ability.name, t.second.name.data()); + FastQueuePacket(&outapp); + } +} + +void Client::SendGuildTributeDetails(uint32 tribute_id, uint32 tier) +{ + if (tribute_list.count(tribute_id) != 1) { + LogGuilds("Details request for invalid tribute [{}]", tribute_id); + return; + } + + TributeData& td = tribute_list[tribute_id]; + + int len = td.description.length(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildSelectTribute, sizeof(GuildTributeSelectReply_Struct) + len + 1); + GuildTributeSelectReply_Struct* t = (GuildTributeSelectReply_Struct*)outapp->pBuffer; + + t->tribute_id = tribute_id; + t->tier = tier; + t->tribute_id2 = tribute_id; + strncpy(&t->description, td.description.c_str(), len); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::DoGuildTributeUpdate() +{ + LogTribute("DoGuildTributeUpdate"); + auto guild = guild_mgr.GetGuildByGuildID(GuildID()); + + if (guild && guild->tribute.enabled && GuildTributeOptIn()) { + TributeData& d1 = tribute_list[guild->tribute.id_1]; + uint32 item_id1 = d1.tiers[guild->tribute.id_1_tier].tribute_item_id; + TributeData& d2 = tribute_list[guild->tribute.id_2]; + uint32 item_id2 = d2.tiers[guild->tribute.id_2_tier].tribute_item_id; + + if (item_id1) { + LogGuilds("Guild Tribute Item 1 is {}", item_id1); + const EQ::ItemInstance* inst = database.CreateItem(item_id1, 1); + if (!inst) { + LogGuilds("Guild Tribute Item 1 was not found. {}", item_id1); + return; + } + auto inst_level = d1.tiers[guild->tribute.id_1_tier].level; + if (m_inv[EQ::invslot::GUILD_TRIBUTE_BEGIN]) { + LogGuilds("Guild Tribute DELETE Item in Slot 450"); + DeleteItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN); + } + + if ((RuleB(Guild,UseCharacterMaxLevelForGuildTributes) && RuleI(Character, MaxLevel) >= inst_level && GetLevel() >= inst_level) || + !RuleB(Guild, UseCharacterMaxLevelForGuildTributes)) { + PutItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN, *inst); + SendItemPacket(EQ::invslot::GUILD_TRIBUTE_BEGIN, inst, ItemPacketGuildTribute); + } + + safe_delete(inst); + } + + if (item_id2) { + LogInfo("Guild Tribute Item 2 is {}", item_id2); + const EQ::ItemInstance* inst = database.CreateItem(item_id2, 1); + if (!inst) { + LogGuilds("Guild Tribute Item 1 was not found. {}", item_id2); + return; + } + auto inst_level = d2.tiers[guild->tribute.id_2_tier].level; + if (m_inv[EQ::invslot::GUILD_TRIBUTE_BEGIN + 1]) { + DeleteItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN + 1); + LogGuilds("Guild Tribute DELETE Item in Slot 451"); + } + + if ((RuleB(Guild, UseCharacterMaxLevelForGuildTributes) && RuleI(Character, MaxLevel) >= inst_level && GetLevel() >= inst_level) || + !RuleB(Guild, UseCharacterMaxLevelForGuildTributes)) { + PutItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN + 1, *inst); + SendItemPacket(EQ::invslot::GUILD_TRIBUTE_BEGIN + 1, inst, ItemPacketGuildTribute); + } + + safe_delete(inst); + } + } + else { + if (m_inv[EQ::invslot::GUILD_TRIBUTE_BEGIN]) { + DeleteItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN); + } + if (m_inv[EQ::invslot::GUILD_TRIBUTE_BEGIN + 1]) { + DeleteItemInInventory(EQ::invslot::GUILD_TRIBUTE_BEGIN + 1); + } + } + CalcBonuses(); +} + +void Client::SendGuildActiveTributes(uint32 guild_id) +{ + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + + auto outapp = new EQApplicationPacket(OP_GuildSendActiveTributes, sizeof(GuildTributeSendActive_Struct)); + auto gtsa = (GuildTributeSendActive_Struct *) outapp->pBuffer; + + if (guild) { + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->tribute_enabled = guild->tribute.enabled; + gtsa->tribute_id_1 = guild->tribute.id_1; + gtsa->tribute_id_1_tier = guild->tribute.id_1_tier; + gtsa->tribute_id_2 = guild->tribute.id_2; + gtsa->tribute_id_2_tier = guild->tribute.id_2_tier; + } + else { + gtsa->guild_favor = 0; + gtsa->tribute_timer = 0; + gtsa->tribute_enabled = 0; + gtsa->tribute_id_1 = 0xffffffff; + gtsa->tribute_id_1_tier = 0; + gtsa->tribute_id_2 = 0xffffffff; + gtsa->tribute_id_2_tier = 0; + } + + QueuePacket(outapp); + safe_delete(outapp) +} + +void Client::SendGuildFavorAndTimer(uint32 guild_id) +{ + auto guild = guild_mgr.GetGuildByGuildID(guild_id); + + if (guild) { + auto outapp = new EQApplicationPacket(OP_GuildTributeFavorAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + auto gtsa = (GuildTributeFavorTimer_Struct *) outapp->pBuffer; + + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->trophy_timer = 0; //not yet implemented + + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::SendGuildTributeOptInToggle(const GuildTributeMemberToggle* in) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct)); + GuildTributeOptInOutReply_Struct* data = (GuildTributeOptInOutReply_Struct*)outapp->pBuffer; + + strncpy(data->player_name, in->player_name, 64); + data->guild_id = in->guild_id; + data->no_donations = in->no_donations; + data->tribute_toggle = in->tribute_toggle; + data->tribute_trophy_toggle = 0; //not yet implemented + data->time = time(nullptr); + data->command = in->command; + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::RequestGuildActiveTributes(uint32 guild_id) +{ + auto sp = new ServerPacket(ServerOP_RequestGuildActiveTributes, sizeof(GuildTributeUpdate)); + auto data = (GuildTributeUpdate *) sp->pBuffer; + + data->guild_id = GuildID(); + + worldserver.SendPacket(sp); + safe_delete(sp); +} + +void Client::RequestGuildFavorAndTimer(uint32 guild_id) +{ + auto sp = new ServerPacket(ServerOP_RequestGuildFavorAndTimer, sizeof(GuildTributeUpdate)); + auto data = (GuildTributeUpdate *) sp->pBuffer; + + data->guild_id = GuildID(); + + worldserver.SendPacket(sp); + safe_delete(sp); +} + +void Client::SendGuildTributeDonateItemReply(GuildTributeDonateItemRequest_Struct* in, uint32 favor) { + + auto outapp = new EQApplicationPacket(OP_GuildTributeDonateItem, sizeof(GuildTributeDonateItemReply_Struct)); + auto out = (GuildTributeDonateItemReply_Struct*)outapp->pBuffer; + + out->type = in->type; + out->slot = in->slot; + out->aug_index = in->aug_index; + out->sub_index = in->sub_index; + out->quantity = in->quantity; + out->unknown10 = in->unknown10; + out->unknown20 = in->unknown20; + out->favor = favor; + + QueuePacket(outapp); + safe_delete(outapp); + +} + +void Client::SendGuildTributeDonatePlatReply(GuildTributeDonatePlatRequest_Struct* in, uint32 favor) { + + auto outapp = new EQApplicationPacket(OP_GuildTributeDonatePlat, sizeof(GuildTributeDonatePlatReply_Struct)); + auto out = (GuildTributeDonatePlatReply_Struct*)outapp->pBuffer; + + out->favor = favor; + out->quantity = in->quantity; + + QueuePacket(outapp); + safe_delete(outapp) + +} /* - 64.37.149.6:1353 == server 66.90.221.245:3173 == client diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index c211908be..d10eb473e 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -57,6 +57,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "dialogue_window.h" #include "bot_command.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/guild_tributes_repository.h" extern EntityList entity_list; extern Zone* zone; @@ -781,8 +782,18 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_DeleteGuild: case ServerOP_GuildCharRefresh: case ServerOP_GuildMemberUpdate: + case ServerOP_GuildPermissionUpdate: case ServerOP_GuildRankUpdate: + case ServerOP_GuildRankNameChange: case ServerOP_LFGuildUpdate: + case ServerOP_GuildMemberLevelUpdate: + case ServerOP_GuildMemberPublicNote: + case ServerOP_GuildChannel: + case ServerOP_GuildURL: + case ServerOP_GuildMemberRemove: + case ServerOP_GuildMemberAdd: + case ServerOP_GuildSendGuildList: + case ServerOP_GuildMembersList: { guild_mgr.ProcessWorldPacket(pack); break; @@ -3613,6 +3624,225 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) DataBucket::HandleWorldMessage(pack); break; } + case ServerOP_GuildTributeUpdate: { + GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + if (guild) { + guild->tribute.favor = in->favor; + guild->tribute.id_1 = in->tribute_id_1; + guild->tribute.id_2 = in->tribute_id_2; + guild->tribute.id_1_tier = in->tribute_id_1_tier; + guild->tribute.id_2_tier = in->tribute_id_2_tier; + guild->tribute.time_remaining = in->time_remaining; + guild->tribute.enabled = in->enabled; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildSendActiveTributes, sizeof(GuildTributeSendActive_Struct)); + GuildTributeSendActive_Struct* out = (GuildTributeSendActive_Struct*)outapp->pBuffer; + + out->not_used = in->guild_id; + out->guild_favor = in->favor; + out->tribute_enabled = in->enabled; + out->tribute_timer = in->time_remaining; + out->tribute_id_1 = in->tribute_id_1; + out->tribute_id_2 = in->tribute_id_2; + out->tribute_id_1_tier = in->tribute_id_1_tier; + out->tribute_id_2_tier = in->tribute_id_2_tier; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + break; + } + case ServerOP_GuildTributeActivate: { + GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + if (guild) { + guild->tribute.favor = in->favor; + guild->tribute.id_1 = in->tribute_id_1; + guild->tribute.id_2 = in->tribute_id_2; + guild->tribute.id_1_tier = in->tribute_id_1_tier; + guild->tribute.id_2_tier = in->tribute_id_2_tier; + guild->tribute.time_remaining = in->time_remaining; + guild->tribute.enabled = in->enabled; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildTributeToggleReply, sizeof(GuildTributeSendActive_Struct)); + GuildTributeSendActive_Struct* out = (GuildTributeSendActive_Struct*)outapp->pBuffer; + + out->not_used = in->guild_id; + out->guild_favor = in->favor; + out->tribute_enabled = in->enabled; + out->tribute_timer = in->time_remaining; + out->tribute_id_1 = in->tribute_id_1; + out->tribute_id_2 = in->tribute_id_2; + out->tribute_id_1_tier = in->tribute_id_1_tier; + out->tribute_id_2_tier = in->tribute_id_2_tier; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + + for (auto& c : entity_list.GetClientList()) { + if (c.second->IsInGuild(in->guild_id)) { + c.second->DoGuildTributeUpdate(); + } + } + break; + } + case ServerOP_GuildTributeUpdateDonations: + { + GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct)); + GuildTributeOptInOutReply_Struct* data = (GuildTributeOptInOutReply_Struct*)outapp->pBuffer; + + data->guild_id = in->guild_id; + strn0cpy(data->player_name, in->player_name, sizeof(data->player_name)); + data->no_donations = in->member_favor; + data->tribute_toggle = in->member_enabled ? true : false; + data->tribute_trophy_toggle = 0; //not yet implemented + data->time = in->member_time; + data->command = 1; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + + //my new items + outapp = new EQApplicationPacket(OP_GuildTributeToggleReply, sizeof(GuildTributeSendActive_Struct)); + GuildTributeSendActive_Struct *out = (GuildTributeSendActive_Struct *) outapp->pBuffer; + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + out->not_used = in->guild_id; + out->guild_favor = guild->tribute.favor; + out->tribute_enabled = guild->tribute.enabled; + out->tribute_timer = guild->tribute.time_remaining; + out->tribute_id_1 = guild->tribute.id_1; + out->tribute_id_2 = guild->tribute.id_2; + out->tribute_id_1_tier = guild->tribute.id_1_tier; + out->tribute_id_2_tier = guild->tribute.id_2_tier; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp) + + break; + } + case ServerOP_GuildTributeOptInToggle: + { + GuildTributeMemberToggle* in = (GuildTributeMemberToggle*)pack->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct)); + GuildTributeOptInOutReply_Struct* data = (GuildTributeOptInOutReply_Struct*)outapp->pBuffer; + + data->guild_id = in->guild_id; + strn0cpy(data->player_name, in->player_name, sizeof(data->player_name)); + data->no_donations = in->no_donations; + data->tribute_toggle = in->tribute_toggle ? true : false; + data->tribute_trophy_toggle = 0; //not yet implemented + data->time = in->member_last_donated; + data->command = in->command; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + auto client = entity_list.GetClientByCharID(in->char_id); + if (guild && client) { + client->SetGuildTributeOptIn(in->tribute_toggle ? true : false); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildTributeToggleReply, sizeof(GuildTributeSendActive_Struct)); + GuildTributeSendActive_Struct* out = (GuildTributeSendActive_Struct*)outapp->pBuffer; + + out->not_used = in->guild_id; + out->guild_favor = guild->tribute.favor; + out->tribute_enabled = guild->tribute.enabled; + out->tribute_timer = guild->tribute.time_remaining; + out->tribute_id_1 = guild->tribute.id_1; + out->tribute_id_2 = guild->tribute.id_2; + out->tribute_id_1_tier = guild->tribute.id_1_tier; + out->tribute_id_2_tier = guild->tribute.id_2_tier; + client->QueuePacket(outapp); + safe_delete(outapp); + //send deactivate and then activate + + client->DoGuildTributeUpdate(); + } + + break; + } + case ServerOP_GuildTributeFavAndTimer: + { + GuildTributeFavorTimer_Struct* in = (GuildTributeFavorTimer_Struct*)pack->pBuffer; + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + if (guild) { + guild->tribute.favor = in->guild_favor; + guild->tribute.time_remaining = in->tribute_timer; + + auto outapp = new EQApplicationPacket(OP_GuildTributeFavorAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + GuildTributeFavorTimer_Struct* gtsa = (GuildTributeFavorTimer_Struct*)outapp->pBuffer; + + gtsa->guild_id = in->guild_id; + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->trophy_timer = 0; //not yet implemented + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + } + break; + } + case ServerOP_RequestGuildActiveTributes: + { + GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + + if (guild) { + auto outapp = new EQApplicationPacket(OP_GuildSendActiveTributes, sizeof(GuildTributeSendActive_Struct)); + GuildTributeSendActive_Struct* gtsa = (GuildTributeSendActive_Struct*)outapp->pBuffer; + + guild->tribute.enabled = in->enabled; + guild->tribute.favor = in->favor; + guild->tribute.id_1 = in->tribute_id_1; + guild->tribute.id_2 = in->tribute_id_2; + guild->tribute.id_1_tier = in->tribute_id_1_tier; + guild->tribute.id_2_tier = in->tribute_id_2_tier; + guild->tribute.time_remaining = in->time_remaining; + + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->tribute_enabled = guild->tribute.enabled; + gtsa->tribute_id_1 = guild->tribute.id_1; + gtsa->tribute_id_1_tier = guild->tribute.id_1_tier; + gtsa->tribute_id_2 = guild->tribute.id_2; + gtsa->tribute_id_2_tier = guild->tribute.id_2_tier; + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + } + break; + } + case ServerOP_RequestGuildFavorAndTimer: + { + GuildTributeFavorTimer_Struct* in = (GuildTributeFavorTimer_Struct*)pack->pBuffer; + + auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); + if (guild) { + guild->tribute.favor = in->guild_favor; + guild->tribute.time_remaining = in->tribute_timer; + + auto outapp = new EQApplicationPacket(OP_GuildTributeFavorAndTimer, sizeof(GuildTributeFavorTimer_Struct)); + GuildTributeFavorTimer_Struct* gtsa = (GuildTributeFavorTimer_Struct*)outapp->pBuffer; + + gtsa->guild_id = in->guild_id; + gtsa->guild_favor = guild->tribute.favor; + gtsa->tribute_timer = guild->tribute.time_remaining; + gtsa->trophy_timer = 0; //not yet implemented + + entity_list.QueueClientsGuild(outapp, in->guild_id); + safe_delete(outapp); + } + break; + } default: { LogInfo("Unknown ZS Opcode [{}] size [{}]", (int)pack->opcode, pack->size); break; diff --git a/zone/zone.cpp b/zone/zone.cpp index a4333c461..c05f0643c 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1203,6 +1203,8 @@ bool Zone::Init(bool is_static) { DynamicZone::CacheAllFromDatabase(); Expedition::CacheAllFromDatabase(); + guild_mgr.LoadGuilds(); + LogInfo("Loading timezone data"); zone_time.setEQTimeZone(content_db.GetZoneTimezone(zoneid, GetInstanceVersion())); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp old mode 100755 new mode 100644