From 738a31df51a991e9816e3b3558b6fe7e234cfe75 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:17:33 -0400 Subject: [PATCH 01/73] [Feature] Implement "Big Bags" --- common/database/database_update_manifest.cpp | 60 ++ common/emu_constants.h | 68 +- common/inventory_profile.cpp | 4 +- common/patches/rof2_limits.h | 130 ++-- common/patches/titanium_structs.h | 12 +- .../base/base_inventory_repository.h | 272 +++---- .../base/base_sharedbank_repository.h | 560 +++++++++++++++ common/repositories/sharedbank_repository.h | 339 +-------- common/ruletypes.h | 2 +- common/shareddb.cpp | 665 ++++++++---------- common/shareddb.h | 1 - common/version.h | 2 +- .../generators/repository-generator.pl | 1 - world/client.cpp | 27 +- zone/client.cpp | 8 +- zone/client_packet.cpp | 54 +- zone/client_process.cpp | 17 +- zone/corpse.cpp | 4 +- zone/embparser_api.cpp | 38 +- zone/gm_commands/show/inventory.cpp | 8 +- zone/inventory.cpp | 54 +- zone/zonedb.h | 8 +- 22 files changed, 1322 insertions(+), 1012 deletions(-) create mode 100644 common/repositories/base/base_sharedbank_repository.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 636899094e..5cfbed3454 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5770,6 +5770,66 @@ MODIFY COLUMN `exp_modifier` float NOT NULL DEFAULT 1.0 AFTER `aa_modifier`; CREATE INDEX idx_character_expires ON data_buckets (character_id, expires); CREATE INDEX idx_npc_expires ON data_buckets (npc_id, expires); CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires); +)" + }, + ManifestEntry{ + .version = 9286, + .description = "2024_10_24_sharedbank_guid_primary_key.sql", + .check = "SHOW COLUMN FROM `sharedbank` LIKE 'guid'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `sharedbank` +CHANGE COLUMN `acctid` `account_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST, +CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `account_id`, +CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `slot_id`, +CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`, +CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`, +CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`, +CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`, +CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`, +CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`, +MODIFY COLUMN `charges` smallint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `item_id`, +ADD COLUMN `color` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`, +ADD COLUMN `ornament_icon` int(11) UNSIGNED NOT NULL AFTER `custom_data`, +ADD COLUMN `ornament_idfile` int(11) UNSIGNED NOT NULL AFTER `ornament_icon`, +ADD COLUMN `ornament_hero_model` int(11) NOT NULL AFTER `ornament_idfile`, +ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_model`, +ADD PRIMARY KEY (`account_id`, `slot_id`); +)" + }, + ManifestEntry{ + .version = 9287, + .description = "2024_10_24_inventory_changes.sql", + .check = "SHOW COLUMN FROM `inventory` LIKE 'charid'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `inventory` +CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST, +CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`, +CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NULL DEFAULT 0 AFTER `slot_id`, +CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `color`, +CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`, +CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`, +CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`, +CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`, +CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`, +CHANGE COLUMN `ornamenticon` `ornament_icon` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `custom_data`, +CHANGE COLUMN `ornamentidfile` `ornament_idfile` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_icon`, +DROP PRIMARY KEY, +ADD PRIMARY KEY (`character_id`, `slot_id`) USING BTREE; +)" + }, + ManifestEntry{ + .version = 9288, + .description = "2024_10_24_merchantlist_temp_uncap.sql", + .check = "SHOW CREATE TABLE `merchantlist_temp`", + .condition = "contains", + .match = "`slot` tinyint(3)", + .sql = R"( +ALTER TABLE `merchantlist_temp` +MODIFY COLUMN `slot` int UNSIGNED NOT NULL DEFAULT 0 AFTER `npcid`; )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/emu_constants.h b/common/emu_constants.h index 83056cfb2e..9bd1945308 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -132,7 +132,7 @@ namespace EQ using RoF2::invtype::KRONO_SIZE; using RoF2::invtype::OTHER_SIZE; - using Titanium::invtype::TRADE_NPC_SIZE; + using RoF2::invtype::TRADE_NPC_SIZE; using RoF2::invtype::TYPE_INVALID; using RoF2::invtype::TYPE_BEGIN; @@ -159,7 +159,7 @@ namespace EQ using RoF2::invslot::SLOT_INVALID; using RoF2::invslot::SLOT_BEGIN; - using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + using RoF2::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value) @@ -179,28 +179,28 @@ namespace EQ using RoF2::invslot::BONUS_STAT_END; using RoF2::invslot::BONUS_SKILL_END; - using Titanium::invslot::BANK_BEGIN; - using SoF::invslot::BANK_END; + using RoF2::invslot::BANK_BEGIN; + using RoF2::invslot::BANK_END; - using Titanium::invslot::SHARED_BANK_BEGIN; - using Titanium::invslot::SHARED_BANK_END; + using RoF2::invslot::SHARED_BANK_BEGIN; + using RoF2::invslot::SHARED_BANK_END; - using Titanium::invslot::TRADE_BEGIN; - using Titanium::invslot::TRADE_END; + using RoF2::invslot::TRADE_BEGIN; + using RoF2::invslot::TRADE_END; - using Titanium::invslot::TRADE_NPC_END; + using RoF2::invslot::TRADE_NPC_END; - using Titanium::invslot::WORLD_BEGIN; - using Titanium::invslot::WORLD_END; + using RoF2::invslot::WORLD_BEGIN; + using RoF2::invslot::WORLD_END; - using Titanium::invslot::TRIBUTE_BEGIN; - using Titanium::invslot::TRIBUTE_END; + using RoF2::invslot::TRIBUTE_BEGIN; + using RoF2::invslot::TRIBUTE_END; - using Titanium::invslot::GUILD_TRIBUTE_BEGIN; - using Titanium::invslot::GUILD_TRIBUTE_END; + using RoF2::invslot::GUILD_TRIBUTE_BEGIN; + using RoF2::invslot::GUILD_TRIBUTE_END; const int16 CORPSE_BEGIN = invslot::slotGeneral1; - const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor; + const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor; using RoF2::invslot::EQUIPMENT_BITMASK; using RoF2::invslot::GENERAL_BITMASK; @@ -214,38 +214,40 @@ namespace EQ } // namespace invslot namespace invbag { - using Titanium::invbag::SLOT_INVALID; - using Titanium::invbag::SLOT_BEGIN; - using Titanium::invbag::SLOT_END; - using Titanium::invbag::SLOT_COUNT; + using RoF2::invbag::SLOT_INVALID; + using RoF2::invbag::SLOT_BEGIN; + using RoF2::invbag::SLOT_END; + using RoF2::invbag::SLOT_COUNT; - using Titanium::invbag::GENERAL_BAGS_BEGIN; + using RoF2::invslot::WORLD_END; + + const int16 GENERAL_BAGS_BEGIN = WORLD_END + 1; const int16 GENERAL_BAGS_COUNT = invslot::GENERAL_COUNT * SLOT_COUNT; - const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1; + const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1; const int16 GENERAL_BAGS_8_COUNT = 8 * SLOT_COUNT; - const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1; + const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1; - const int16 CURSOR_BAG_BEGIN = 351; + const int16 CURSOR_BAG_BEGIN = GENERAL_BAGS_END + 1; const int16 CURSOR_BAG_COUNT = SLOT_COUNT; - const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1; + const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1; - using Titanium::invbag::BANK_BAGS_BEGIN; + const int16 BANK_BAGS_BEGIN = CURSOR_BAG_END + 1; const int16 BANK_BAGS_COUNT = (invtype::BANK_SIZE * SLOT_COUNT); - const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1; + const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1; const int16 BANK_BAGS_16_COUNT = 16 * SLOT_COUNT; - const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1; + const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1; - using Titanium::invbag::SHARED_BANK_BAGS_BEGIN; + const int16 SHARED_BANK_BAGS_BEGIN = BANK_BAGS_END + 1; const int16 SHARED_BANK_BAGS_COUNT = invtype::SHARED_BANK_SIZE * SLOT_COUNT; - const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1; + const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1; - using Titanium::invbag::TRADE_BAGS_BEGIN; + const int16 TRADE_BAGS_BEGIN = SHARED_BANK_BAGS_END + 1; const int16 TRADE_BAGS_COUNT = invtype::TRADE_SIZE * SLOT_COUNT; - const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1; + const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1; - using Titanium::invbag::GetInvBagIndexName; + using RoF2::invbag::GetInvBagIndexName; } // namespace invbag diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index df81e516c4..5f1dd6b681 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -1027,7 +1027,7 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { int16 slot_id = INVALID_INDEX; - if (bagslot_id == invslot::slotCursor || bagslot_id == 8000) { + if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; } else if (bagslot_id >= invslot::GENERAL_BEGIN && bagslot_id <= invslot::GENERAL_END) { @@ -1807,4 +1807,4 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it } } return 0; -} \ No newline at end of file +} diff --git a/common/patches/rof2_limits.h b/common/patches/rof2_limits.h index 7e7774de65..4ebd16769f 100644 --- a/common/patches/rof2_limits.h +++ b/common/patches/rof2_limits.h @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ namespace RoF2 const bool AllowOverLevelEquipment = true; - const bool AllowEmptyBagInBag = true; + const bool AllowEmptyBagInBag = true; const bool AllowClickCastFromBag = true; } /*inventory*/ @@ -77,38 +77,38 @@ namespace RoF2 } // namespace enum_ using namespace enum_; - const int16 POSSESSIONS_SIZE = 34; - const int16 BANK_SIZE = 24; - const int16 SHARED_BANK_SIZE = 2; - const int16 TRADE_SIZE = 8; - const int16 WORLD_SIZE = 10; - const int16 LIMBO_SIZE = 36; - const int16 TRIBUTE_SIZE = 5; - const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown - const int16 GUILD_TRIBUTE_SIZE = 2;//unverified - const int16 MERCHANT_SIZE = 200; - const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab" - const int16 CORPSE_SIZE = POSSESSIONS_SIZE; - const int16 BAZAAR_SIZE = 200; - const int16 INSPECT_SIZE = 23; - const int16 REAL_ESTATE_SIZE = 0;//unknown - const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE; - const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE; + const int16 POSSESSIONS_SIZE = 34; + const int16 BANK_SIZE = 24; + const int16 SHARED_BANK_SIZE = 2; + const int16 TRADE_SIZE = 8; + const int16 WORLD_SIZE = 10; + const int16 LIMBO_SIZE = 36; + const int16 TRIBUTE_SIZE = 5; + const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown + const int16 GUILD_TRIBUTE_SIZE = 2;//unverified + const int16 MERCHANT_SIZE = 500; + const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab" + const int16 CORPSE_SIZE = POSSESSIONS_SIZE; + const int16 BAZAAR_SIZE = 200; + const int16 INSPECT_SIZE = 23; + const int16 REAL_ESTATE_SIZE = 0;//unknown + const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE; + const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE; const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE; - const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE; - const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank" - const int16 ARCHIVED_SIZE = 0;//unknown - const int16 MAIL_SIZE = 0;//unknown + const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE; + const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank" + const int16 ARCHIVED_SIZE = 0;//unknown + const int16 MAIL_SIZE = 0;//unknown const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown - const int16 KRONO_SIZE = 0;//unknown - const int16 OTHER_SIZE = 0;//unknown + const int16 KRONO_SIZE = 0;//unknown + const int16 OTHER_SIZE = 0;//unknown const int16 TRADE_NPC_SIZE = 4; // defined by implication const int16 TYPE_INVALID = IINVALID; - const int16 TYPE_BEGIN = typePossessions; - const int16 TYPE_END = typeOther; - const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1; + const int16 TYPE_BEGIN = typePossessions; + const int16 TYPE_END = typeOther; + const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1; int16 GetInvTypeSize(int16 inv_type); const char* GetInvTypeName(int16 inv_type); @@ -162,33 +162,54 @@ namespace RoF2 } // namespace enum_ using namespace enum_; - const int16 SLOT_INVALID = IINVALID; - const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + + const int16 BANK_BEGIN = 2000; + const int16 BANK_END = (BANK_BEGIN + invtype::BANK_SIZE) - 1; + + const int16 SHARED_BANK_BEGIN = 2500; + const int16 SHARED_BANK_END = (SHARED_BANK_BEGIN + invtype::SHARED_BANK_SIZE) - 1; + + const int16 TRADE_BEGIN = 3000; + const int16 TRADE_END = (TRADE_BEGIN + invtype::TRADE_SIZE) - 1; + + const int16 TRADE_NPC_END = (TRADE_BEGIN + invtype::TRADE_NPC_SIZE) - 1; // defined by implication + + const int16 WORLD_BEGIN = 4000; + const int16 WORLD_END = (WORLD_BEGIN + invtype::WORLD_SIZE) - 1; + + const int16 TRIBUTE_BEGIN = 400; + const int16 TRIBUTE_END = (TRIBUTE_BEGIN + invtype::TRIBUTE_SIZE) - 1; + + const int16 GUILD_TRIBUTE_BEGIN = 450; + const int16 GUILD_TRIBUTE_END = (GUILD_TRIBUTE_BEGIN + invtype::GUILD_TRIBUTE_SIZE) - 1; const int16 POSSESSIONS_BEGIN = slotCharm; - const int16 POSSESSIONS_END = slotCursor; + const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; const int16 EQUIPMENT_BEGIN = slotCharm; - const int16 EQUIPMENT_END = slotAmmo; + const int16 EQUIPMENT_END = slotAmmo; const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1; const int16 GENERAL_BEGIN = slotGeneral1; - const int16 GENERAL_END = slotGeneral10; + const int16 GENERAL_END = slotGeneral10; const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1; - const int16 BONUS_BEGIN = invslot::slotCharm; - const int16 BONUS_STAT_END = invslot::slotPowerSource; + const int16 BONUS_BEGIN = invslot::slotCharm; + const int16 BONUS_STAT_END = invslot::slotPowerSource; const int16 BONUS_SKILL_END = invslot::slotAmmo; const int16 CORPSE_BEGIN = invslot::slotGeneral1; - const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; + const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; - const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; - const uint64 GENERAL_BITMASK = 0x00000001FF800000; - const uint64 CURSOR_BITMASK = 0x0000000200000000; + const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; + const uint64 GENERAL_BITMASK = 0x00000001FF800000; + const uint64 CURSOR_BITMASK = 0x0000000200000000; const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 34-slot count (RoF+) - const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) + const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) const char* GetInvPossessionsSlotName(int16 inv_slot); @@ -199,10 +220,21 @@ namespace RoF2 namespace invbag { inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::RoF2; } - const int16 SLOT_INVALID = IINVALID; - const int16 SLOT_BEGIN = INULL; - const int16 SLOT_END = 9; //254; - const int16 SLOT_COUNT = 10; //255; // server Size will be 255..unsure what actual client is (test) + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + const int16 SLOT_COUNT = 200; + const int16 SLOT_END = SLOT_COUNT - 1; + + const int16 GENERAL_BAGS_BEGIN = 251; + + const int16 CURSOR_BAG_BEGIN = 351; + + const int16 BANK_BAGS_BEGIN = 2031; + + const int16 SHARED_BANK_BAGS_BEGIN = 2531; + + const int16 TRADE_BAGS_BEGIN = 3031; const char* GetInvBagIndexName(int16 bag_index); @@ -212,9 +244,9 @@ namespace RoF2 inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::RoF2; } const int16 SOCKET_INVALID = IINVALID; - const int16 SOCKET_BEGIN = INULL; - const int16 SOCKET_END = 5; - const int16 SOCKET_COUNT = 6; + const int16 SOCKET_BEGIN = INULL; + const int16 SOCKET_END = 5; + const int16 SOCKET_COUNT = 6; const char* GetInvAugIndexName(int16 aug_index); @@ -291,7 +323,7 @@ namespace RoF2 namespace spells { inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::RoF2; } - + enum class CastingSlot : uint32 { Gem1 = 0, Gem2 = 1, @@ -314,7 +346,7 @@ namespace RoF2 const int SPELL_ID_MAX = 45000; const int SPELLBOOK_SIZE = 720; const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); - + const int LONG_BUFFS = 42; const int SHORT_BUFFS = 20; const int DISC_BUFFS = 1; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 8311983639..2d92759425 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -2463,25 +2463,25 @@ struct WhoAllReturnStruct { struct BeginTrader_Struct { uint32 action; uint32 unknown04; - uint64 serial_number[80]; - uint32 cost[80]; + uint64 serial_number[EQ::invtype::BAZAAR_SIZE]; + uint32 cost[EQ::invtype::BAZAAR_SIZE]; }; struct Trader_Struct { uint32 action; uint32 unknown004; - uint64 item_id[80]; - uint32 item_cost[80]; + uint64 item_id[EQ::invtype::BAZAAR_SIZE]; + uint32 item_cost[EQ::invtype::BAZAAR_SIZE]; }; struct ClickTrader_Struct { uint32 code; uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE - uint32 itemcost[80]; + uint32 itemcost[EQ::invtype::BAZAAR_SIZE]; }; struct GetItems_Struct{ - uint32 items[80]; + uint32 items[EQ::invtype::BAZAAR_SIZE]; }; struct BecomeTrader_Struct { diff --git a/common/repositories/base/base_inventory_repository.h b/common/repositories/base/base_inventory_repository.h index a029ac6b41..9a4b5122b6 100644 --- a/common/repositories/base/base_inventory_repository.h +++ b/common/repositories/base/base_inventory_repository.h @@ -19,48 +19,48 @@ class BaseInventoryRepository { public: struct Inventory { - uint32_t charid; - uint32_t slotid; - uint32_t itemid; + uint32_t character_id; + uint32_t slot_id; + uint32_t item_id; uint16_t charges; uint32_t color; - uint32_t augslot1; - uint32_t augslot2; - uint32_t augslot3; - uint32_t augslot4; - uint32_t augslot5; - int32_t augslot6; + uint32_t augment_one; + uint32_t augment_two; + uint32_t augment_three; + uint32_t augment_four; + uint32_t augment_five; + uint32_t augment_six; uint8_t instnodrop; std::string custom_data; - uint32_t ornamenticon; - uint32_t ornamentidfile; + uint32_t ornament_icon; + uint32_t ornament_idfile; int32_t ornament_hero_model; uint64_t guid; }; static std::string PrimaryKey() { - return std::string("charid"); + return std::string("character_id"); } static std::vector Columns() { return { - "charid", - "slotid", - "itemid", + "character_id", + "slot_id", + "item_id", "charges", "color", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "instnodrop", "custom_data", - "ornamenticon", - "ornamentidfile", + "ornament_icon", + "ornament_idfile", "ornament_hero_model", "guid", }; @@ -69,21 +69,21 @@ class BaseInventoryRepository { static std::vector SelectColumns() { return { - "charid", - "slotid", - "itemid", + "character_id", + "slot_id", + "item_id", "charges", "color", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "instnodrop", "custom_data", - "ornamenticon", - "ornamentidfile", + "ornament_icon", + "ornament_idfile", "ornament_hero_model", "guid", }; @@ -126,21 +126,21 @@ class BaseInventoryRepository { { Inventory e{}; - e.charid = 0; - e.slotid = 0; - e.itemid = 0; + e.character_id = 0; + e.slot_id = 0; + e.item_id = 0; e.charges = 0; e.color = 0; - e.augslot1 = 0; - e.augslot2 = 0; - e.augslot3 = 0; - e.augslot4 = 0; - e.augslot5 = 0; - e.augslot6 = 0; + e.augment_one = 0; + e.augment_two = 0; + e.augment_three = 0; + e.augment_four = 0; + e.augment_five = 0; + e.augment_six = 0; e.instnodrop = 0; e.custom_data = ""; - e.ornamenticon = 0; - e.ornamentidfile = 0; + e.ornament_icon = 0; + e.ornament_idfile = 0; e.ornament_hero_model = 0; e.guid = 0; @@ -153,7 +153,7 @@ class BaseInventoryRepository { ) { for (auto &inventory : inventorys) { - if (inventory.charid == inventory_id) { + if (inventory.character_id == inventory_id) { return inventory; } } @@ -179,21 +179,21 @@ class BaseInventoryRepository { if (results.RowCount() == 1) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -229,21 +229,21 @@ class BaseInventoryRepository { auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.charid)); - v.push_back(columns[1] + " = " + std::to_string(e.slotid)); - v.push_back(columns[2] + " = " + std::to_string(e.itemid)); + v.push_back(columns[0] + " = " + std::to_string(e.character_id)); + v.push_back(columns[1] + " = " + std::to_string(e.slot_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); v.push_back(columns[3] + " = " + std::to_string(e.charges)); v.push_back(columns[4] + " = " + std::to_string(e.color)); - v.push_back(columns[5] + " = " + std::to_string(e.augslot1)); - v.push_back(columns[6] + " = " + std::to_string(e.augslot2)); - v.push_back(columns[7] + " = " + std::to_string(e.augslot3)); - v.push_back(columns[8] + " = " + std::to_string(e.augslot4)); - v.push_back(columns[9] + " = " + std::to_string(e.augslot5)); - v.push_back(columns[10] + " = " + std::to_string(e.augslot6)); + v.push_back(columns[5] + " = " + std::to_string(e.augment_one)); + v.push_back(columns[6] + " = " + std::to_string(e.augment_two)); + v.push_back(columns[7] + " = " + std::to_string(e.augment_three)); + v.push_back(columns[8] + " = " + std::to_string(e.augment_four)); + v.push_back(columns[9] + " = " + std::to_string(e.augment_five)); + v.push_back(columns[10] + " = " + std::to_string(e.augment_six)); v.push_back(columns[11] + " = " + std::to_string(e.instnodrop)); v.push_back(columns[12] + " = '" + Strings::Escape(e.custom_data) + "'"); - v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon)); - v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile)); + v.push_back(columns[13] + " = " + std::to_string(e.ornament_icon)); + v.push_back(columns[14] + " = " + std::to_string(e.ornament_idfile)); v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model)); v.push_back(columns[16] + " = " + std::to_string(e.guid)); @@ -253,7 +253,7 @@ class BaseInventoryRepository { TableName(), Strings::Implode(", ", v), PrimaryKey(), - e.charid + e.character_id ) ); @@ -267,21 +267,21 @@ class BaseInventoryRepository { { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -294,7 +294,7 @@ class BaseInventoryRepository { ); if (results.Success()) { - e.charid = results.LastInsertedID(); + e.character_id = results.LastInsertedID(); return e; } @@ -313,21 +313,21 @@ class BaseInventoryRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -363,21 +363,21 @@ class BaseInventoryRepository { for (auto row = results.begin(); row != results.end(); ++row) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -404,21 +404,21 @@ class BaseInventoryRepository { for (auto row = results.begin(); row != results.end(); ++row) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -495,21 +495,21 @@ class BaseInventoryRepository { { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -534,21 +534,21 @@ class BaseInventoryRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); diff --git a/common/repositories/base/base_sharedbank_repository.h b/common/repositories/base/base_sharedbank_repository.h new file mode 100644 index 0000000000..d208251da4 --- /dev/null +++ b/common/repositories/base/base_sharedbank_repository.h @@ -0,0 +1,560 @@ +/** + * 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_SHAREDBANK_REPOSITORY_H +#define EQEMU_BASE_SHAREDBANK_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseSharedbankRepository { +public: + struct Sharedbank { + uint32_t account_id; + uint32_t slot_id; + uint32_t item_id; + uint16_t charges; + uint32_t color; + uint32_t augment_one; + uint32_t augment_two; + uint32_t augment_three; + uint32_t augment_four; + uint32_t augment_five; + uint32_t augment_six; + std::string custom_data; + uint32_t ornament_icon; + uint32_t ornament_idfile; + int32_t ornament_hero_model; + uint64_t guid; + }; + + static std::string PrimaryKey() + { + return std::string("account_id"); + } + + static std::vector Columns() + { + return { + "account_id", + "slot_id", + "item_id", + "charges", + "color", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", + "custom_data", + "ornament_icon", + "ornament_idfile", + "ornament_hero_model", + "guid", + }; + } + + static std::vector SelectColumns() + { + return { + "account_id", + "slot_id", + "item_id", + "charges", + "color", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", + "custom_data", + "ornament_icon", + "ornament_idfile", + "ornament_hero_model", + "guid", + }; + } + + 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("sharedbank"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static Sharedbank NewEntity() + { + Sharedbank e{}; + + e.account_id = 0; + e.slot_id = 0; + e.item_id = 0; + e.charges = 0; + e.color = 0; + e.augment_one = 0; + e.augment_two = 0; + e.augment_three = 0; + e.augment_four = 0; + e.augment_five = 0; + e.augment_six = 0; + e.custom_data = ""; + e.ornament_icon = 0; + e.ornament_idfile = 0; + e.ornament_hero_model = 0; + e.guid = 0; + + return e; + } + + static Sharedbank GetSharedbank( + const std::vector &sharedbanks, + int sharedbank_id + ) + { + for (auto &sharedbank : sharedbanks) { + if (sharedbank.account_id == sharedbank_id) { + return sharedbank; + } + } + + return NewEntity(); + } + + static Sharedbank FindOne( + Database& db, + int sharedbank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + sharedbank_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int sharedbank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + sharedbank_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const Sharedbank &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.account_id)); + v.push_back(columns[1] + " = " + std::to_string(e.slot_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); + v.push_back(columns[3] + " = " + std::to_string(e.charges)); + v.push_back(columns[4] + " = " + std::to_string(e.color)); + v.push_back(columns[5] + " = " + std::to_string(e.augment_one)); + v.push_back(columns[6] + " = " + std::to_string(e.augment_two)); + v.push_back(columns[7] + " = " + std::to_string(e.augment_three)); + v.push_back(columns[8] + " = " + std::to_string(e.augment_four)); + v.push_back(columns[9] + " = " + std::to_string(e.augment_five)); + v.push_back(columns[10] + " = " + std::to_string(e.augment_six)); + v.push_back(columns[11] + " = '" + Strings::Escape(e.custom_data) + "'"); + v.push_back(columns[12] + " = " + std::to_string(e.ornament_icon)); + v.push_back(columns[13] + " = " + std::to_string(e.ornament_idfile)); + v.push_back(columns[14] + " = " + std::to_string(e.ornament_hero_model)); + v.push_back(columns[15] + " = " + std::to_string(e.guid)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.account_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static Sharedbank InsertOne( + Database& db, + Sharedbank e + ) + { + std::vector v; + + v.push_back(std::to_string(e.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.account_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.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + 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) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + 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 Sharedbank &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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_SHAREDBANK_REPOSITORY_H diff --git a/common/repositories/sharedbank_repository.h b/common/repositories/sharedbank_repository.h index 15243d83e2..d0aa47eafb 100644 --- a/common/repositories/sharedbank_repository.h +++ b/common/repositories/sharedbank_repository.h @@ -3,310 +3,47 @@ #include "../database.h" #include "../strings.h" +#include "base/base_sharedbank_repository.h" -class SharedbankRepository { +class SharedbankRepository: public BaseSharedbankRepository { public: - struct Sharedbank { - int acctid; - int slotid; - int itemid; - int16 charges; - int augslot1; - int augslot2; - int augslot3; - int augslot4; - int augslot5; - int augslot6; - std::string custom_data; - }; - static std::string PrimaryKey() - { - return std::string(""); - } - - static std::vector Columns() - { - return { - "acctid", - "slotid", - "itemid", - "charges", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", - "custom_data", - }; - } - - 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("sharedbank"); - } - - static std::string BaseSelect() - { - return fmt::format( - "SELECT {} FROM {}", - ColumnsRaw(), - TableName() - ); - } - - static std::string BaseInsert() - { - return fmt::format( - "INSERT INTO {} ({}) ", - TableName(), - InsertColumnsRaw() - ); - } - - static Sharedbank NewEntity() - { - Sharedbank entry{}; - - entry.acctid = 0; - entry.slotid = 0; - entry.itemid = 0; - entry.charges = 0; - entry.augslot1 = 0; - entry.augslot2 = 0; - entry.augslot3 = 0; - entry.augslot4 = 0; - entry.augslot5 = 0; - entry.augslot6 = 0; - entry.custom_data = 0; - - return entry; - } - - static Sharedbank GetSharedbankEntry( - const std::vector &sharedbanks, - int sharedbank_id - ) - { - for (auto &sharedbank : sharedbanks) { - if (sharedbank. == sharedbank_id) { - return sharedbank; - } - } - - return NewEntity(); - } - - static Sharedbank FindOne( - int sharedbank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "{} WHERE id = {} LIMIT 1", - BaseSelect(), - sharedbank_id - ) - ); - - auto row = results.begin(); - if (results.RowCount() == 1) { - Sharedbank entry{}; - - entry.acctid = atoi(row[0]); - entry.slotid = atoi(row[1]); - entry.itemid = atoi(row[2]); - entry.charges = atoi(row[3]); - entry.augslot1 = atoi(row[4]); - entry.augslot2 = atoi(row[5]); - entry.augslot3 = atoi(row[6]); - entry.augslot4 = atoi(row[7]); - entry.augslot5 = atoi(row[8]); - entry.augslot6 = atoi(row[9]); - entry.custom_data = row[10]; - - return entry; - } - - return NewEntity(); - } - - static int DeleteOne( - int sharedbank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "DELETE FROM {} WHERE {} = {}", - TableName(), - PrimaryKey(), - sharedbank_id - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int UpdateOne( - Sharedbank sharedbank_entry - ) - { - std::vector update_values; - - auto columns = Columns(); - - update_values.push_back(columns[0] + " = " + std::to_string(sharedbank_entry.acctid)); - update_values.push_back(columns[1] + " = " + std::to_string(sharedbank_entry.slotid)); - update_values.push_back(columns[2] + " = " + std::to_string(sharedbank_entry.itemid)); - update_values.push_back(columns[3] + " = " + std::to_string(sharedbank_entry.charges)); - update_values.push_back(columns[4] + " = " + std::to_string(sharedbank_entry.augslot1)); - update_values.push_back(columns[5] + " = " + std::to_string(sharedbank_entry.augslot2)); - update_values.push_back(columns[6] + " = " + std::to_string(sharedbank_entry.augslot3)); - update_values.push_back(columns[7] + " = " + std::to_string(sharedbank_entry.augslot4)); - update_values.push_back(columns[8] + " = " + std::to_string(sharedbank_entry.augslot5)); - update_values.push_back(columns[9] + " = " + std::to_string(sharedbank_entry.augslot6)); - update_values.push_back(columns[10] + " = '" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "UPDATE {} SET {} WHERE {} = {}", - TableName(), - Strings::Implode(", ", update_values), - PrimaryKey(), - sharedbank_entry. - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static Sharedbank InsertOne( - Sharedbank sharedbank_entry - ) - { - std::vector insert_values; - - insert_values.push_back(std::to_string(sharedbank_entry.acctid)); - insert_values.push_back(std::to_string(sharedbank_entry.slotid)); - insert_values.push_back(std::to_string(sharedbank_entry.itemid)); - insert_values.push_back(std::to_string(sharedbank_entry.charges)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot1)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot2)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot3)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot4)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot5)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot6)); - insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "{} VALUES ({})", - BaseInsert(), - Strings::Implode(",", insert_values) - ) - ); - - if (results.Success()) { - sharedbank_entry.id = results.LastInsertedID(); - return sharedbank_entry; - } - - sharedbank_entry = InstanceListRepository::NewEntity(); - - return sharedbank_entry; - } - - static int InsertMany( - std::vector sharedbank_entries - ) - { - std::vector insert_chunks; - - for (auto &sharedbank_entry: sharedbank_entries) { - std::vector insert_values; - - insert_values.push_back(std::to_string(sharedbank_entry.acctid)); - insert_values.push_back(std::to_string(sharedbank_entry.slotid)); - insert_values.push_back(std::to_string(sharedbank_entry.itemid)); - insert_values.push_back(std::to_string(sharedbank_entry.charges)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot1)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot2)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot3)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot4)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot5)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot6)); - insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - 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) { - Sharedbank entry{}; - - entry.acctid = atoi(row[0]); - entry.slotid = atoi(row[1]); - entry.itemid = atoi(row[2]); - entry.charges = atoi(row[3]); - entry.augslot1 = atoi(row[4]); - entry.augslot2 = atoi(row[5]); - entry.augslot3 = atoi(row[6]); - entry.augslot4 = atoi(row[7]); - entry.augslot5 = atoi(row[8]); - entry.augslot6 = atoi(row[9]); - entry.custom_data = row[10]; - - all_entries.push_back(entry); - } - - return all_entries; - } + /** + * 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 + * + * SharedbankRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedbankRepository::GetWhereNeverExpires() + * SharedbankRepository::GetWhereXAndY() + * SharedbankRepository::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 }; diff --git a/common/ruletypes.h b/common/ruletypes.h index b2de14baa7..090e2f00d0 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -339,7 +339,7 @@ RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to ha RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C") RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame") RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag") -RULE_STRING(World, SupportedClients, "", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") +RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index ac7a3a173a..0bb452cfef 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -48,6 +48,7 @@ #include "repositories/skill_caps_repository.h" #include "repositories/inventory_repository.h" #include "repositories/books_repository.h" +#include "repositories/sharedbank_repository.h" namespace ItemField { @@ -190,242 +191,268 @@ SharedDatabase::MailKeys SharedDatabase::GetMailKey(int character_id) return MailKeys{}; } -bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) +bool SharedDatabase::SaveCursor( + uint32 char_id, + std::list::const_iterator& start, + std::list::const_iterator& end +) { - // Delete cursor items - const std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i " - "AND ((slotid >= 8000 AND slotid <= 8999) " - "OR slotid = %i OR (slotid >= %i AND slotid <= %i) )", - char_id, EQ::invslot::slotCursor, - EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END); - const auto results = QueryDatabase(query); - if (!results.Success()) { - std::cout << "Clearing cursor failed: " << results.ErrorMessage() << std::endl; - return false; - } + const int deleted = InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND (`slot_id` = {} OR `slot_id` BETWEEN {} AND {})", + char_id, + EQ::invslot::slotCursor, + EQ::invbag::CURSOR_BAG_BEGIN, + EQ::invbag::CURSOR_BAG_END + ) + ); - int i = 8000; - for(auto& it = start; it != end; ++it, i++) { - if (i > 8999) { break; } // shouldn't be anything in the queue that indexes this high - const EQ::ItemInstance *inst = *it; - const int16 use_slot = (i == 8000) ? EQ::invslot::slotCursor : i; + int16 i = EQ::invslot::slotCursor; + for (auto& it = start; it != end; ++it, i++) { + // shouldn't be anything in the queue that indexes this high + if (i > EQ::invbag::CURSOR_BAG_END) { + break; + } + + const EQ::ItemInstance* inst = *it; + const int16 use_slot = i == EQ::invslot::slotCursor ? EQ::invslot::slotCursor : i; if (!SaveInventory(char_id, inst, use_slot)) { return false; } - } + } return true; } bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const EQ::ItemInstance* inst) { - // Delete cursor items - const std::string query = StringFormat("SELECT itemid, charges FROM sharedbank " - "WHERE acctid = %d AND slotid = %d", - account_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - //returning true is less harmful in the face of a query error - return true; + if (!inst || !inst->GetItem()) { + return false; } - if (results.RowCount() == 0) - return false; - - auto& row = results.begin(); - - const uint32 id = Strings::ToUnsignedInt(row[0]); - const uint16 charges = Strings::ToUnsignedInt(row[1]); + const auto& l = SharedbankRepository::GetWhere( + *this, + fmt::format( + "`account_id` = {} AND `slot_id` = {} LIMIT 1", + account_id, + slot_id + ) + ); - uint16 expect_charges; + if (l.empty()) { + return false; + } - if(inst->GetCharges() >= 0) - expect_charges = inst->GetCharges(); - else - expect_charges = 0x7FFF; + const auto& e = l.front(); - if(id != inst->GetItem()->ID || charges != expect_charges) - return false; + uint16 expect_charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); - return true; + return e.item_id == inst->GetID() && e.charges == expect_charges; } -bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - - //never save tribute slots: - if (slot_id >= EQ::invslot::TRIBUTE_BEGIN && slot_id <= EQ::invslot::TRIBUTE_END) - return true; - if (slot_id >= EQ::invslot::GUILD_TRIBUTE_BEGIN && slot_id <= EQ::invslot::GUILD_TRIBUTE_END) +bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + // Don't save any Tribute slots + if ( + EQ::ValueWithin(slot_id, EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END) || + EQ::ValueWithin(slot_id, EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END) + ) { return true; + } - if (slot_id >= EQ::invslot::SHARED_BANK_BEGIN && slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { - // Shared bank inventory + if ( + EQ::ValueWithin(slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) { if (!inst) { return DeleteSharedBankSlot(char_id, slot_id); - } - else { + } else { // Needed to clear out bag slots that 'REPLACE' in UpdateSharedBankSlot does not overwrite..otherwise, duplication occurs // (This requires that parent then child items be sent..which should be how they are currently passed) - if (EQ::InventoryProfile::SupportsContainers(slot_id)) + if (EQ::InventoryProfile::SupportsContainers(slot_id)) { DeleteSharedBankSlot(char_id, slot_id); + } + return UpdateSharedBankSlot(char_id, inst, slot_id); } - } - else if (!inst) { // All other inventory + } else if (!inst) { // All other inventory return DeleteInventorySlot(char_id, slot_id); } // Needed to clear out bag slots that 'REPLACE' in UpdateInventorySlot does not overwrite..otherwise, duplication occurs // (This requires that parent then child items be sent..which should be how they are currently passed) - if (EQ::InventoryProfile::SupportsContainers(slot_id)) + if (EQ::InventoryProfile::SupportsContainers(slot_id)) { DeleteInventorySlot(char_id, slot_id); - return UpdateInventorySlot(char_id, inst, slot_id); -} + } -bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - // need to check 'inst' argument for valid pointer + return UpdateInventorySlot(char_id, inst, slot_id); +} - uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 }; - if (inst->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - const EQ::ItemInstance *auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } +bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + if (!inst || !inst->GetItem()) { + return false; } - uint16 charges; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - // Update/Insert item - const std::string query = StringFormat("REPLACE INTO inventory " - "(charid, slotid, itemid, charges, instnodrop, custom_data, color, " - "augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model, guid) " - "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " - "%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", - static_cast(char_id), static_cast(slot_id), static_cast(inst->GetItem()->ID), - static_cast(charges), static_cast(inst->IsAttuned() ? 1 : 0), - inst->GetCustomDataString().c_str(), static_cast(inst->GetColor()), - static_cast(augslot[0]), static_cast(augslot[1]), static_cast(augslot[2]), - static_cast(augslot[3]), static_cast(augslot[4]), static_cast(augslot[5]), static_cast(inst->GetOrnamentationIcon()), - static_cast(inst->GetOrnamentationIDFile()), static_cast(inst->GetOrnamentHeroModel()), inst->GetSerialNumber()); - const auto results = QueryDatabase(query); + std::vector augment_ids = inst->GetAugmentIDs(); + + uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); + + auto e = InventoryRepository::NewEntity(); - // Save bag contents, if slot supports bag contents - if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) + e.character_id = char_id; + e.slot_id = slot_id; + e.item_id = inst->GetID(); + e.charges = charges; + e.color = inst->GetColor(); + e.augment_one = augment_ids[0]; + e.augment_two = augment_ids[1]; + e.augment_three = augment_ids[2]; + e.augment_four = augment_ids[3]; + e.augment_five = augment_ids[4]; + e.augment_six = augment_ids[5]; + e.instnodrop = inst->IsAttuned() ? 1 : 0; + e.custom_data = inst->GetCustomDataString(); + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); + + const int replaced = InventoryRepository::ReplaceOne(*this, e); + + // Save bag contents, if slot supports bag contents + if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) { // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' // messages through attrition (and the modded code in SaveInventory) - for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) { - const EQ::ItemInstance* baginst = inst->GetItem(idx); - SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx)); + for ( + uint8 i = EQ::invbag::SLOT_BEGIN; + i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END; + i++ + ) { + const EQ::ItemInstance* bag_inst = inst->GetItem(i); + SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i)); } + } - if (!results.Success()) { - return false; - } - - return true; + return replaced; } -bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - // need to check 'inst' argument for valid pointer - - uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 }; - if (inst->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - const EQ::ItemInstance *auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } +bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + if (!inst || !inst->GetItem()) { + return false; } -// Update/Insert item + std::vector augment_ids = inst->GetAugmentIDs(); + + uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); + const uint32 account_id = GetAccountIDByChar(char_id); - uint16 charges; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - const std::string query = StringFormat("REPLACE INTO sharedbank " - "(acctid, slotid, itemid, charges, custom_data, " - "augslot1, augslot2, augslot3, augslot4, augslot5, augslot6) " - "VALUES( %lu, %lu, %lu, %lu, '%s', " - "%lu, %lu, %lu, %lu, %lu, %lu)", - static_cast(account_id), static_cast(slot_id), static_cast(inst->GetItem()->ID), - static_cast(charges), inst->GetCustomDataString().c_str(), static_cast(augslot[0]), - static_cast(augslot[1]), static_cast(augslot[2]), static_cast(augslot[3]), static_cast(augslot[4]), - static_cast(augslot[5])); - const auto results = QueryDatabase(query); - // Save bag contents, if slot supports bag contents + auto e = SharedbankRepository::NewEntity(); + + e.account_id = account_id; + e.slot_id = slot_id; + e.item_id = inst->GetID(); + e.charges = charges; + e.color = inst->GetColor(); + e.augment_one = augment_ids[0]; + e.augment_two = augment_ids[1]; + e.augment_three = augment_ids[2]; + e.augment_four = augment_ids[3]; + e.augment_five = augment_ids[4]; + e.augment_six = augment_ids[5]; + e.custom_data = inst->GetCustomDataString(); + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); + + const int replaced = SharedbankRepository::ReplaceOne(*this, e); + + // Save bag contents, if slot supports bag contents if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) { // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' // messages through attrition (and the modded code in SaveInventory) - for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) { - const EQ::ItemInstance* baginst = inst->GetItem(idx); - SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx)); + for ( + uint8 i = EQ::invbag::SLOT_BEGIN; + i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END; + i++ + ) { + const EQ::ItemInstance* bag_inst = inst->GetItem(i); + SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i)); } } - if (!results.Success()) { - return false; - } - - return true; + return replaced; } -bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) { +bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) +{ + const int deleted = InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` = {}", + char_id, + slot_id + ) + ); - // Delete item - std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid = %i", char_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + if (!deleted) { + return false; + } - // Delete bag slots, if need be - if (!EQ::InventoryProfile::SupportsContainers(slot_id)) - return true; + // Delete bag slots, if need be + if (!EQ::InventoryProfile::SupportsContainers(slot_id)) { + return true; + } const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); - query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid >= %i AND slotid < %i", - char_id, base_slot_id, (base_slot_id+10)); - results = QueryDatabase(query); - if (!results.Success()) { - return false; - } - // @merth: need to delete augments here - return true; + return InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` BETWEEN {} AND {}", + char_id, + base_slot_id, + base_slot_id + (EQ::invbag::SLOT_COUNT - 1) + ) + ); } -bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) { +bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) +{ + const uint32 account_id = GetAccountIDByChar(char_id); - // Delete item - const uint32 account_id = GetAccountIDByChar(char_id); - std::string query = StringFormat("DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + const int deleted = SharedbankRepository::DeleteWhere( + *this, + fmt::format( + "`account_id` = {} AND `slot_id` = {}", + account_id, + slot_id + ) + ); - // Delete bag slots, if need be - if (!EQ::InventoryProfile::SupportsContainers(slot_id)) - return true; - - const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); - query = StringFormat("DELETE FROM sharedbank WHERE acctid = %i " - "AND slotid >= %i AND slotid < %i", - account_id, base_slot_id, (base_slot_id+10)); - results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + if (!deleted) { + return false; + } - // @merth: need to delete augments here - return true; + if (!EQ::InventoryProfile::SupportsContainers(slot_id)) { + return true; + } + + const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); + + return SharedbankRepository::DeleteWhere( + *this, + fmt::format( + "`account_id` = {} AND `slotid` BETWEEN {} AND {}", + account_id, + base_slot_id, + base_slot_id + (EQ::invbag::SLOT_COUNT - 1) + ) + ); } @@ -550,96 +577,81 @@ bool SharedDatabase::SetStartingItems( // Retrieve shared bank inventory based on either account or character bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid) { - std::string query; + const uint32 account_id = is_charid ? GetAccountIDByChar(id) : id; - if (is_charid) { - query = fmt::format( - "SELECT sb.slotid, sb.itemid, sb.charges, " - "sb.augslot1, sb.augslot2, sb.augslot3, " - "sb.augslot4, sb.augslot5, sb.augslot6, sb.custom_data " - "FROM sharedbank sb INNER JOIN character_data ch " - "ON ch.account_id = sb.acctid WHERE ch.id = {} ORDER BY sb.slotid", - id - ); - } else { - query = fmt::format( - "SELECT slotid, itemid, charges, " - "augslot1, augslot2, augslot3, " - "augslot4, augslot5, augslot6, custom_data " - "FROM sharedbank WHERE acctid = {} ORDER BY slotid", - id - ); - } - - auto results = QueryDatabase(query); - // If we have no results we still need to return true - if (!results.Success()) { + if (!account_id) { return false; } - for (auto row : results) { - int16 slot_id = static_cast(Strings::ToInt(row[0])); - uint32 item_id = Strings::ToUnsignedInt(row[1]); - const int16 charges = static_cast(Strings::ToInt(row[2])); + const auto& l = SharedbankRepository::GetWhere( + *this, + fmt::format( + "`account_id` = {}", + account_id + ) + ); - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = Strings::ToUnsignedInt(row[3]); - aug[1] = Strings::ToUnsignedInt(row[4]); - aug[2] = Strings::ToUnsignedInt(row[5]); - aug[3] = Strings::ToUnsignedInt(row[6]); - aug[4] = Strings::ToUnsignedInt(row[7]); - aug[5] = Strings::ToUnsignedInt(row[8]); + if (l.empty()) { + return true; + } - const EQ::ItemData *item = GetItem(item_id); + for (const auto& e : l) { + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + e.augment_one, + e.augment_two, + e.augment_three, + e.augment_four, + e.augment_five, + e.augment_six + }; + + const EQ::ItemData* item = GetItem(e.item_id); if (!item) { LogError( - "Warning: [{}] [{}] has an invalid item_id [{}] in inventory slot [{}]", - is_charid ? "charid" : "acctid", + "Warning: {} [{}] has an invalid item_id [{}] in slot_id [{}]", + is_charid ? "character_id" : "account_id", id, - item_id, - slot_id + e.item_id, + e.slot_id ); continue; } - auto inst = CreateBaseItem(item, charges); + EQ::ItemInstance* inst = CreateBaseItem(item, e.charges); if (!inst) { continue; } - if (inst && item->IsClassCommon()) { + if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); } } } - if (inst && row[9]) { - std::string data_str(row[9]); - inst->SetCustomDataString(data_str); + if (!e.custom_data.empty()) { + inst->SetCustomDataString(e.custom_data); } - // theoretically inst can be nullptr ... this would be very bad ... - const int16 put_slot_id = inv->PutItem(slot_id, *inst); + const int16 put_slot_id = inv->PutItem(e.slot_id, *inst); safe_delete(inst); - // Save ptr to item in inventory if (put_slot_id != INVALID_INDEX) { continue; } LogError( - "Warning: Invalid slot_id for item in shared bank inventory: [{}]=[{}], item_id=[{}], slot_id=[{}]", - is_charid ? "charid" : "acctid", + "Warning: Invalid slot_id for item in shared bank inventory for {} [{}] item_id [{}] slot_id [{}]", + is_charid ? "character_id" : "account_id", id, - item_id, - slot_id + e.item_id, + e.slot_id ); if (is_charid) { - SaveInventory(id, nullptr, slot_id); + SaveInventory(id, nullptr, e.slot_id); } } @@ -647,72 +659,77 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is } // Overloaded: Retrieve character inventory based on character id (zone entry) -bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) +bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv) { - if (!char_id || !inv) + if (!char_id || !inv) { return false; + } // Retrieve character inventory - auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id)); + auto results = InventoryRepository::GetWhere( + *this, + fmt::format( + "`character_id` = '{}' ORDER BY `slot_id`", + char_id + ) + ); + if (results.empty()) { LogError("Error loading inventory for char_id {} from the database.", char_id); return false; } - for (auto const &row: results) { + for (auto const& row: results) { if (row.guid != 0) { EQ::ItemInstance::AddGUIDToMap(row.guid); } } - const auto timestamps = GetItemRecastTimestamps(char_id); - auto cv_conflict = false; - const auto pmask = inv->GetLookup()->PossessionsBitmask; - const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; + const auto timestamps = GetItemRecastTimestamps(char_id); + auto cv_conflict = false; + const auto pmask = inv->GetLookup()->PossessionsBitmask; + const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; - std::vector queue{}; - for (auto &row: results) { - const int16 slot_id = row.slotid; - const uint32 item_id = row.itemid; + std::vector queue{ }; + for (auto& row: results) { + const int16 slot_id = row.slot_id; + const uint32 item_id = row.item_id; const uint16 charges = row.charges; const uint32 color = row.color; - const bool instnodrop = row.instnodrop; - const uint32 ornament_icon = row.ornamenticon; - const uint32 ornament_idfile = row.ornamentidfile; + const bool instnodrop = row.instnodrop; + const uint32 ornament_icon = row.ornament_icon; + const uint32 ornament_idfile = row.ornament_idfile; const uint32 ornament_hero_model = row.ornament_hero_model; - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = row.augslot1; - aug[1] = row.augslot2; - aug[2] = row.augslot3; - aug[3] = row.augslot4; - aug[4] = row.augslot5; - aug[5] = row.augslot6; + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + row.augment_one, + row.augment_two, + row.augment_three, + row.augment_four, + row.augment_five, + row.augment_six + }; - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_END, EQ::invslot::POSSESSIONS_BEGIN)) { // Titanium thru UF check if (((static_cast(1) << slot_id) & pmask) == 0) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { // Titanium thru UF check - const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ( - (slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); + const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if (((static_cast(1) << parent_slot) & pmask) == 0) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_END, EQ::invslot::BANK_BEGIN)) { // Titanium check if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { // Titanium check const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if (parent_index >= bank_size) { @@ -721,7 +738,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) } } - auto *item = GetItem(item_id); + auto* item = GetItem(item_id); if (!item) { LogError( "Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", @@ -732,7 +749,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) continue; } - auto *inst = CreateBaseItem(item, charges); + auto* inst = CreateBaseItem(item, charges); if (!inst) { continue; } @@ -745,8 +762,13 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentHeroModel(item->HerosForgeModel); - if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= - EQ::invslot::EQUIPMENT_END)) { + if ( + instnodrop || + ( + inst->GetItem()->Attuneable && + EQ::ValueWithin(slot_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) + ) + ) { inst->SetAttuned(true); } @@ -754,52 +776,37 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) inst->SetColor(color); } - if (charges == 0x7FFF) { + if (charges == std::numeric_limits::max()) { inst->SetCharges(-1); - } - else if (charges == 0 && inst->IsStackable()) { + } else if (charges == 0 && inst->IsStackable()) { // Stackable items need a minimum charge of 1 remain moveable. inst->SetCharges(1); - } - else { + } else { inst->SetCharges(charges); } if (item->RecastDelay) { if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) { inst->SetRecastTimestamp(timestamps.at(item->RecastType)); - } - else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { + } else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { inst->SetRecastTimestamp(timestamps.at(item->ID)); - } - else { + } else { inst->SetRecastTimestamp(0); } } if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); } } } int16 put_slot_id; - if (slot_id >= 8000 && slot_id <= 8999) { + if (slot_id > EQ::invbag::TRADE_BAGS_END) { put_slot_id = inv->PushCursor(*inst); - } - else if (slot_id >= 3111 && slot_id <= 3179) { - // Admins: please report any occurrences of this error - LogError( - "Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...", - char_id, - item_id, - slot_id - ); - put_slot_id = inv->PushCursor(*inst); - } - else { + } else { put_slot_id = inv->PutItem(slot_id, *inst); } @@ -811,7 +818,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) // Save ptr to item in inventory if (put_slot_id == INVALID_INDEX) { LogError( - "Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]", + "Warning: Invalid slot_id for item in inventory for character_id [{}] item_id [{}] slot_id [{}]", char_id, item_id, slot_id @@ -820,7 +827,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) } if (cv_conflict) { - const std::string &char_name = GetCharName(char_id); + const std::string& char_name = GetCharName(char_id); LogError( "ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", char_name, @@ -840,94 +847,6 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) return GetSharedBank(char_id, inv, true); } -// Overloaded: Retrieve character inventory based on account_id and character name (char select) -bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv) // deprecated -{ - // Retrieve character inventory - const std::string query = - StringFormat("SELECT slotid, itemid, charges, color, augslot1, " - "augslot2, augslot3, augslot4, augslot5, augslot6, instnodrop, custom_data, ornamenticon, " - "ornamentidfile, ornament_hero_model " - "FROM inventory INNER JOIN character_data ch " - "ON ch.id = charid WHERE ch.name = '%s' AND ch.account_id = %i ORDER BY slotid", - name, account_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogError("If you got an error related to the 'instnodrop' field, run the " - "following SQL Queries:\nalter table inventory add instnodrop " - "tinyint(1) unsigned default 0 not null;\n"); - return false; - } - - for (auto& row = results.begin(); row != results.end(); ++row) { - int16 slot_id = Strings::ToInt(row[0]); - uint32 item_id = Strings::ToUnsignedInt(row[1]); - const int8 charges = Strings::ToInt(row[2]); - const uint32 color = Strings::ToUnsignedInt(row[3]); - - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = Strings::ToUnsignedInt(row[4]); - aug[1] = Strings::ToUnsignedInt(row[5]); - aug[2] = Strings::ToUnsignedInt(row[6]); - aug[3] = Strings::ToUnsignedInt(row[7]); - aug[4] = Strings::ToUnsignedInt(row[8]); - aug[5] = Strings::ToUnsignedInt(row[9]); - - const bool instnodrop = (row[10] && static_cast(Strings::ToUnsignedInt(row[10]))); - const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]); - const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]); - uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]); - - const EQ::ItemData *item = GetItem(item_id); - if (!item) - continue; - - EQ::ItemInstance *inst = CreateBaseItem(item, charges); - - if (inst == nullptr) - continue; - - inst->SetAttuned(instnodrop); - - if (row[11]) { - std::string data_str(row[11]); - inst->SetCustomDataString(data_str); - } - - inst->SetOrnamentIcon(ornament_icon); - inst->SetOrnamentationIDFile(ornament_idfile); - inst->SetOrnamentHeroModel(item->HerosForgeModel); - - if (color > 0) - inst->SetColor(color); - - inst->SetCharges(charges); - - if (item->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) - inst->PutAugment(this, i, aug[i]); - } - } - - int16 put_slot_id; - if (slot_id >= 8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) - LogError("Warning: Invalid slot_id for item in inventory: name={}, acctid={}, item_id={}, slot_id={}", - name, account_id, item_id, slot_id); - } - - // Retrieve shared inventory - return GetSharedBank(account_id, inv, false); -} - std::map SharedDatabase::GetItemRecastTimestamps(uint32 char_id) { std::map timers; @@ -1279,7 +1198,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ // Bag item.BagSize = static_cast(Strings::ToUnsignedInt(row[ItemField::bagsize])); - item.BagSlots = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, 10)); // Will need to be changed from std::min to just use database value when bag slots are increased + item.BagSlots = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, static_cast(EQ::invbag::SLOT_COUNT))); item.BagType = static_cast(Strings::ToUnsignedInt(row[ItemField::bagtype])); item.BagWR = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagwr]), 0, 100)); diff --git a/common/shareddb.h b/common/shareddb.h index 67c684206b..3b28ad6b48 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -104,7 +104,6 @@ class SharedDatabase : public Database { int32 GetSharedPlatinum(uint32 account_id); bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); bool GetInventory(uint32 char_id, EQ::InventoryProfile *inv); - bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated std::map GetItemRecastTimestamps(uint32 char_id); uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type); void ClearOldRecastTimestamps(uint32 char_id); diff --git a/common/version.h b/common/version.h index c615c7c854..107add4b4b 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9285 +#define CURRENT_BINARY_DATABASE_VERSION 9288 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index 492bf3c969..396b1e8756 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -142,7 +142,6 @@ "guild_bank", "inventory_versions", "raid_leaders", - "sharedbank", "trader_audit", "eqtime", "db_version", diff --git a/world/client.cpp b/world/client.cpp index a6f5cfb5f4..19b894d4f2 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -2369,21 +2369,26 @@ bool Client::StoreCharacter( auto e = InventoryRepository::NewEntity(); - e.charid = character_id; + e.character_id = character_id; for (int16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invbag::BANK_BAGS_END;) { const auto inst = p_inventory_profile->GetItem(slot_id); if (inst) { - e.slotid = slot_id; - e.itemid = inst->GetItem()->ID; - e.charges = inst->GetCharges(); - e.color = inst->GetColor(); - e.augslot1 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN); - e.augslot2 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1); - e.augslot3 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2); - e.augslot4 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3); - e.augslot5 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4); - e.augslot6 = inst->GetAugmentItemID(EQ::invaug::SOCKET_END); + e.slot_id = slot_id; + e.item_id = inst->GetItem()->ID; + e.charges = inst->GetCharges(); + e.color = inst->GetColor(); + e.augment_one = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN); + e.augment_two = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1); + e.augment_three = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2); + e.augment_four = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3); + e.augment_five = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4); + e.augment_six = inst->GetAugmentItemID(EQ::invaug::SOCKET_END); + e.instnodrop = inst->IsAttuned() ? 1 : 0; + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); v.emplace_back(e); } diff --git a/zone/client.cpp b/zone/client.cpp index d0a8d97b0b..4bbee4edb6 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9124,7 +9124,7 @@ void Client::SetPrimaryWeaponOrnamentation(uint32 model_id) auto l = InventoryRepository::GetWhere( database, fmt::format( - "`charid` = {} AND `slotid` = {}", + "`character_id` = {} AND `slot_id` = {}", character_id, EQ::invslot::slotPrimary ) @@ -9136,7 +9136,7 @@ void Client::SetPrimaryWeaponOrnamentation(uint32 model_id) auto e = l.front(); - e.ornamentidfile = model_id; + e.ornament_idfile = model_id; const int updated = InventoryRepository::UpdateOne(database, e); @@ -9157,7 +9157,7 @@ void Client::SetSecondaryWeaponOrnamentation(uint32 model_id) auto l = InventoryRepository::GetWhere( database, fmt::format( - "`charid` = {} AND `slotid` = {}", + "`character_id` = {} AND `slot_id` = {}", character_id, EQ::invslot::slotSecondary ) @@ -9169,7 +9169,7 @@ void Client::SetSecondaryWeaponOrnamentation(uint32 model_id) auto e = l.front(); - e.ornamentidfile = model_id; + e.ornament_idfile = model_id; const int updated = InventoryRepository::UpdateOne(database, e); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 14ee222f34..aba25030b0 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10769,8 +10769,7 @@ void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) { - if (!CharacterID()) - { + if (!CharacterID()) { return; } @@ -10779,60 +10778,35 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) return; } - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if (mi->from_slot != mi->to_slot && (mi->from_slot <= EQ::invslot::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - const EQ::ItemInstance *itm_from = GetInv().GetItem(mi->from_slot); - const EQ::ItemInstance *itm_to = GetInv().GetItem(mi->to_slot); - auto message = fmt::format("Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.", + MoveItem_Struct* mi = (MoveItem_Struct*) app->pBuffer; + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) { + if (mi->from_slot != mi->to_slot && (mi->from_slot <= EQ::invslot::GENERAL_END || mi->from_slot > 39) && + IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { + const EQ::ItemInstance* itm_from = GetInv().GetItem(mi->from_slot); + const EQ::ItemInstance* itm_to = GetInv().GetItem(mi->to_slot); + auto message = fmt::format( + "Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.", mi->from_slot, itm_from ? itm_from->GetID() : 0, mi->to_slot, itm_to ? itm_to->GetID() : 0, - casting_spell_id); - RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message}); + casting_spell_id + ); + RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{ .message = message }); Kick("Inventory desync"); // Kick client to prevent client and server from getting out-of-sync inventory slots return; } } - // Illegal bagslot usage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if (mi->from_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && mi->from_slot <= EQ::invbag::CURSOR_BAG_END) { - if (mi->from_slot >= EQ::invbag::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if (!m_inv[from_parent]) { mi_hack = true; } - else if (!m_inv[from_parent]->IsClassBag()) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi->to_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && mi->to_slot <= EQ::invbag::CURSOR_BAG_END) { - if (mi->to_slot >= EQ::invbag::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if (!m_inv[to_parent]) { mi_hack = true; } - else if (!m_inv[to_parent]->IsClassBag()) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi_hack) { Message(Chat::Yellow, "Caution: Illegal use of inaccessible bag slots!"); } - if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); bool error = false; InterrogateInventory(this, false, true, false, error, false); - if (error) + if (error) { InterrogateInventory(this, true, false, true, error); + } } - - return; } void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c2e20fe5b6..b8f3ace3eb 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -770,11 +770,12 @@ void Client::BulkSendInventoryItems() EQ::OutBuffer ob; EQ::OutBuffer::pos_type last_pos = ob.tellp(); - // Possessions items - for (int16 slot_id = EQ::invslot::POSSESSIONS_BEGIN; slot_id <= EQ::invslot::POSSESSIONS_END; slot_id++) { + // Equipment items + for (int16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; slot_id++) { const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) + if (!inst) { continue; + } inst->Serialize(ob, slot_id); @@ -784,7 +785,7 @@ void Client::BulkSendInventoryItems() last_pos = ob.tellp(); } - if (!RuleB(Inventory, LazyLoadBank)) { + if (!RuleB(Inventory, LazyLoadBank)) { // Bank items for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { const EQ::ItemInstance* inst = m_inv[slot_id]; @@ -823,12 +824,6 @@ void Client::BulkSendInventoryItems() void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { const EQ::ItemData* handy_item = nullptr; - - uint32 merchant_slots = 80; //The max number of items passed in the transaction. - if (m_ClientVersionBit & EQ::versions::maskRoFAndLater) { // RoF+ can send 200 items - merchant_slots = 200; - } - const EQ::ItemData *item = nullptr; auto merchant_list = zone->merchanttable[merchant_id]; auto npc = entity_list.GetMobByNpcTypeID(npcid); @@ -840,6 +835,8 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { } } + const int16 merchant_slots = (m_ClientVersionBit & EQ::versions::maskRoFAndLater) ? EQ::invtype::MERCHANT_SIZE : 80; + auto temporary_merchant_list = zone->tmpmerchanttable[npcid]; uint32 slot_id = 1; uint8 handy_chance = 0; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e54dd2e635..560f4a799e 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -845,7 +845,7 @@ LootItem *Corpse::GetItem(uint16 lootslot, LootItem **bag_item_data) // convert above code to for loop for (const auto &item: m_item_list) { - if (item->equip_slot >= bagstart && item->equip_slot < bagstart + 10) { + if (item->equip_slot >= bagstart && item->equip_slot < bagstart + EQ::invbag::SLOT_COUNT) { bag_item_data[item->equip_slot - bagstart] = item; } } @@ -1472,7 +1472,7 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app) const EQ::ItemData *item = nullptr; EQ::ItemInstance *inst = nullptr; - LootItem *item_data = nullptr, *bag_item_data[10] = {}; + LootItem *item_data = nullptr, *bag_item_data[EQ::invbag::SLOT_COUNT] = {}; memset(bag_item_data, 0, sizeof(bag_item_data)); if (GetPlayerKillItem() > 1) { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index d18796a4e7..738a7eb064 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -278,25 +278,25 @@ int Perl__getinventoryslotid(std::string identifier) else if (identifier == "generalbags.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN; else if (identifier == "generalbags.end") result = EQ::invbag::GENERAL_BAGS_END; else if (identifier == "general1bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN; - else if (identifier == "general1bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 9; - else if (identifier == "general2bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 10; - else if (identifier == "general2bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 19; - else if (identifier == "general3bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 20; - else if (identifier == "general3bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 29; - else if (identifier == "general4bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 30; - else if (identifier == "general4bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 39; - else if (identifier == "general5bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 40; - else if (identifier == "general5bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 49; - else if (identifier == "general6bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 50; - else if (identifier == "general6bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 59; - else if (identifier == "general7bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 60; - else if (identifier == "general7bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 69; - else if (identifier == "general8bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 70; - else if (identifier == "general8bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 79; - else if (identifier == "general9bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 80; - else if (identifier == "general9bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 89; - else if (identifier == "general10bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 90; - else if (identifier == "general10bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 99; + else if (identifier == "general1bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT - 1); + else if (identifier == "general2bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + EQ::invbag::SLOT_COUNT; + else if (identifier == "general2bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 2) - 1); + else if (identifier == "general3bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 2); + else if (identifier == "general3bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 3) - 1); + else if (identifier == "general4bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 3); + else if (identifier == "general4bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 4) - 1); + else if (identifier == "general5bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 4); + else if (identifier == "general5bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 5) - 1); + else if (identifier == "general6bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 5); + else if (identifier == "general6bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 6) - 1); + else if (identifier == "general7bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 6); + else if (identifier == "general7bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 7) - 1); + else if (identifier == "general8bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 7); + else if (identifier == "general8bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 8) - 1); + else if (identifier == "general9bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 8); + else if (identifier == "general9bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 9) - 1); + else if (identifier == "general10bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 9); + else if (identifier == "general10bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 10) - 1); else if (identifier == "cursorbag.begin") result = EQ::invbag::CURSOR_BAG_BEGIN; else if (identifier == "cursorbag.end") result = EQ::invbag::CURSOR_BAG_END; else if (identifier == "bank.begin") result = EQ::invslot::BANK_BEGIN; diff --git a/zone/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index c2d22b2d96..2078f2bdd6 100644 --- a/zone/gm_commands/show/inventory.cpp +++ b/zone/gm_commands/show/inventory.cpp @@ -310,7 +310,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), item_data->ID, linker.GenerateLink(), ( @@ -339,7 +339,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} (Augment Slot {}) | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), augment_index, linker.GenerateLink(), item_data->ID, @@ -375,7 +375,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), sub_index, linker.GenerateLink(), item_data->ID, @@ -407,7 +407,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), sub_index, augment_index, linker.GenerateLink(), diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 30f4c31daf..6afeeff31e 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1909,13 +1909,20 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } //verify shared bank transactions in the database - if (src_inst && src_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && src_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { + if ( + src_inst && + ( + EQ::ValueWithin(src_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(src_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) + ) { if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { LogError("Player [{}] on account [{}] was found exploiting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(dst_slot_id,0,true); return(false); } - if (src_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && src_slot_id <= EQ::invslot::SHARED_BANK_END && src_inst->IsClassBag()){ + + if (EQ::ValueWithin(src_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) && src_inst->IsClassBag()){ for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx <= EQ::invbag::SLOT_END; idx++) { const EQ::ItemInstance* baginst = src_inst->GetItem(idx); if (baginst && !database.VerifyInventory(account_id, EQ::InventoryProfile::CalcSlotId(src_slot_id, idx), baginst)){ @@ -1924,13 +1931,21 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } } } - if (dst_inst && dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { + + if ( + dst_inst && + ( + EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(dst_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) + ) { if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { LogError("Player [{}] on account [{}] was found exploting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(src_slot_id,0,true); return(false); } - if (dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invslot::SHARED_BANK_END && dst_inst->IsClassBag()){ + + if (EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) && dst_inst->IsClassBag()){ for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx <= EQ::invbag::SLOT_END; idx++) { const EQ::ItemInstance* baginst = dst_inst->GetItem(idx); if (baginst && !database.VerifyInventory(account_id, EQ::InventoryProfile::CalcSlotId(dst_slot_id, idx), baginst)){ @@ -1943,10 +1958,20 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Check for No Drop Hacks Mob* with = trade->With(); - if (((with && with->IsClient() && !with->CastToClient()->IsBecomeNPC() && dst_slot_id >= EQ::invslot::TRADE_BEGIN && dst_slot_id <= EQ::invslot::TRADE_END) || - (dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END)) - && GetInv().CheckNoDrop(src_slot_id) - && !CanTradeFVNoDropItem()) { + if ( + ( + ( + with && + with->IsClient() && + !with->CastToClient()->IsBecomeNPC() && + EQ::ValueWithin(dst_slot_id, EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END) + ) || + EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(dst_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) && + GetInv().CheckNoDrop(src_slot_id) && + !CanTradeFVNoDropItem() + ) { auto ndh_inst = m_inv[src_slot_id]; std::string ndh_item_data; if (ndh_inst == nullptr) { @@ -3641,7 +3666,7 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool if (cursor_itr == m_inv.cursor_cbegin()) continue; - instmap[8000 + limbo] = *cursor_itr; + instmap[EQ::invbag::CURSOR_BAG_BEGIN + limbo] = *cursor_itr; } // call InterrogateInventory_ for error check @@ -3744,11 +3769,12 @@ bool Client::InterrogateInventory_error(int16 head, int16 index, const EQ::ItemI // very basic error checking - can be elaborated upon if more in-depth testing is needed... 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)) { + EQ::ValueWithin(head, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) || + EQ::ValueWithin(head, EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END) || + EQ::ValueWithin(head, EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END) || + EQ::ValueWithin(head, EQ::invslot::WORLD_BEGIN, EQ::invslot::WORLD_END) || + EQ::ValueWithin(head, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) + ) { switch (depth) { case 0: // requirement: inst is extant diff --git a/zone/zonedb.h b/zone/zonedb.h index ca0447758c..28c224dbb3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -208,10 +208,10 @@ struct ZoneSpellsBlocked { }; struct TraderCharges_Struct { - uint32 ItemID[80]; - int32 SerialNumber[80]; - uint32 ItemCost[80]; - int32 Charges[80]; + uint32 ItemID[EQ::invtype::BAZAAR_SIZE]; + int32 SerialNumber[EQ::invtype::BAZAAR_SIZE]; + uint32 ItemCost[EQ::invtype::BAZAAR_SIZE]; + int32 Charges[EQ::invtype::BAZAAR_SIZE]; }; const int MaxMercStanceID = 9; From 1da7901029d3cc477f2c80f225690b9a1b5eece5 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:26:13 -0400 Subject: [PATCH 02/73] Update worlddb.cpp --- world/worlddb.cpp | 154 +++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 89 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 986843da5e..6a82eba4a5 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -26,6 +26,7 @@ #include #include "sof_char_create_data.h" #include "../common/repositories/character_instance_safereturns_repository.h" +#include "../common/repositories/inventory_repository.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/zone_store.h" @@ -857,115 +858,90 @@ bool WorldDatabase::LoadCharacterCreateCombos() // this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv) { - if (!account_id || !name || !inv) + if (!account_id || !name || !inv) { return false; + } - std::string query = StringFormat( - "SELECT" - " slotid," - " itemid," - " charges," - " color," - " augslot1," - " augslot2," - " augslot3," - " augslot4," - " augslot5," - " augslot6," - " instnodrop," - " custom_data," - " ornamenticon," - " ornamentidfile," - " ornament_hero_model " - "FROM" - " inventory " - "INNER JOIN" - " character_data ch " - "ON" - " ch.id = charid " - "WHERE" - " ch.name = '%s' " - "AND" - " ch.account_id = %i " - "AND" - " slotid >= %i " - "AND" - " slotid <= %i", - name, - account_id, - EQ::invslot::slotHead, - EQ::invslot::slotFeet - ); - auto results = QueryDatabase(query); - if (!results.Success()) + const uint32 character_id = GetCharacterID(name); + + if (!character_id) { return false; + } - for (auto row = results.begin(); row != results.end(); ++row) { - int16 slot_id = Strings::ToInt(row[0]); - - switch (slot_id) { - case EQ::invslot::slotFace: - case EQ::invslot::slotEar2: - case EQ::invslot::slotNeck: - case EQ::invslot::slotShoulders: - case EQ::invslot::slotBack: - case EQ::invslot::slotFinger1: - case EQ::invslot::slotFinger2: - continue; - default: - break; + const auto& l = InventoryRepository::GetWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` BETWEEN {} AND {}", + character_id, + EQ::invslot::slotHead, + EQ::invslot::slotFeet + ) + ); + + if (l.empty()) { + return true; + } + + for (const auto& e : l) { + switch (e.slot_id) { + case EQ::invslot::slotFace: + case EQ::invslot::slotEar2: + case EQ::invslot::slotNeck: + case EQ::invslot::slotShoulders: + case EQ::invslot::slotBack: + case EQ::invslot::slotFinger1: + case EQ::invslot::slotFinger2: + continue; + default: + break; } - uint32 item_id = Strings::ToInt(row[1]); - int8 charges = Strings::ToInt(row[2]); - uint32 color = Strings::ToUnsignedInt(row[3]); - - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = (uint32)Strings::ToInt(row[4]); - aug[1] = (uint32)Strings::ToInt(row[5]); - aug[2] = (uint32)Strings::ToInt(row[6]); - aug[3] = (uint32)Strings::ToInt(row[7]); - aug[4] = (uint32)Strings::ToInt(row[8]); - aug[5] = (uint32)Strings::ToInt(row[9]); - - bool instnodrop = ((row[10] && (uint16)Strings::ToInt(row[10])) ? true : false); - uint32 ornament_icon = (uint32)Strings::ToUnsignedInt(row[12]); - uint32 ornament_idfile = (uint32)Strings::ToUnsignedInt(row[13]); - uint32 ornament_hero_model = (uint32)Strings::ToUnsignedInt(row[14]); - - const EQ::ItemData *item = content_db.GetItem(item_id); - if (!item) - continue; - EQ::ItemInstance *inst = content_db.CreateBaseItem(item, charges); + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + e.augment_one, + e.augment_two, + e.augment_three, + e.augment_four, + e.augment_five, + e.augment_six + }; - if (inst == nullptr) + const EQ::ItemData* item = content_db.GetItem(e.item_id); + if (!item) { continue; + } - inst->SetAttuned(instnodrop); + EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges); - if (row[11]) { - std::string data_str(row[11]); - inst->SetCustomDataString(data_str); + if (!inst) { + continue; } - inst->SetOrnamentIcon(ornament_icon); - inst->SetOrnamentationIDFile(ornament_idfile); - inst->SetOrnamentHeroModel(item->HerosForgeModel); - - if (color > 0) - inst->SetColor(color); + inst->SetCharges(e.charges); - inst->SetCharges(charges); + if (e.color > 0) { + inst->SetColor(e.color); + } if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); + } } } - inv->PutItem(slot_id, *inst); + inst->SetAttuned(e.instnodrop); + + if (!e.custom_data.empty()) { + inst->SetCustomDataString(e.custom_data); + } + + inst->SetOrnamentIcon(e.ornament_icon); + inst->SetOrnamentationIDFile(e.ornament_idfile); + inst->SetOrnamentHeroModel(e.ornament_hero_model); + + inv->PutItem(e.slot_id, *inst); safe_delete(inst); } From 77de74a0893cacfe5ba5be0790925a84196176c0 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:45:32 -0400 Subject: [PATCH 03/73] Update shareddb.cpp --- common/shareddb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 0bb452cfef..a052c86520 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -710,7 +710,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv) row.augment_six }; - if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_END, EQ::invslot::POSSESSIONS_BEGIN)) { + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { // Titanium thru UF check if (((static_cast(1) << slot_id) & pmask) == 0) { cv_conflict = true; @@ -723,7 +723,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv) cv_conflict = true; continue; } - } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_END, EQ::invslot::BANK_BEGIN)) { + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { // Titanium check if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) { cv_conflict = true; @@ -804,7 +804,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv) } int16 put_slot_id; - if (slot_id > EQ::invbag::TRADE_BAGS_END) { + if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) || slot_id == EQ::invslot::slotCursor) { put_slot_id = inv->PushCursor(*inst); } else { put_slot_id = inv->PutItem(slot_id, *inst); From 95687b57e904a2a5abe11154740e7a34f8042d2f Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 18:12:07 -0400 Subject: [PATCH 04/73] Cleanup --- common/inventory_profile.cpp | 1003 +++++++++++++++++----------------- common/inventory_profile.h | 9 - zone/client_packet.cpp | 5 - 3 files changed, 515 insertions(+), 502 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 5f1dd6b681..b200388aa9 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -28,6 +28,7 @@ #include "strings.h" #include "../common/light_source.h" +#include "data_verification.h" //#include @@ -44,6 +45,7 @@ ItemInstQueue::~ItemInstQueue() for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) { safe_delete(*iter); } + m_list.clear(); } @@ -62,8 +64,9 @@ void ItemInstQueue::push_front(EQ::ItemInstance* inst) // Remove item from front of queue EQ::ItemInstance* ItemInstQueue::pop() { - if (m_list.empty()) + if (m_list.empty()) { return nullptr; + } EQ::ItemInstance* inst = m_list.front(); m_list.pop_front(); @@ -73,8 +76,9 @@ EQ::ItemInstance* ItemInstQueue::pop() // Remove item from back of queue EQ::ItemInstance* ItemInstQueue::pop_back() { - if (m_list.empty()) + if (m_list.empty()) { return nullptr; + } EQ::ItemInstance* inst = m_list.back(); m_list.pop_back(); @@ -87,7 +91,6 @@ EQ::ItemInstance* ItemInstQueue::peek_front() const return (m_list.empty()) ? nullptr : m_list.front(); } - // // class EQ::InventoryProfile // @@ -96,26 +99,31 @@ EQ::InventoryProfile::~InventoryProfile() for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { safe_delete(iter->second); } + m_worn.clear(); for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { safe_delete(iter->second); } + m_inv.clear(); for (auto iter = m_bank.begin(); iter != m_bank.end(); ++iter) { safe_delete(iter->second); } + m_bank.clear(); for (auto iter = m_shbank.begin(); iter != m_shbank.end(); ++iter) { safe_delete(iter->second); } + m_shbank.clear(); for (auto iter = m_trade.begin(); iter != m_trade.end(); ++iter) { safe_delete(iter->second); } + m_trade.clear(); } @@ -136,6 +144,7 @@ void EQ::InventoryProfile::CleanDirty() { delete (*iter); ++iter; } + dirty_inst.clear(); } @@ -157,59 +166,44 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id) const } // Non bag slots - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { result = _GetItem(m_trade, slot_id); - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { - // Shared Bank slots + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { result = _GetItem(m_shbank, slot_id); - } - else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { - // Bank slots + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { result = _GetItem(m_bank, slot_id); - } - else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { - // Personal inventory slots + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { 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::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END)) { - // Equippable slots (on body) + } else if ( + EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END) || + EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END) || + EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, invslot::GUILD_TRIBUTE_END) + ) { result = _GetItem(m_worn, slot_id); } // Inner bag slots - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { - // Trade bag slots + else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { ItemInstance* inst = _GetItem(m_trade, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { - // Shared Bank bag slots + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { ItemInstance* inst = _GetItem(m_shbank, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { - // Bank bag slots + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { ItemInstance* inst = _GetItem(m_bank, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { - // Cursor bag slots + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { ItemInstance* inst = m_cursor.peek_front(); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { - // Personal inventory bag slots + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { ItemInstance* inst = _GetItem(m_inv, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); @@ -228,25 +222,27 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id, uint8 bagidx) con // Put an item into specified slot int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) { - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) == 0) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } + } else if (EQ::ValueWithin(slot_id, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); - if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { - if ((slot_id - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if ((slot_id - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { + } + } else if (EQ::ValueWithin(slot_id, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; - if (temp_slot >= m_lookup->InventoryTypeSize.Bank) + if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { return EQ::invslot::SLOT_INVALID; + } } + // Clean up item already in slot (if exists) DeleteItem(slot_id); @@ -281,26 +277,23 @@ bool EQ::InventoryProfile::SwapItem( ) { fail_state = swapInvalid; - if (source_slot <= EQ::invslot::POSSESSIONS_END && source_slot >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64) 1 << source_slot) & m_lookup->PossessionsBitmask) == 0) { + if (EQ::ValueWithin(source_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64)1 << source_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invbag::GENERAL_BAGS_END && source_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((source_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invslot::BANK_END && source_slot >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { if ((source_slot - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invbag::BANK_BAGS_END && source_slot >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (source_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; @@ -308,26 +301,23 @@ bool EQ::InventoryProfile::SwapItem( } } - if (destination_slot <= EQ::invslot::POSSESSIONS_END && destination_slot >= EQ::invslot::POSSESSIONS_BEGIN) { + if (EQ::ValueWithin(destination_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { if ((((uint64)1 << destination_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invbag::GENERAL_BAGS_END && destination_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((destination_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invslot::BANK_END && destination_slot >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { if ((destination_slot - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invbag::BANK_BAGS_END && destination_slot >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (destination_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; @@ -335,6 +325,7 @@ bool EQ::InventoryProfile::SwapItem( } } + // Temp holding areas for source and destination ItemInstance *source_item_instance = GetItem(source_slot); ItemInstance *destination_item_instance = GetItem(destination_slot); @@ -344,20 +335,24 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } - if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { + + if (EQ::ValueWithin(destination_slot, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { auto source_item = source_item_instance->GetItem(); if (!source_item) { fail_state = swapNullData; return false; } + if (race_id && class_id && !source_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } + if (deity_id && source_item->Deity && !(Deity::GetBitmask(deity_id) & source_item->Deity)) { fail_state = swapDeity; return false; } + if (level && source_item->ReqLevel && level < source_item->ReqLevel) { fail_state = swapLevel; return false; @@ -370,20 +365,24 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } - if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { + + if (EQ::ValueWithin(source_slot, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { auto destination_item = destination_item_instance->GetItem(); if (!destination_item) { fail_state = swapNullData; return false; } + if (race_id && class_id && !destination_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } + if (deity_id && destination_item->Deity && !(Deity::GetBitmask(deity_id) & destination_item->Deity)) { fail_state = swapDeity; return false; } + if (level && destination_item->ReqLevel && level < destination_item->ReqLevel) { fail_state = swapLevel; return false; @@ -439,10 +438,8 @@ bool EQ::InventoryProfile::DeleteItem(int16 slot_id, int16 quantity) { bool EQ::InventoryProfile::CheckNoDrop(int16 slot_id, bool recurse) { ItemInstance* inst = GetItem(slot_id); - if (!inst) - return false; - return (!inst->IsDroppable(recurse)); + return inst ? !inst->IsDroppable(recurse) : false; } // Remove item from bucket without memory delete @@ -453,40 +450,32 @@ EQ::ItemInstance* EQ::InventoryProfile::PopItem(int16 slot_id) if (slot_id == invslot::slotCursor) { p = m_cursor.pop(); - } - else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + } else if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { p = m_worn[slot_id]; m_worn.erase(slot_id); - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { p = m_inv[slot_id]; m_inv.erase(slot_id); - } - else if (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END)) { p = m_worn[slot_id]; m_worn.erase(slot_id); - } - else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, 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) { + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { p = m_bank[slot_id]; m_bank.erase(slot_id); - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { p = m_shbank[slot_id]; m_shbank.erase(slot_id); - } - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { p = m_trade[slot_id]; m_trade.erase(slot_id); - } - else { - // Is slot inside bag? - ItemInstance* baginst = GetItem(InventoryProfile::CalcSlotId(slot_id)); - if (baginst != nullptr && baginst->IsClassBag()) { - p = baginst->PopItem(InventoryProfile::CalcBagIdx(slot_id)); + } else { + // Is slot inside bag? + ItemInstance* bag_inst = GetItem(InventoryProfile::CalcSlotId(slot_id)); + if (bag_inst && bag_inst->IsClassBag()) { + p = bag_inst->PopItem(InventoryProfile::CalcBagIdx(slot_id)); } } @@ -494,43 +483,49 @@ EQ::ItemInstance* EQ::InventoryProfile::PopItem(int16 slot_id) return p; } -bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quantity) { - +bool EQ::InventoryProfile::HasSpaceForItem(const ItemData* ItemToTry, int16 Quantity) +{ if (ItemToTry->Stackable) { - for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { - if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) { continue; + } - ItemInstance* InvItem = GetItem(i); - - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + ItemInstance* inv_item = GetItem(i); - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + if ( + inv_item && + inv_item->GetItem()->ID == ItemToTry->ID && + inv_item->GetCharges() < inv_item->GetItem()->StackSize + ) { + int charges_left = inv_item->GetItem()->StackSize - inv_item->GetCharges(); - if (Quantity <= ChargeSlotsLeft) + if (Quantity <= charges_left) { return true; + } - Quantity -= ChargeSlotsLeft; - + Quantity -= charges_left; } - if (InvItem && InvItem->IsClassBag()) { - - int16 BaseSlotID = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); - uint8 BagSize = InvItem->GetItem()->BagSlots; - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlot < BagSize; BagSlot++) { - InvItem = GetItem(BaseSlotID + BagSlot); + if (inv_item && inv_item->IsClassBag()) { + int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + uint8 bag_slots = inv_item->GetItem()->BagSlots; - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && - (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_slots; bag_slot++) { + inv_item = GetItem(base_slot_id + bag_slot); - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + if ( + inv_item && + inv_item->GetItem()->ID == ItemToTry->ID && + inv_item->GetCharges() < inv_item->GetItem()->StackSize + ) { + int charges_left = inv_item->GetItem()->StackSize - inv_item->GetCharges(); - if (Quantity <= ChargeSlotsLeft) + if (Quantity <= charges_left) { return true; + } - Quantity -= ChargeSlotsLeft; + Quantity -= charges_left; } } } @@ -538,51 +533,46 @@ bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quan } for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { - if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) { continue; + } - ItemInstance* InvItem = GetItem(i); - - if (!InvItem) { + ItemInstance* inv_item = GetItem(i); + if (!inv_item) { if (!ItemToTry->Stackable) { - - if (Quantity == 1) + if (Quantity == 1) { return true; - else + } else { Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) + } + } else { + if (Quantity <= ItemToTry->StackSize) { return true; - else + } else { Quantity -= ItemToTry->StackSize; + } } + } else if (inv_item->IsClassBag() && CanItemFitInContainer(ItemToTry, inv_item->GetItem())) { + int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + uint8 bag_slots = inv_item->GetItem()->BagSlots; - } - else if (InvItem->IsClassBag() && CanItemFitInContainer(ItemToTry, InvItem->GetItem())) { - - int16 BaseSlotID = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); - - uint8 BagSize = InvItem->GetItem()->BagSlots; - - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlot < BagSize; BagSlot++) { - - InvItem = GetItem(BaseSlotID + BagSlot); + for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_slots; bag_slot++) { + inv_item = GetItem(base_slot_id + bag_slot); - if (!InvItem) { + if (!inv_item) { if (!ItemToTry->Stackable) { - - if (Quantity == 1) + if (Quantity == 1) { return true; - else + } else { Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) + } + } else { + if (Quantity <= ItemToTry->StackSize) { return true; - else + } else { Quantity -= ItemToTry->StackSize; + } } } } @@ -590,7 +580,6 @@ bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quan } return false; - } // Checks that user has at least 'quantity' number of items in a given inventory slot @@ -670,40 +659,46 @@ int16 EQ::InventoryProfile::HasItem(uint32 item_id, uint8 quantity, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItem(m_worn, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItem(m_inv, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItem(m_bank, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItem(m_shbank, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItem(m_trade, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItem(m_cursor, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -717,40 +712,46 @@ int16 EQ::InventoryProfile::HasItemByUse(uint8 use, uint8 quantity, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItemByUse(m_worn, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItemByUse(m_inv, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItemByUse(m_bank, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItemByUse(m_shbank, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItemByUse(m_trade, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByUse(m_cursor, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -763,40 +764,46 @@ int16 EQ::InventoryProfile::HasItemByLoreGroup(uint32 loregroup, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItemByLoreGroup(m_worn, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItemByLoreGroup(m_inv, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItemByLoreGroup(m_bank, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItemByLoreGroup(m_shbank, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItemByLoreGroup(m_trade, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByLoreGroup(m_cursor, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -824,7 +831,7 @@ int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 mi continue; } - const auto *inst = GetItem(i); + const EQ::ItemInstance* inst = GetItem(i); if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) { if (inst->GetItem()->BagType == item::BagTypeQuiver && inst->GetItem()->ItemType != item::ItemTypeArrow) { @@ -832,8 +839,8 @@ int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 mi } const int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + const uint8 slots = inst->GetItem()->BagSlots; - const uint8 slots = inst->GetItem()->BagSlots; for (uint8 j = invbag::SLOT_BEGIN; j < slots; j++) { if (!GetItem(base_slot_id + j)) { // Found available slot within bag @@ -861,22 +868,25 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // // I'll probably implement a bitmask in the new inventory system to avoid having to adjust stack bias - if ((general_start < invslot::GENERAL_BEGIN) || (general_start > invslot::GENERAL_END)) - return INVALID_INDEX; - if (bag_start > invbag::SLOT_END) - return INVALID_INDEX; - - if (!inst || !inst->GetID()) + if ( + !EQ::ValueWithin(general_start, invslot::GENERAL_BEGIN, invslot::GENERAL_END) || + bag_start > invbag::SLOT_END || + !inst || + !inst->GetID() + ) { return INVALID_INDEX; + } // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) if (inst->IsClassBag()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } - if (!m_inv[free_slot]) + if (!m_inv[free_slot]) { return free_slot; + } } return invslot::slotCursor; // return cursor since bags do not stack and will not fit inside other bags..yet...) @@ -885,37 +895,55 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 2: find partial room for stackables if (inst->IsStackable()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { continue; + } - if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize)) + if ( + main_inst->GetID() == inst->GetID() && + main_inst->GetCharges() < main_inst->GetItem()->StackSize + ) { return free_slot; + } } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { continue; + } if (main_inst->IsClassBag()) { // if item-specific containers already have bad items, we won't fix it here... uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { const ItemInstance* sub_inst = main_inst->GetItem(free_bag_slot); - if (!sub_inst) + if (!sub_inst) { continue; + } - if ((sub_inst->GetID() == inst->GetID()) && (sub_inst->GetCharges() < sub_inst->GetItem()->StackSize)) + if ( + sub_inst->GetID() == inst->GetID() && + sub_inst->GetCharges() < sub_inst->GetItem()->StackSize + ) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -924,18 +952,30 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 3a: find room for container-specific items (ItemClassArrow) if (inst->GetItem()->ItemType == item::ItemTypeArrow) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeQuiver) || !main_inst->IsClassBag()) + if ( + !main_inst || + main_inst->GetItem()->BagType != item::BagTypeQuiver || + !main_inst->IsClassBag() + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -943,47 +983,73 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 3b: find room for container-specific items (ItemClassSmallThrowing) if (inst->GetItem()->ItemType == item::ItemTypeSmallThrowing) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeBandolier) || !main_inst->IsClassBag()) + if ( + !main_inst || + main_inst->GetItem()->BagType != item::BagTypeBandolier || + !main_inst->IsClassBag() + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } // step 4: just find an empty slot for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { return free_slot; + } } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; if (main_inst && main_inst->IsClassBag()) { - if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == item::BagTypeBandolier) || (main_inst->GetItem()->BagType == item::BagTypeQuiver)) + if ( + main_inst->GetItem()->BagSize < inst->GetItem()->Size || + main_inst->GetItem()->BagType == item::BagTypeBandolier || + main_inst->GetItem()->BagType == item::BagTypeQuiver + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -993,7 +1059,8 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i } // Opposite of below: Get parent bag slot_id from a slot inside of bag -int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { +int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) +{ int16 parent_slot_id = INVALID_INDEX; // this is not a bag range... using this risks over-writing existing items @@ -1001,19 +1068,15 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { // parent_slot_id = EmuConstants::BANK_BEGIN + (slot_id - EmuConstants::BANK_BEGIN) / EmuConstants::ITEM_CONTAINER_SIZE; //else if (slot_id >= 3100 && slot_id <= 3179) should be {3031..3110}..where did this range come from!!? (verified db save range) - if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { parent_slot_id = invslot::GENERAL_BEGIN + (slot_id - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { parent_slot_id = invslot::slotCursor; - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { parent_slot_id = invslot::BANK_BEGIN + (slot_id - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { parent_slot_id = invslot::SHARED_BANK_BEGIN + (slot_id - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { parent_slot_id = invslot::TRADE_BEGIN + (slot_id - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT; } @@ -1021,26 +1084,24 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { } // Calculate slot_id for an item within a bag -int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { - if (!InventoryProfile::SupportsContainers(bagslot_id)) +int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) +{ + if (!InventoryProfile::SupportsContainers(bagslot_id)) { return INVALID_INDEX; + } int16 slot_id = INVALID_INDEX; if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; - } - else if (bagslot_id >= invslot::GENERAL_BEGIN && bagslot_id <= invslot::GENERAL_END) { - slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::BANK_BEGIN && bagslot_id <= invslot::BANK_END) { - slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invslot::BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::SHARED_BANK_BEGIN && bagslot_id <= invslot::SHARED_BANK_END) { - slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invslot::SHARED_BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::TRADE_BEGIN && bagslot_id <= invslot::TRADE_END) { - slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invslot::TRADE_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { + slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invbag::GENERAL_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { + slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invbag::BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { + slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invbag::SHARED_BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { + slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invbag::TRADE_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; } return slot_id; @@ -1049,26 +1110,17 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { uint8 EQ::InventoryProfile::CalcBagIdx(int16 slot_id) { uint8 index = 0; - // this is not a bag range... using this risks over-writing existing items - //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) - // index = (slot_id - EmuConstants::BANK_BEGIN) % EmuConstants::ITEM_CONTAINER_SIZE; - - if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { index = (slot_id - invbag::GENERAL_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { index = (slot_id - invbag::CURSOR_BAG_BEGIN); // % invbag::SLOT_COUNT; - not needed since range is 10 slots - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { index = (slot_id - invbag::BANK_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { index = (slot_id - invbag::SHARED_BANK_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { index = (slot_id - invbag::TRADE_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invslot::WORLD_BEGIN && slot_id <= invslot::WORLD_END) { + } else if (EQ::ValueWithin(slot_id, invslot::WORLD_BEGIN, invslot::WORLD_END)) { index = (slot_id - invslot::WORLD_BEGIN); // % invbag::SLOT_COUNT; - not needed since range is 10 slots } @@ -1104,45 +1156,53 @@ int16 EQ::InventoryProfile::CalcSlotFromMaterial(uint8 material) uint8 EQ::InventoryProfile::CalcMaterialFromSlot(int16 equipslot) { - switch (equipslot) - { - case invslot::slotHead: - return textures::armorHead; - case invslot::slotChest: - return textures::armorChest; - case invslot::slotArms: - return textures::armorArms; - case invslot::slotWrist1: - //case SLOT_BRACER02: // non-live behavior - return textures::armorWrist; - case invslot::slotHands: - return textures::armorHands; - case invslot::slotLegs: - return textures::armorLegs; - case invslot::slotFeet: - return textures::armorFeet; - case invslot::slotPrimary: - return textures::weaponPrimary; - case invslot::slotSecondary: - return textures::weaponSecondary; - default: - return textures::materialInvalid; + switch (equipslot) { + case invslot::slotHead: + return textures::armorHead; + case invslot::slotChest: + return textures::armorChest; + case invslot::slotArms: + return textures::armorArms; + case invslot::slotWrist1: + return textures::armorWrist; + case invslot::slotHands: + return textures::armorHands; + case invslot::slotLegs: + return textures::armorLegs; + case invslot::slotFeet: + return textures::armorFeet; + case invslot::slotPrimary: + return textures::weaponPrimary; + case invslot::slotSecondary: + return textures::weaponSecondary; + default: + return textures::materialInvalid; } } bool EQ::InventoryProfile::CanItemFitInContainer(const ItemData *ItemToTry, const ItemData *Container) { - if (!ItemToTry || !Container) + if (!ItemToTry || !Container) { return false; + } - if (ItemToTry->Size > Container->BagSize) + if (ItemToTry->Size > Container->BagSize) { return false; + } - if ((Container->BagType == item::BagTypeQuiver) && (ItemToTry->ItemType != item::ItemTypeArrow)) + if ( + Container->BagType == item::BagTypeQuiver && + ItemToTry->ItemType != item::ItemTypeArrow + ) { return false; + } - if ((Container->BagType == item::BagTypeBandolier) && (ItemToTry->ItemType != item::ItemTypeSmallThrowing)) + if ( + Container->BagType == item::BagTypeBandolier && + ItemToTry->ItemType != item::ItemTypeSmallThrowing + ) { return false; + } return true; } @@ -1150,15 +1210,14 @@ bool EQ::InventoryProfile::CanItemFitInContainer(const ItemData *ItemToTry, cons bool EQ::InventoryProfile::SupportsClickCasting(int16 slot_id) { // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range - if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { return true; - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { return true; - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { - if (inventory::StaticLookup(m_mob_version)->AllowClickCastFromBag) + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { + if (inventory::StaticLookup(m_mob_version)->AllowClickCastFromBag) { return true; + } } return false; @@ -1167,13 +1226,11 @@ bool EQ::InventoryProfile::SupportsClickCasting(int16 slot_id) bool EQ::InventoryProfile::SupportsPotionBeltCasting(int16 slot_id) { // does this have the same criteria as 'SupportsClickCasting' above? (bag clicking per client) - if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { return true; - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { return true; - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { return true; } @@ -1183,11 +1240,12 @@ bool EQ::InventoryProfile::SupportsPotionBeltCasting(int16 slot_id) // Test whether a given slot can support a container item bool EQ::InventoryProfile::SupportsContainers(int16 slot_id) { - if ((slot_id == invslot::slotCursor) || - (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) || - (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) || - (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) || - (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) + if ( + slot_id == invslot::slotCursor || + EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END) || + EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END) || + EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END) || + EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END) ) { return true; } @@ -1236,84 +1294,65 @@ uint8 EQ::InventoryProfile::FindBrightestLightType() uint8 brightest_light_type = 0; for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { - if ((iter->first < invslot::EQUIPMENT_BEGIN || iter->first > invslot::EQUIPMENT_END)) + if (!EQ::ValueWithin(iter->first, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { continue; + } - if (iter->first == invslot::slotAmmo) + if (iter->first == invslot::slotAmmo) { continue; + } - auto inst = iter->second; - if (inst == nullptr) + EQ::ItemInstance* inst = iter->second; + if (!inst) { continue; + } - auto item = inst->GetItem(); - if (item == nullptr) + const EQ::ItemData* item = inst->GetItem(); + if (!item) { continue; + } - if (lightsource::IsLevelGreater(item->Light, brightest_light_type)) + if (lightsource::IsLevelGreater(item->Light, brightest_light_type)) { brightest_light_type = item->Light; + } } uint8 general_light_type = 0; for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { - if (iter->first < invslot::GENERAL_BEGIN || iter->first > invslot::GENERAL_END) + if (!EQ::ValueWithin(iter->first, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { continue; + } - auto inst = iter->second; - if (inst == nullptr) + EQ::ItemInstance* inst = iter->second; + if (!inst) { continue; + } - auto item = inst->GetItem(); - if (item == nullptr) + const EQ::ItemData* item = inst->GetItem(); + if (!item) { continue; + } - if (!item->IsClassCommon()) + if (!item->IsClassCommon()) { continue; - if (item->Light < 9 || item->Light > 13) + } + + if (!EQ::ValueWithin(item->Light, 9, 13)) { continue; + } - if (lightsource::TypeToLevel(item->Light)) + if (lightsource::TypeToLevel(item->Light)) { general_light_type = item->Light; + } } - if (lightsource::IsLevelGreater(general_light_type, brightest_light_type)) + if (lightsource::IsLevelGreater(general_light_type, brightest_light_type)) { brightest_light_type = general_light_type; + } return brightest_light_type; } -void EQ::InventoryProfile::dumpEntireInventory() { - - dumpWornItems(); - dumpInventory(); - dumpBankItems(); - dumpSharedBankItems(); - - std::cout << std::endl; -} - -void EQ::InventoryProfile::dumpWornItems() { - std::cout << "Worn items:" << std::endl; - dumpItemCollection(m_worn); -} - -void EQ::InventoryProfile::dumpInventory() { - std::cout << "Inventory items:" << std::endl; - dumpItemCollection(m_inv); -} - -void EQ::InventoryProfile::dumpBankItems() { - - std::cout << "Bank items:" << std::endl; - dumpItemCollection(m_bank); -} - -void EQ::InventoryProfile::dumpSharedBankItems() { - - std::cout << "Shared Bank items:" << std::endl; - dumpItemCollection(m_shbank); -} - int EQ::InventoryProfile::GetSlotByItemInstCollection(const std::map &collection, ItemInstance *inst) { for (auto iter = collection.begin(); iter != collection.end(); ++iter) { ItemInstance *t_inst = iter->second; @@ -1333,57 +1372,22 @@ int EQ::InventoryProfile::GetSlotByItemInstCollection(const std::map &collection) -{ - for (auto it = collection.cbegin(); it != collection.cend(); ++it) { - auto inst = it->second; - if (!inst || !inst->GetItem()) - continue; - - std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); - std::cout << slot << std::endl; - - dumpBagContents(inst, &it); - } -} - -void EQ::InventoryProfile::dumpBagContents(ItemInstance *inst, std::map::const_iterator *it) -{ - if (!inst || !inst->IsClassBag()) - return; - - // Go through bag, if bag - for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { - ItemInstance* baginst = itb->second; - if (!baginst || !baginst->GetItem()) - continue; - - std::string subSlot = StringFormat(" Slot %d: %s (%d)", InventoryProfile::CalcSlotId((*it)->first, itb->first), - baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges()); - std::cout << subSlot << std::endl; - } - -} - // Internal Method: Retrieves item within an inventory bucket EQ::ItemInstance* EQ::InventoryProfile::_GetItem(const std::map& bucket, int16 slot_id) const { - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) == 0) { return nullptr; - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { - if (slot_id - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (slot_id - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { return nullptr; + } } auto it = bucket.find(slot_id); - if (it != bucket.end()) { - return it->second; - } - // Not found! - return nullptr; + return it != bucket.end() ? it->second : nullptr; } // Internal Method: "put" item into bucket, without regard for what is currently in bucket @@ -1393,68 +1397,59 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... // // If putting a nullptr into slot, we need to remove slot without memory delete - if (inst == nullptr) { + if (!inst) { //Why do we not delete the poped item here???? PopItem(slot_id); return slot_id; } - int16 result = INVALID_INDEX; - int16 parentSlot = INVALID_INDEX; + int16 result = INVALID_INDEX; + int16 parent_slot = INVALID_INDEX; if (slot_id == invslot::slotCursor) { // Replace current item on cursor, if exists m_cursor.pop(); // no memory delete, clients of this function know what they are doing m_cursor.push_front(inst); result = slot_id; - } - else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + } else if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { m_worn[slot_id] = inst; result = slot_id; } - } - else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { m_inv[slot_id] = inst; result = slot_id; } - } - else if (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END)) { m_worn[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, invslot::GUILD_TRIBUTE_END)) { m_worn[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { if (slot_id - EQ::invslot::BANK_BEGIN < m_lookup->InventoryTypeSize.Bank) { m_bank[slot_id] = inst; result = slot_id; } - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { m_shbank[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { m_trade[slot_id] = inst; result = slot_id; - } - else { + } else { // Slot must be within a bag - parentSlot = InventoryProfile::CalcSlotId(slot_id); - ItemInstance* baginst = GetItem(parentSlot); // Get parent bag - if (baginst && baginst->IsClassBag()) - { + parent_slot = InventoryProfile::CalcSlotId(slot_id); + ItemInstance* baginst = GetItem(parent_slot); // Get parent bag + if (baginst && baginst->IsClassBag()) { baginst->_PutItem(InventoryProfile::CalcBagIdx(slot_id), inst); result = slot_id; } } if (result == INVALID_INDEX) { - LogError("Invalid slot_id specified ({}) with parent slot id ({})", slot_id, parentSlot); + LogError("Invalid slot_id specified ({}) with parent slot id ({})", slot_id, parent_slot); InventoryProfile::MarkDirty(inst); // Slot not found, clean up } @@ -1467,44 +1462,55 @@ int16 EQ::InventoryProfile::_HasItem(std::map& bucket, uin uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } if (inst->GetID() == item_id) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return iter->first; + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->GetID() == item_id) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } } @@ -1524,35 +1530,45 @@ int16 EQ::InventoryProfile::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint uint32 quantity_found = 0; for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } if (inst->GetID() == item_id) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return invslot::slotCursor; + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->GetID() == item_id) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } @@ -1569,34 +1585,43 @@ int16 EQ::InventoryProfile::_HasItemByUse(std::map& bucket uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return iter->first; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } } } } @@ -1610,25 +1635,33 @@ int16 EQ::InventoryProfile::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint uint32 quantity_found = 0; for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return invslot::slotCursor; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } } } @@ -1642,44 +1675,57 @@ int16 EQ::InventoryProfile::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint int16 EQ::InventoryProfile::_HasItemByLoreGroup(std::map& bucket, uint32 loregroup) { for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } - if (inst->GetItem()->LoreGroup == loregroup) + if (inst->GetItem()->LoreGroup == loregroup) { return iter->first; + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { auto aug_inst = inst->GetAugment(index); if (aug_inst == nullptr) { continue; } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = bag_inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } } @@ -1691,35 +1737,49 @@ int16 EQ::InventoryProfile::_HasItemByLoreGroup(std::map& int16 EQ::InventoryProfile::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) { for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } - if (inst->GetItem()->LoreGroup == loregroup) + if (inst->GetItem()->LoreGroup == loregroup) { return invslot::slotCursor; + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = bag_inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } @@ -1744,42 +1804,9 @@ std::vector EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id) return augments; } -std::vector EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::ItemData *item_data) -{ - std::vector free_slots{}; - for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { - if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) { - continue; - } - - EQ::ItemInstance *inv_item = GetItem(i); - - if (!inv_item) { - // Found available slot in personal inventory - free_slots.push_back(i); - } - - if (inv_item->IsClassBag() && - EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) { - - int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN); - uint8 bag_size = inv_item->GetItem()->BagSlots; - - for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) { - auto bag_item = GetItem(base_slot_id + bag_slot); - if (!bag_item) { - // Found available slot within bag - free_slots.push_back(i); - } - } - } - } - return free_slots; -} - int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data) { - for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { + for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) { continue; } diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 100d5ebd8d..18b6154c45 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -176,7 +176,6 @@ namespace EQ // Locate an available inventory slot int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN); - std::vector FindAllFreeSlotsThatFitItem(const EQ::ItemData *inst); int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst); // Calculate slot_id for an item within a bag @@ -199,12 +198,6 @@ namespace EQ uint8 FindBrightestLightType(); - void dumpEntireInventory(); - void dumpWornItems(); - void dumpInventory(); - void dumpBankItems(); - void dumpSharedBankItems(); - void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, const std::string& value); void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, int value); void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, float value); @@ -217,8 +210,6 @@ namespace EQ /////////////////////////////// int GetSlotByItemInstCollection(const std::map &collection, ItemInstance *inst); - void dumpItemCollection(const std::map &collection); - void dumpBagContents(ItemInstance *inst, std::map::const_iterator *it); // Retrieves item within an inventory bucket ItemInstance* _GetItem(const std::map& bucket, int16 slot_id) const; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index aba25030b0..c8818c8a1e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1639,11 +1639,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.abilitySlotRefresh = 0; } -#ifdef _EQDEBUG - printf("Dumping inventory on load:\n"); - m_inv.dumpEntireInventory(); -#endif - /* Reset to max so they dont drown on zone in if its underwater */ m_pp.air_remaining = 60; /* Check for PVP Zone status*/ From cd158aabcae0546795f654d127d0d8941b23ee19 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 21:49:12 -0400 Subject: [PATCH 05/73] Cleanup --- common/inventory_profile.cpp | 8 +-- common/shareddb.cpp | 2 +- zone/client_process.cpp | 77 +++++++++++++++++++---------- zone/gm_commands/show/inventory.cpp | 8 +-- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index b200388aa9..dbb3f6e1b4 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -1095,13 +1095,13 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { - slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invbag::GENERAL_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { - slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invbag::BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invslot::BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { - slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invbag::SHARED_BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invslot::SHARED_BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { - slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invbag::TRADE_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invslot::TRADE_BEGIN) * invbag::SLOT_COUNT + bagidx; } return slot_id; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a052c86520..d9f57f8bed 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -804,7 +804,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile* inv) } int16 put_slot_id; - if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) || slot_id == EQ::invslot::slotCursor) { + if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END)) { put_slot_id = inv->PushCursor(*inst); } else { put_slot_id = inv->PutItem(slot_id, *inst); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b8f3ace3eb..7e938e9743 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -779,47 +779,70 @@ void Client::BulkSendInventoryItems() inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) + if (ob.tellp() == last_pos) { LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + + last_pos = ob.tellp(); + } + + // General items + for (int16 slot_id = EQ::invslot::GENERAL_BEGIN; slot_id <= EQ::invslot::GENERAL_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + inst->Serialize(ob, slot_id); + + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } last_pos = ob.tellp(); } if (!RuleB(Inventory, LazyLoadBank)) { - // Bank items - for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { - const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + // Bank items + for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } - inst->Serialize(ob, slot_id); + inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } - last_pos = ob.tellp(); - } + last_pos = ob.tellp(); + } - // SharedBank items - for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) { - const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + // SharedBank items + for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } - inst->Serialize(ob, slot_id); + inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + + last_pos = ob.tellp(); + } + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); - last_pos = ob.tellp(); - } - } + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); - auto outapp = new EQApplicationPacket(OP_CharInventory); - outapp->size = ob.size(); - outapp->pBuffer = ob.detach(); - QueuePacket(outapp); - safe_delete(outapp); + QueuePacket(outapp); + safe_delete(outapp); } void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { diff --git a/zone/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index 2078f2bdd6..2cffdc82f0 100644 --- a/zone/gm_commands/show/inventory.cpp +++ b/zone/gm_commands/show/inventory.cpp @@ -310,7 +310,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), item_data->ID, linker.GenerateLink(), ( @@ -339,7 +339,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} (Augment Slot {}) | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), augment_index, linker.GenerateLink(), item_data->ID, @@ -375,7 +375,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), sub_index, linker.GenerateLink(), item_data->ID, @@ -407,7 +407,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), sub_index, augment_index, linker.GenerateLink(), From df746dd07d4d0ff7012cf01bb561610f4f5ae53d Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Thu, 21 Nov 2024 23:19:41 -0500 Subject: [PATCH 06/73] Add slot ID conversions --- common/database/database_update_manifest.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 5cfbed3454..69558cefbe 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5819,6 +5819,20 @@ CHANGE COLUMN `ornamenticon` `ornament_icon` int(11) UNSIGNED NOT NULL DEFAULT 0 CHANGE COLUMN `ornamentidfile` `ornament_idfile` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_icon`, DROP PRIMARY KEY, ADD PRIMARY KEY (`character_id`, `slot_id`) USING BTREE; + +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 251) + 4010) WHERE `slot_id` BETWEEN 251 AND 260; -- Bag 1 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 261) + 4210) WHERE `slot_id` BETWEEN 261 AND 270; -- Bag 2 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 271) + 4410) WHERE `slot_id` BETWEEN 271 AND 280; -- Bag 3 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 281) + 4610) WHERE `slot_id` BETWEEN 281 AND 290; -- Bag 4 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 291) + 4810) WHERE `slot_id` BETWEEN 291 AND 300; -- Bag 5 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 301) + 5010) WHERE `slot_id` BETWEEN 301 AND 310; -- Bag 6 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 311) + 5210) WHERE `slot_id` BETWEEN 311 AND 320; -- Bag 7 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 321) + 5410) WHERE `slot_id` BETWEEN 321 AND 330; -- Bag 8 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 331) + 5610) WHERE `slot_id` BETWEEN 331 AND 340; -- Bag 9 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 341) + 5810) WHERE `slot_id` BETWEEN 341 AND 350; -- Bag 10 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 351) + 6010) WHERE `slot_id` BETWEEN 351 AND 360; -- Cursor Bag +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2270; -- Bank Bags +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags )" }, ManifestEntry{ From b2d0fa6a2f8a007392c2c8cb5f82d243e1dcd4ea Mon Sep 17 00:00:00 2001 From: carolus21rex <85852042+carolus21rex@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:23:48 -0500 Subject: [PATCH 07/73] [Bug Fix] Fix Strings::Commify bug with #mystats (#4547) * Fix a formatting bug with #mystats When using values larger than 1,000, we were calling commify on a string that already had commas. This resulted in the value 1005 looking like 1,,005. * Update mob.cpp --------- Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com> --- zone/mob.cpp | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index a06dbc7432..c6c81629c3 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2048,19 +2048,19 @@ void Mob::SendStatsWindow(Client* c, bool use_window) case 0: { mod2a_name = "Avoidance"; mod2b_name = "Combat Effects"; - mod2a_cap = Strings::Commify(RuleI(Character, ItemAvoidanceCap)); - mod2b_cap = Strings::Commify(RuleI(Character, ItemCombatEffectsCap)); + mod2a_cap = RuleI(Character, ItemAvoidanceCap); + mod2b_cap = RuleI(Character, ItemCombatEffectsCap); if (IsBot()) { - mod2a = Strings::Commify(CastToBot()->GetAvoidance()); + mod2a = CastToBot()->GetAvoidance(); } else if (IsClient()) { - mod2a = Strings::Commify(CastToClient()->GetAvoidance()); + mod2a = CastToClient()->GetAvoidance(); } if (IsBot()) { - mod2b = Strings::Commify(CastToBot()->GetCombatEffects()); + mod2b = CastToBot()->GetCombatEffects(); } else if (IsClient()) { - mod2b = Strings::Commify(CastToClient()->GetCombatEffects()); + mod2b = CastToClient()->GetCombatEffects(); } break; @@ -2068,19 +2068,19 @@ void Mob::SendStatsWindow(Client* c, bool use_window) case 1: { mod2a_name = "Accuracy"; mod2b_name = "Strikethrough"; - mod2a_cap = Strings::Commify(RuleI(Character, ItemAccuracyCap)); - mod2b_cap = Strings::Commify(RuleI(Character, ItemStrikethroughCap)); + mod2a_cap = RuleI(Character, ItemAccuracyCap); + mod2b_cap = RuleI(Character, ItemStrikethroughCap); if (IsBot()) { - mod2a = Strings::Commify(CastToBot()->GetAccuracy()); + mod2a = CastToBot()->GetAccuracy(); } else if (IsClient()) { - mod2a = Strings::Commify(CastToClient()->GetAccuracy()); + mod2a = CastToClient()->GetAccuracy(); } if (IsBot()) { - mod2b = Strings::Commify(CastToBot()->GetStrikeThrough()); + mod2b = CastToBot()->GetStrikeThrough(); } else if (IsClient()) { - mod2b = Strings::Commify(CastToClient()->GetStrikeThrough()); + mod2b = CastToClient()->GetStrikeThrough(); } break; @@ -2088,20 +2088,20 @@ void Mob::SendStatsWindow(Client* c, bool use_window) case 2: { mod2a_name = "Shielding"; mod2b_name = "Spell Shielding"; - mod2a_cap = Strings::Commify(RuleI(Character, ItemShieldingCap)); - mod2b_cap = Strings::Commify(RuleI(Character, ItemSpellShieldingCap)); + mod2a_cap = RuleI(Character, ItemShieldingCap); + mod2b_cap = RuleI(Character, ItemSpellShieldingCap); if (IsBot()) { - mod2a = Strings::Commify(CastToBot()->GetShielding()); + mod2a = CastToBot()->GetShielding(); } else if (IsClient()) { - mod2a = Strings::Commify(CastToClient()->GetShielding()); + mod2a = CastToClient()->GetShielding(); } if (IsBot()) { - mod2b = Strings::Commify(CastToBot()->GetSpellShield()); + mod2b = CastToBot()->GetSpellShield(); } else if (IsClient()) { - mod2b = Strings::Commify(CastToClient()->GetSpellShield()); + mod2b = CastToClient()->GetSpellShield(); } break; @@ -2109,19 +2109,19 @@ void Mob::SendStatsWindow(Client* c, bool use_window) case 3: { mod2a_name = "Stun Resist"; mod2b_name = "DOT Shielding"; - mod2a_cap = Strings::Commify(RuleI(Character, ItemStunResistCap)); - mod2b_cap = Strings::Commify(RuleI(Character, ItemDoTShieldingCap)); + mod2a_cap = RuleI(Character, ItemStunResistCap); + mod2b_cap = RuleI(Character, ItemDoTShieldingCap); if (IsBot()) { - mod2a = Strings::Commify(CastToBot()->GetStunResist()); + mod2a = CastToBot()->GetStunResist(); } else if (IsClient()) { - mod2a = Strings::Commify(CastToClient()->GetStunResist()); + mod2a = CastToClient()->GetStunResist(); } if (IsBot()) { - mod2b = Strings::Commify(CastToBot()->GetDoTShield()); + mod2b = CastToBot()->GetDoTShield(); } else if (IsClient()) { - mod2b = Strings::Commify(CastToClient()->GetDoTShield()); + mod2b = CastToClient()->GetDoTShield(); } break; From a49d1446b70c7f7fb60d0cea8032d9299602b2b9 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:15:18 -0400 Subject: [PATCH 08/73] [Bug Fix] Fix for sending money via Parcel, then changing your mind (#4552) --- zone/parcels.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zone/parcels.cpp b/zone/parcels.cpp index efb9267352..e3661906f9 100644 --- a/zone/parcels.cpp +++ b/zone/parcels.cpp @@ -278,6 +278,19 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) return; } + if (parcel_in->money_flag && parcel_in->item_slot != INVALID_INDEX) { + Message( + Chat::Yellow, + fmt::format( + "{} tells you, 'I am confused! Do you want to send money or an item?'", + merchant->GetCleanName() + ).c_str() + ); + DoParcelCancel(); + SendParcelAck(); + return; + } + auto num_of_parcels = GetParcelCount(); if (num_of_parcels >= RuleI(Parcel, ParcelMaxItems)) { SendParcelIconStatus(); From 7a841c11c5738fa6bc686072a7b7f3b98d40a406 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:17:01 -0400 Subject: [PATCH 09/73] [Bug Fix] Players could become flagged as a Trader when they were not trading (#4553) --- common/servertalk.h | 1 + zone/trading.cpp | 9 +++++---- zone/worldserver.cpp | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 46a3e08bc9..5761c1e79a 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -1945,6 +1945,7 @@ struct ServerOP_GuildMessage_Struct { struct TraderMessaging_Struct { uint32 action; uint32 zone_id; + uint32 instance_id; uint32 trader_id; uint32 entity_id; char trader_name[64]; diff --git a/zone/trading.cpp b/zone/trading.cpp index d3896fb091..faa1130662 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -2913,10 +2913,11 @@ void Client::SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions a auto outapp = new ServerPacket(ServerOP_TraderMessaging, sizeof(TraderMessaging_Struct)); auto data = (TraderMessaging_Struct *) outapp->pBuffer; - data->action = action; - data->entity_id = trader->GetID(); - data->trader_id = trader->CharacterID(); - data->zone_id = trader->GetZoneID(); + data->action = action; + data->entity_id = trader->GetID(); + data->trader_id = trader->CharacterID(); + data->zone_id = trader->GetZoneID(); + data->instance_id = trader->GetInstanceID(); strn0cpy(data->trader_name, trader->GetName(), sizeof(data->trader_name)); worldserver.SendPacket(outapp); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index acfb686df6..afff2a5342 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3942,7 +3942,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) c.second->QueuePacket(outapp); safe_delete(outapp); } - if (zone && zone->GetZoneID() == Zones::BAZAAR) { + if (zone && zone->GetZoneID() == Zones::BAZAAR && in->instance_id == zone->GetInstanceID()) { if (in->action == TraderOn) { c.second->SendBecomeTrader(TraderOn, in->entity_id); } From 12ada57ee8d0f2cf0af07667853a9318783f0a84 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:18:36 -0500 Subject: [PATCH 10/73] [Code] Fix build with older C++ libraries (#4549) This adds a compile time concept to determine if from_chars has floating-point support and uses fallbacks if not. This is a C++17 feature but support for floats was only added to libstdc++ with GCC 11.1 and LLVM libc++ in 20.0 (unreleased). --- common/mysql_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/mysql_stmt.cpp b/common/mysql_stmt.cpp index 0c71aa53c5..872df0086c 100644 --- a/common/mysql_stmt.cpp +++ b/common/mysql_stmt.cpp @@ -414,6 +414,12 @@ static uint64_t MakeBits(std::span data) return bits; } +template +concept has_from_chars = requires (const char* first, const char* last, T value) +{ + std::from_chars(first, last, value); +}; + template static T FromString(std::string_view sv) { @@ -422,6 +428,14 @@ static T FromString(std::string_view sv) // return false for empty (zero-length) strings return !sv.empty(); } + else if constexpr (std::is_same_v && !has_from_chars) + { + return std::strtof(std::string(sv).c_str(), nullptr); + } + else if constexpr (std::is_same_v && !has_from_chars) + { + return std::strtod(std::string(sv).c_str(), nullptr); + } else { // non numbers return a zero initialized T (could return nullopt instead) From 3158386aa37f93225925c2ba96d75366273debf7 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:19:40 -0500 Subject: [PATCH 11/73] [Bug Fix] Fix Issue with Perl EVENT_PAYLOAD (#4545) --- zone/embparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 113374e814..55cf1a4ce8 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1736,7 +1736,7 @@ void PerlembParser::ExportEventVariables( case EVENT_PAYLOAD: { Seperator sep(data); ExportVar(package_name.c_str(), "payload_id", sep.arg[0]); - ExportVar(package_name.c_str(), "payload_value", sep.arg[1]); + ExportVar(package_name.c_str(), "payload_value", sep.argplus[1]); break; } From 630da0eee6414f0496746f254a72110d04cad96c Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 24 Nov 2024 17:27:31 -0600 Subject: [PATCH 12/73] [Config] Fix World TCP Address Configuration Default (#4551) --- common/eqemu_config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/eqemu_config.cpp b/common/eqemu_config.cpp index 85db274921..40374de8bf 100644 --- a/common/eqemu_config.cpp +++ b/common/eqemu_config.cpp @@ -94,7 +94,7 @@ void EQEmuConfig::parse_config() auto_database_updates = true; } - WorldIP = _root["server"]["world"]["tcp"].get("host", "127.0.0.1").asString(); + WorldIP = _root["server"]["world"]["tcp"].get("ip", "127.0.0.1").asString(); WorldTCPPort = Strings::ToUnsignedInt(_root["server"]["world"]["tcp"].get("port", "9000").asString()); TelnetIP = _root["server"]["world"]["telnet"].get("ip", "127.0.0.1").asString(); From 9938755517fe69687405dcbad88e51f4ec03fafe Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:29:27 -0500 Subject: [PATCH 13/73] [Bug Fix] Fix Possible Item Loss in Trades (#4554) --- zone/trading.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/trading.cpp b/zone/trading.cpp index faa1130662..f963f7ca10 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -777,6 +777,8 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st tradingWith->SayString(TRADE_BACK, GetCleanName()); PushItemOnCursor(*inst, true); } + + items.clear(); } // Only enforce trade rules if the NPC doesn't have an EVENT_TRADE // subroutine. That overrides all. From 6bd758b3ddd77c572637843ae08f7a76dd438df7 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:30:44 -0500 Subject: [PATCH 14/73] [Rules] Add Rule to Disable NPCs Facing Target (#4543) --- common/ruletypes.h | 1 + zone/npc.cpp | 42 ++++++++++++++++++++++++++++++++++-------- zone/npc.h | 3 ++- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index b2de14baa7..a4a1994004 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -681,6 +681,7 @@ RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names") RULE_BOOL(NPC, NPCIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") RULE_INT(NPC, NPCHasteCap, 150, "Haste cap for non-v3(over haste) haste") RULE_INT(NPC, NPCHastev3Cap, 25, "Haste cap for v3(over haste) haste") +RULE_STRING(NPC, ExcludedFaceTargetRaces, "52,72,73,141,233,328,329,372,376,377,378,379,380,381,382,383,404,422,423,424,425,426,428,429,445,449,460,462,463,500,501,502,503,504,505,506,507,508,509,510,511,513,514,515,516,533,534,535,536,537,538,539,540,541,542,543,544,545,546,550,551,552,553,554,555,556,557,567,573,577,586,589,590,591,592,593,595,596,599,601,616,619,621,628,629,630,633,634,635,636,665,683,684,685,691,692,693,694,702,703,705,706,707,710,711,714,720,2250,2254", "Race IDs excluded from facing target when hailed") RULE_CATEGORY_END() RULE_CATEGORY(Aggro) diff --git a/zone/npc.cpp b/zone/npc.cpp index f1260cd9c9..38f597e631 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3297,16 +3297,28 @@ uint32 NPC::GetSpawnKillCount() return(0); } -void NPC::DoQuestPause(Mob *other) { - if(IsMoving() && !IsOnHatelist(other)) { - PauseWandering(RuleI(NPC, SayPauseTimeInSec)); - if (other && !other->sneaking) - FaceTarget(other); - } else if(!IsMoving()) { - if (other && !other->sneaking && GetAppearance() != eaSitting && GetAppearance() != eaDead) - FaceTarget(other); +void NPC::DoQuestPause(Mob* m) +{ + if (!m) { + return; } + if (IsMoving() && !IsOnHatelist(m)) { + PauseWandering(RuleI(NPC, SayPauseTimeInSec)); + + if (FacesTarget() && !m->sneaking) { + FaceTarget(m); + } + } else if (!IsMoving()) { + if ( + FacesTarget() && + !m->sneaking && + GetAppearance() != eaSitting && + GetAppearance() != eaDead + ) { + FaceTarget(m); + } + } } void NPC::ChangeLastName(std::string last_name) @@ -4238,3 +4250,17 @@ void NPC::DoNpcToNpcAggroScan() false ); } + +bool NPC::FacesTarget() +{ + const std::string& excluded_races_rule = RuleS(NPC, ExcludedFaceTargetRaces); + + if (excluded_races_rule.empty()) { + return true; + } + + const auto& v = Strings::Split(excluded_races_rule, ","); + + return std::find(v.begin(), v.end(), std::to_string(GetBaseRace())) == v.end(); +} + diff --git a/zone/npc.h b/zone/npc.h index 20b6f74f6f..a20d975b12 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -482,7 +482,8 @@ class NPC : public Mob NPC_Emote_Struct* GetNPCEmote(uint32 emote_id, uint8 event_); void DoNPCEmote(uint8 event_, uint32 emote_id, Mob* t = nullptr); bool CanTalk(); - void DoQuestPause(Mob *other); + void DoQuestPause(Mob* m); + bool FacesTarget(); inline void SetSpellScale(float amt) { spellscale = amt; } inline float GetSpellScale() { return spellscale; } From 187ee1021890f16228606b610617938efd7a22ea Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:02:14 -0500 Subject: [PATCH 15/73] [Tasks] Update tasks in all zones if invalid zone set (#4550) This allows task elements to update in any zone when it has an invalid zone id <= 0. This has the same effect as leaving the zones field empty except "Unknown Zone" instead of "ALL" will be shown in task windows. Note that Titanium shows "ALL" for a zone id of 0 despite it being an invalid zone id. This could be manipulated server side to match newer clients but there isn't much benefit since any other invalid zone id below 0 can be used to do the same. --- common/tasks.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/tasks.h b/common/tasks.h index 8f081daf56..8cf98335b7 100644 --- a/common/tasks.h +++ b/common/tasks.h @@ -83,7 +83,8 @@ struct ActivityInformation { if (zone_ids.empty()) { return true; } - bool found_zone = std::find(zone_ids.begin(), zone_ids.end(), zone_id) != zone_ids.end(); + bool found_zone = std::any_of(zone_ids.begin(), zone_ids.end(), + [zone_id](int id) { return id <= 0 || id == zone_id; }); return found_zone && (zone_version == version || zone_version == -1); } @@ -100,7 +101,7 @@ struct ActivityInformation { out.WriteInt32(activity_type == TaskActivityType::GiveCash ? 1 : goal_count); out.WriteLengthString(skill_list); // used in SkillOn objective type string, "-1" for none out.WriteLengthString(spell_list); // used in CastOn objective type string, "0" for none - out.WriteString(zones); // used in objective zone column and task select "begins in" (may have multiple, "0" for "unknown zone", empty for "ALL") + out.WriteString(zones); // used in ui zone columns and task select "begins in" (may have multiple, invalid id for "Unknown Zone", empty for "ALL") } else { @@ -114,7 +115,7 @@ struct ActivityInformation { out.WriteString(description_override); if (client_version >= EQ::versions::ClientVersion::RoF) { - out.WriteString(zones); // serialized again after description (seems unused) + out.WriteString(zones); // target zone version internal id (unused client side) } } From 3d7cf4235c110f24fb9bfa3684fa171a3529c7cd Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:17:03 -0500 Subject: [PATCH 16/73] [Release] 22.60.0 (#4555) --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ common/version.h | 2 +- package.json | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e5cd22c2..dcfa1f1bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +## [22.60.0] 11/25/2024 + +### Bazaar + +* Further refinements for instanced bazaar ([#4544](https://github.com/EQEmu/Server/pull/4544)) @neckkola 2024-11-16 + +### Code + +* Fix build with older C++ libraries ([#4549](https://github.com/EQEmu/Server/pull/4549)) @hgtw 2024-11-24 + +### Config + +* Fix World TCP Address Configuration Default ([#4551](https://github.com/EQEmu/Server/pull/4551)) @Akkadius 2024-11-24 + +### Fixes + +* Fix Issue with Perl EVENT_PAYLOAD ([#4545](https://github.com/EQEmu/Server/pull/4545)) @Kinglykrab 2024-11-24 +* Fix Possible Item Loss in Trades ([#4554](https://github.com/EQEmu/Server/pull/4554)) @Kinglykrab 2024-11-24 +* Fix Strings::Commify bug with #mystats ([#4547](https://github.com/EQEmu/Server/pull/4547)) @carolus21rex 2024-11-22 +* Fix an edge case with augmented items inside parceled containers ([#4546](https://github.com/EQEmu/Server/pull/4546)) @neckkola 2024-11-21 +* Fix for bazaar search of containers. ([#4540](https://github.com/EQEmu/Server/pull/4540)) @neckkola 2024-11-15 +* Fix for mult-instanced bazaar zones ([#4541](https://github.com/EQEmu/Server/pull/4541)) @neckkola 2024-11-15 +* Fix for sending money via Parcel, then changing your mind ([#4552](https://github.com/EQEmu/Server/pull/4552)) @neckkola 2024-11-24 +* Fix issue where NPC's are being hidden as traders ([#4539](https://github.com/EQEmu/Server/pull/4539)) @Akkadius 2024-11-15 +* Players could become flagged as a Trader when they were not trading ([#4553](https://github.com/EQEmu/Server/pull/4553)) @neckkola 2024-11-24 + +### Rules + +* Add Rule to Disable NPCs Facing Target ([#4543](https://github.com/EQEmu/Server/pull/4543)) @Kinglykrab 2024-11-24 + +### Tasks + +* Update tasks in all zones if invalid zone set ([#4550](https://github.com/EQEmu/Server/pull/4550)) @hgtw 2024-11-25 + ## [22.59.1] 11/13/2024 ### Hotfix diff --git a/common/version.h b/common/version.h index c615c7c854..a144157a78 100644 --- a/common/version.h +++ b/common/version.h @@ -25,7 +25,7 @@ // Build variables // these get injected during the build pipeline -#define CURRENT_VERSION "22.59.1-dev" // always append -dev to the current version for custom-builds +#define CURRENT_VERSION "22.60.0-dev" // always append -dev to the current version for custom-builds #define LOGIN_VERSION "0.8.0" #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ diff --git a/package.json b/package.json index f2590babbe..26dfe3d29b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eqemu-server", - "version": "22.59.1", + "version": "22.60.0", "repository": { "type": "git", "url": "https://github.com/EQEmu/Server.git" From fe9df46a2484b4aff109b2639d88e583fe6a94c8 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:30:29 -0500 Subject: [PATCH 17/73] [Bug Fix] Fix EVENT_COMBAT on NPC Death (#4558) --- zone/mob_ai.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index fa2a48aa55..69255073b1 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1815,12 +1815,18 @@ void Mob::AI_Event_NoLongerEngaged() { StopNavigation(); ClearRampage(); - parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; }); - if (IsNPC()) { SetPrimaryAggro(false); SetAssistAggro(false); - if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { + if ( + CastToNPC()->GetCombatEvent() && + GetHP() > 0 && + entity_list.GetNPCByID(GetID()) + ) { + if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) { + parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); + } + const uint32 emote_id = CastToNPC()->GetEmoteID(); if (emote_id) { CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); @@ -1829,6 +1835,8 @@ void Mob::AI_Event_NoLongerEngaged() { m_combat_record.Stop(); CastToNPC()->SetCombatEvent(false); } + } else { + parse->EventBotMerc(EVENT_COMBAT, this, nullptr, [&]() { return "0"; }); } } From 5f0b999ca9f6a93ba5d2da39b50b26a12fe004ce Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:04:34 -0600 Subject: [PATCH 18/73] [Groups] Fix AmIMainAssist incorrectly checking for MainTankName (#4565) --- zone/groups.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index ad6972e2a6..e61bbade79 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -2419,7 +2419,7 @@ bool Group::AmIMainAssist(const char *mob_name) if (!mob_name) return false; - return !((bool)MainTankName.compare(mob_name)); + return !((bool)MainAssistName.compare(mob_name)); } bool Group::AmIPuller(const char *mob_name) From 8e40e5357ce4f9c4b1485fb0f11dc9a7ffce7e69 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:58:11 -0600 Subject: [PATCH 19/73] [Bots] Fix AA ranks to account for level (#4567) Previously level requirement was only being checked on the initial rank of an AA. If passed, bots would gain all ranks for that AA regardless of level, this will now check for the level requirement for each rank before granting the AA --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7871f9ac9f..bd077b4564 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1265,7 +1265,7 @@ void Bot::LoadAAs() { } while(current) { - if (!CanUseAlternateAdvancementRank(current)) { + if (current->level_req > GetLevel() || !CanUseAlternateAdvancementRank(current)) { current = nullptr; } else { current = current->next; From bd85fc96a000eb3f30ee5145c9a680049c0945be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 00:17:28 -0600 Subject: [PATCH 20/73] Bump golang.org/x/crypto in /utils/scripts/build/should-release (#4570) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- utils/scripts/build/should-release/go.mod | 2 +- utils/scripts/build/should-release/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/scripts/build/should-release/go.mod b/utils/scripts/build/should-release/go.mod index d963fcd5f5..929c8e621c 100644 --- a/utils/scripts/build/should-release/go.mod +++ b/utils/scripts/build/should-release/go.mod @@ -10,7 +10,7 @@ require ( require ( github.com/golang/protobuf v1.3.2 // indirect github.com/google/go-querystring v1.1.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.23.0 // indirect google.golang.org/appengine v1.6.7 // indirect ) diff --git a/utils/scripts/build/should-release/go.sum b/utils/scripts/build/should-release/go.sum index cdad01fc49..2caeabf3c0 100644 --- a/utils/scripts/build/should-release/go.sum +++ b/utils/scripts/build/should-release/go.sum @@ -10,8 +10,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= From 5c6e7a8b0989ff47337258b9ca60430fb4d0fa83 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 12 Dec 2024 01:43:22 -0500 Subject: [PATCH 21/73] [Cleanup] Convert Event Parses to Single Line (#4569) * [Cleanup] Convert Event Parses to Single Line * Push * Update spells.cpp * Update spells.cpp --------- Co-authored-by: Akkadius --- zone/attack.cpp | 29 +++-------- zone/bot.cpp | 18 +------ zone/client_packet.cpp | 116 +++++------------------------------------ zone/entity.cpp | 16 +----- zone/mob.cpp | 9 +--- zone/questmgr.cpp | 63 +++++----------------- zone/spells.cpp | 58 +++++++-------------- 7 files changed, 56 insertions(+), 253 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 6ffe44099e..e2d7cdf176 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1550,10 +1550,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo hit.damage_done = 0; } - parse->EventBotMerc( - EVENT_USE_SKILL, - this, - nullptr, + parse->EventBotMerc(EVENT_USE_SKILL, this, nullptr, [&]() { return fmt::format( "{} {}", @@ -3078,10 +3075,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy std::vector args = { corpse }; - parse->EventMercNPC( - EVENT_DEATH_COMPLETE, - this, - owner_or_self, + parse->EventMercNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, [&]() { return fmt::format( "{} {} {} {} {} {} {} {} {}", @@ -3096,8 +3090,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy m_combat_record.GetHealingReceived() ); }, - 0, - &args + 0, &args ); // Zone controller process EVENT_DEATH_ZONE (Death events) @@ -4266,10 +4259,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons if (attacker) { args = { this }; - parse->EventMob( - EVENT_DAMAGE_GIVEN, - attacker, - this, + parse->EventMob(EVENT_DAMAGE_GIVEN, attacker, this, [&]() { return fmt::format( "{} {} {} {} {} {} {} {} {}", @@ -4284,17 +4274,13 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons static_cast(special) ); }, - 0, - &args + 0, &args ); } args = { attacker }; - damage_override = parse->EventMob( - EVENT_DAMAGE_TAKEN, - this, - attacker, + damage_override = parse->EventMob(EVENT_DAMAGE_TAKEN, this, attacker, [&]() { return fmt::format( "{} {} {} {} {} {} {} {} {}", @@ -4309,8 +4295,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons static_cast(special) ); }, - 0, - &args + 0, &args ); if (damage_override > 0) { diff --git a/zone/bot.cpp b/zone/bot.cpp index bd077b4564..68e0f561eb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4192,14 +4192,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* std::vector args = { return_iterator.return_item_instance }; - parse->EventBot( - EVENT_UNEQUIP_ITEM_BOT, - this, - nullptr, - export_string, - return_iterator.return_item_instance->GetID(), - &args - ); + parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, this, nullptr, export_string, return_iterator.return_item_instance->GetID(), &args); } if (return_instance) { @@ -4264,14 +4257,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* std::vector args = { trade_iterator.trade_item_instance }; - parse->EventBot( - EVENT_EQUIP_ITEM_BOT, - this, - nullptr, - export_string, - trade_iterator.trade_item_instance->GetID(), - &args - ); + parse->EventBot(EVENT_EQUIP_ITEM_BOT, this, nullptr, export_string, trade_iterator.trade_item_instance->GetID(), &args); } trade_iterator.trade_item_instance = nullptr; // actual deletion occurs in client delete below diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 14ee222f34..94d6756dde 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3251,30 +3251,14 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) args.push_back(old_aug); if (parse->ItemHasQuestSub(tobe_auged, EVENT_UNAUGMENT_ITEM)) { - parse->EventItem( - EVENT_UNAUGMENT_ITEM, - this, - tobe_auged, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); } args.assign(1, tobe_auged); args.push_back(false); if (parse->ItemHasQuestSub(old_aug, EVENT_AUGMENT_REMOVE)) { - parse->EventItem( - EVENT_AUGMENT_REMOVE, - this, - old_aug, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args); } if (parse->PlayerHasQuestSub(EVENT_AUGMENT_REMOVE_CLIENT)) { @@ -3306,29 +3290,13 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) args.push_back(aug); if (parse->ItemHasQuestSub(tobe_auged, EVENT_AUGMENT_ITEM)) { - parse->EventItem( - EVENT_AUGMENT_ITEM, - this, - tobe_auged, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); } args.assign(1, tobe_auged); if (parse->ItemHasQuestSub(aug, EVENT_AUGMENT_INSERT)) { - parse->EventItem( - EVENT_AUGMENT_INSERT, - this, - aug, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); } args.push_back(aug); @@ -3398,30 +3366,14 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) args.push_back(aug); if (parse->ItemHasQuestSub(tobe_auged, EVENT_UNAUGMENT_ITEM)) { - parse->EventItem( - EVENT_UNAUGMENT_ITEM, - this, - tobe_auged, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); } args.assign(1, tobe_auged); args.push_back(false); if (parse->ItemHasQuestSub(aug, EVENT_AUGMENT_REMOVE)) { - parse->EventItem( - EVENT_AUGMENT_REMOVE, - this, - aug, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); } args.push_back(aug); @@ -3486,30 +3438,14 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) args.push_back(aug); if (parse->ItemHasQuestSub(tobe_auged, EVENT_UNAUGMENT_ITEM)) { - parse->EventItem( - EVENT_UNAUGMENT_ITEM, - this, - tobe_auged, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); } args.assign(1, tobe_auged); args.push_back(true); if (parse->ItemHasQuestSub(aug, EVENT_AUGMENT_REMOVE)) { - parse->EventItem( - EVENT_AUGMENT_REMOVE, - this, - aug, - nullptr, - "", - in_augment->augment_index, - &args - ); + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); } args.push_back(aug); @@ -4479,14 +4415,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) int i = 0; if (parse->ItemHasQuestSub(p_inst, EVENT_ITEM_CLICK_CAST)) { - i = parse->EventItem( - EVENT_ITEM_CLICK_CAST, - this, - p_inst, - nullptr, - "", - castspell->inventoryslot - ); + i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); } if (parse->PlayerHasQuestSub(EVENT_ITEM_CLICK_CAST_CLIENT)) { @@ -4518,14 +4447,7 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) int i = 0; if (parse->ItemHasQuestSub(p_inst, EVENT_ITEM_CLICK_CAST)) { - i = parse->EventItem( - EVENT_ITEM_CLICK_CAST, - this, - p_inst, - nullptr, - "", - castspell->inventoryslot - ); + i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); } if (parse->PlayerHasQuestSub(EVENT_ITEM_CLICK_CAST_CLIENT)) { @@ -9591,14 +9513,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) int i = 0; if (parse->ItemHasQuestSub(p_inst, EVENT_ITEM_CLICK_CAST)) { - i = parse->EventItem( - EVENT_ITEM_CLICK_CAST, - this, - p_inst, - nullptr, - "", - slot_id - ); + i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); } if (parse->PlayerHasQuestSub(EVENT_ITEM_CLICK_CAST_CLIENT)) { @@ -9662,14 +9577,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) int i = 0; if (parse->ItemHasQuestSub(p_inst, EVENT_ITEM_CLICK_CAST)) { - i = parse->EventItem( - EVENT_ITEM_CLICK_CAST, - this, - clickaug, - nullptr, - "", - slot_id - ); + i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); } if (parse->PlayerHasQuestSub(EVENT_ITEM_CLICK_CAST_CLIENT)) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 4a9a232cad..fab1be99f7 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1817,25 +1817,13 @@ void EntityList::DuelMessage(Mob *winner, Mob *loser, bool flee) if (parse->PlayerHasQuestSub(EVENT_DUEL_WIN)) { std::vector args = { winner, loser }; - parse->EventPlayer( - EVENT_DUEL_WIN, - winner->CastToClient(), - loser->GetName(), - loser->CastToClient()->CharacterID(), - &args - ); + parse->EventPlayer(EVENT_DUEL_WIN, winner->CastToClient(), loser->GetName(), loser->CastToClient()->CharacterID(), &args); } if (parse->PlayerHasQuestSub(EVENT_DUEL_LOSE)) { std::vector args = { winner, loser }; - parse->EventPlayer( - EVENT_DUEL_LOSE, - loser->CastToClient(), - winner->GetName(), - winner->CastToClient()->CharacterID(), - &args - ); + parse->EventPlayer(EVENT_DUEL_LOSE, loser->CastToClient(), winner->GetName(), winner->CastToClient()->CharacterID(), &args); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index c6c81629c3..8e42193196 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5325,14 +5325,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance* inst, uint16 spell_id, Mob* on, //It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere. //So we'll live with it for now if (parse->ItemHasQuestSub(const_cast(inst), EVENT_WEAPON_PROC)) { - int i = parse->EventItem( - EVENT_WEAPON_PROC, - CastToClient(), - const_cast(inst), - on, - "", - spell_id - ); + int i = parse->EventItem(EVENT_WEAPON_PROC, CastToClient(), const_cast(inst), on, "", spell_id); if (i != 0) { return; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 68bd859af1..e1a859addd 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -91,13 +91,7 @@ void QuestManager::Process() { if (cur->Timer_.Enabled() && cur->Timer_.Check()) { if (cur->mob) { if (cur->mob->IsEncounter()) { - parse->EventEncounter( - EVENT_TIMER, - cur->mob->CastToEncounter()->GetEncounterName(), - cur->name, - 0, - nullptr - ); + parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr); } else { parse->EventMob(EVENT_TIMER, cur->mob, nullptr, [&]() { return cur->name; }, 0); } @@ -661,14 +655,7 @@ void QuestManager::stoptimer(const std::string& timer_name) for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { if (e->mob && e->mob == owner && e->name == timer_name) { - parse->EventMob( - EVENT_TIMER_STOP, - owner, - nullptr, - [&]() { - return timer_name; - } - ); + parse->EventMob(EVENT_TIMER_STOP, owner, nullptr, [&]() { return timer_name; }); QTimerList.erase(e); break; @@ -695,14 +682,7 @@ void QuestManager::stoptimer(const std::string& timer_name, Mob* m) for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == m) { - parse->EventMob( - EVENT_TIMER_STOP, - m, - nullptr, - [&]() { - return timer_name; - } - ); + parse->EventMob(EVENT_TIMER_STOP, m, nullptr, [&]() { return timer_name; }); QTimerList.erase(e); break; @@ -743,14 +723,7 @@ void QuestManager::stopalltimers() for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == owner) { - parse->EventMob( - EVENT_TIMER_STOP, - owner, - nullptr, - [&]() { - return e->name; - } - ); + parse->EventMob(EVENT_TIMER_STOP, owner, nullptr, [&]() { return e->name; }); e = QTimerList.erase(e); } else { @@ -792,14 +765,7 @@ void QuestManager::stopalltimers(Mob* m) for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == m) { - parse->EventMob( - EVENT_TIMER_STOP, - m, - nullptr, - [&]() { - return e->name; - } - ); + parse->EventMob(EVENT_TIMER_STOP, m, nullptr, [&]() { return e->name; }); e = QTimerList.erase(e); } else { @@ -853,18 +819,13 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m) } ); - parse->EventMob( - EVENT_TIMER_PAUSE, - mob, - nullptr, - [&]() { - return fmt::format( - "{} {}", - timer_name, - milliseconds - ); - } - ); + parse->EventMob(EVENT_TIMER_PAUSE, mob, nullptr, [&]() { + return fmt::format( + "{} {}", + timer_name, + milliseconds + ); + }); LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); } diff --git a/zone/spells.cpp b/zone/spells.cpp index 7b644cd3ac..15d1e55745 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -256,22 +256,15 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, Mob* spell_target = entity_list.GetMobID(target_id); std::vector args = { spell_target }; - int return_value = parse->EventMob( - EVENT_CAST_BEGIN, - this, - nullptr, - [&]() { - return fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - }, - 0, - &args - ); + int return_value = parse->EventMob(EVENT_CAST_BEGIN, this, nullptr, [&]() { + return fmt::format( + "{} {} {} {}", + spell_id, + GetID(), + GetCasterLevel(spell_id), + target_id + ); + }, 0, &args); if (IsClient() && return_value != 0) { if (IsDiscipline(spell_id)) { @@ -1804,22 +1797,15 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo std::vector args = { spell_target }; - parse->EventMob( - EVENT_CAST, - this, - nullptr, - [&]() { - return fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - }, - 0, - &args - ); + parse->EventMob(EVENT_CAST, this, nullptr, [&]() { + return fmt::format( + "{} {} {} {}", + spell_id, + GetID(), + GetCasterLevel(spell_id), + target_id + ); + }, 0, &args); if(bard_song_mode) { @@ -3965,10 +3951,7 @@ bool Mob::SpellOnTarget( std::vector args = { spelltar }; - parse->EventMob( - EVENT_CAST_ON, - spelltar, - this, + parse->EventMob(EVENT_CAST_ON, spelltar, this, [&]() { return fmt::format( "{} {} {} {}", @@ -3978,8 +3961,7 @@ bool Mob::SpellOnTarget( target_id ); }, - 0, - &args + 0, &args ); if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { From 66a7dd0143edcf1d2c684a26565f194934771d4a Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 12 Dec 2024 01:17:08 -0600 Subject: [PATCH 22/73] [Databuckets] Improved Reliability and Performance of Databuckets (#4562) * [Databuckets] Don't broadcast client-scoped updates * Remove temp feature flag * Remove distributed caching, only cache for character scoped data, simplify * Update bot.cpp * Cleanup * Update data_bucket.cpp * Cleanup * Cleanup * Remove BulkLoadEntities from LoadNPCTypes * Update data_bucket.cpp * Cleanup * More cleanup * More cleanup * BulkLoadEntities to BulkLoadEntitiesToCache * Add CanCache in DeleteData to gate an unnecessary call --- common/servertalk.h | 2 - world/zoneserver.cpp | 5 - zone/bot.cpp | 1 + zone/client_packet.cpp | 2 +- zone/data_bucket.cpp | 424 +++++++++++++++-------------------------- zone/data_bucket.h | 39 +--- zone/mob.cpp | 20 ++ zone/mob.h | 2 + zone/worldserver.cpp | 5 - zone/zonedb.cpp | 2 - 10 files changed, 180 insertions(+), 322 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 5761c1e79a..62641ee583 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -319,8 +319,6 @@ // player events #define ServerOP_PlayerEvent 0x5100 -#define ServerOP_DataBucketCacheUpdate 0x5200 - enum { CZUpdateType_Character, CZUpdateType_Group, diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index b00afb4b70..25f6fdbd84 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1564,11 +1564,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { DynamicZone::HandleZoneMessage(pack); break; } - case ServerOP_DataBucketCacheUpdate: { - zoneserver_list.SendPacket(pack); - - break; - } case ServerOP_GuildTributeUpdate: { auto data = (GuildTributeUpdate *)pack->pBuffer; auto guild = guild_mgr.GetGuildByGuildID(data->guild_id); diff --git a/zone/bot.cpp b/zone/bot.cpp index 68e0f561eb..aa746e7677 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3089,6 +3089,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { m_targetable = true; entity_list.AddBot(this, true, true); + ClearDataBucketCache(); DataBucket::GetDataBuckets(this); LoadBotSpellSettings(); if (!AI_AddBotSpells(GetBotSpellID())) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 94d6756dde..73a333f828 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -524,7 +524,6 @@ int Client::HandlePacket(const EQApplicationPacket *app) // Finish client connecting state void Client::CompleteConnect() { - UpdateWho(); client_state = CLIENT_CONNECTED; SendAllPackets(); @@ -1413,6 +1412,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) drakkin_details = m_pp.drakkin_details; // Load Data Buckets + ClearDataBucketCache(); DataBucket::GetDataBuckets(this); // Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel diff --git a/zone/data_bucket.cpp b/zone/data_bucket.cpp index d022183bc8..db2222348d 100644 --- a/zone/data_bucket.cpp +++ b/zone/data_bucket.cpp @@ -8,7 +8,7 @@ extern WorldServer worldserver; -std::vector g_data_bucket_cache = {}; +std::vector g_data_bucket_cache = {}; void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time) { @@ -58,14 +58,14 @@ void DataBucket::SetData(const DataBucketKey &k) b.value = k.value; if (bucket_id) { - // loop cache and update cache value and timestamp - for (auto &ce: g_data_bucket_cache) { - if (CheckBucketMatch(ce.e, k)) { - ce.e = b; - ce.updated_time = GetCurrentTimeUNIX(); - ce.update_action = DataBucketCacheUpdateAction::Upsert; - SendDataBucketCacheUpdate(ce); - break; + + // update the cache if it exists + if (CanCache(k)) { + for (auto &e: g_data_bucket_cache) { + if (CheckBucketMatch(e, k)) { + e = b; + break; + } } } @@ -74,28 +74,18 @@ void DataBucket::SetData(const DataBucketKey &k) else { b.key_ = k.key; b = DataBucketsRepository::InsertOne(database, b); - if (!ExistsInCache(b)) { - // add data bucket and timestamp to cache - auto ce = DataBucketCacheEntry{ - .e = b, - .updated_time = DataBucket::GetCurrentTimeUNIX(), - .update_action = DataBucketCacheUpdateAction::Upsert - }; - - g_data_bucket_cache.emplace_back(ce); - - SendDataBucketCacheUpdate(ce); + // add to cache if it doesn't exist + if (CanCache(k) && !ExistsInCache(b)) { DeleteFromMissesCache(b); + g_data_bucket_cache.emplace_back(b); } } } std::string DataBucket::GetData(const std::string &bucket_key) { - DataBucketKey k = {}; - k.key = bucket_key; - return GetData(k).value; + return GetData(DataBucketKey{.key = bucket_key}).value; } // GetData fetches bucket data from the database or cache if it exists @@ -112,22 +102,27 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b k.npc_id ); - for (const auto &ce: g_data_bucket_cache) { - if (CheckBucketMatch(ce.e, k)) { - if (ce.e.expires > 0 && ce.e.expires < std::time(nullptr)) { - LogDataBuckets("Attempted to read expired key [{}] removing from cache", ce.e.key_); - DeleteData(k); - return DataBucketsRepository::NewEntity(); - } + bool can_cache = CanCache(k); - // this is a bucket miss, return empty entity - // we still cache bucket misses, so we don't have to hit the database - if (ce.e.id == 0) { - return DataBucketsRepository::NewEntity(); - } + // check the cache first if we can cache + if (can_cache) { + for (const auto &e: g_data_bucket_cache) { + if (CheckBucketMatch(e, k)) { + if (e.expires > 0 && e.expires < std::time(nullptr)) { + LogDataBuckets("Attempted to read expired key [{}] removing from cache", e.key_); + DeleteData(k); + return DataBucketsRepository::NewEntity(); + } + + // this is a bucket miss, return empty entity + // we still cache bucket misses, so we don't have to hit the database + if (e.id == 0) { + return DataBucketsRepository::NewEntity(); + } - LogDataBuckets("Returning key [{}] value [{}] from cache", ce.e.key_, ce.e.value); - return ce.e; + LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value); + return e; + } } } @@ -144,23 +139,21 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b // if we're ignoring the misses cache, don't add to the cache // the only place this is ignored is during the initial read of SetData - if (!ignore_misses_cache) { + bool add_to_misses_cache = !ignore_misses_cache && can_cache; + if (add_to_misses_cache) { size_t size_before = g_data_bucket_cache.size(); // cache bucket misses, so we don't have to hit the database // when scripts try to read a bucket that doesn't exist g_data_bucket_cache.emplace_back( - DataBucketCacheEntry{ - .e = DataBucketsRepository::DataBuckets{ - .id = 0, - .key_ = k.key, - .value = "", - .expires = 0, - .character_id = k.character_id, - .npc_id = k.npc_id, - .bot_id = k.bot_id - }, - .updated_time = DataBucket::GetCurrentTimeUNIX() + DataBucketsRepository::DataBuckets{ + .id = 0, + .key_ = k.key, + .value = "", + .expires = 0, + .character_id = k.character_id, + .npc_id = k.npc_id, + .bot_id = k.bot_id } ); @@ -178,60 +171,54 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b return {}; } + auto bucket = r.front(); + // if the entry has expired, delete it - if (r[0].expires > 0 && r[0].expires < (long long) std::time(nullptr)) { + if (bucket.expires > 0 && bucket.expires < (long long) std::time(nullptr)) { DeleteData(k); return {}; } - bool has_cache = false; - for (auto &ce: g_data_bucket_cache) { - if (ce.e.id == r[0].id) { - has_cache = true; - break; - } - } + // add to cache if it doesn't exist + if (can_cache) { + bool has_cache = false; - if (!has_cache) { - // add data bucket and timestamp to cache - g_data_bucket_cache.emplace_back( - DataBucketCacheEntry{ - .e = r[0], - .updated_time = DataBucket::GetCurrentTimeUNIX() + for (auto &e: g_data_bucket_cache) { + if (e.id == bucket.id) { + has_cache = true; + break; } - ); + } + + if (!has_cache) { + g_data_bucket_cache.emplace_back(bucket); + } } - return r[0]; + return bucket; } std::string DataBucket::GetDataExpires(const std::string &bucket_key) { - DataBucketKey k = {}; - k.key = bucket_key; - - return GetDataExpires(k); + return GetDataExpires(DataBucketKey{.key = bucket_key}); } std::string DataBucket::GetDataRemaining(const std::string &bucket_key) { - DataBucketKey k = {}; - k.key = bucket_key; - return GetDataRemaining(k); + return GetDataRemaining(DataBucketKey{.key = bucket_key}); } bool DataBucket::DeleteData(const std::string &bucket_key) { - DataBucketKey k = {}; - k.key = bucket_key; - return DeleteData(k); + return DeleteData(DataBucketKey{.key = bucket_key}); } // GetDataBuckets bulk loads all data buckets for a mob bool DataBucket::GetDataBuckets(Mob *mob) { - DataBucketLoadType::Type t; - const uint32 id = mob->GetMobTypeIdentifier(); + DataBucketLoadType::Type t{}; + + const uint32 id = mob->GetMobTypeIdentifier(); if (!id) { return false; @@ -243,46 +230,39 @@ bool DataBucket::GetDataBuckets(Mob *mob) else if (mob->IsClient()) { t = DataBucketLoadType::Client; } - else if (mob->IsNPC()) { - t = DataBucketLoadType::NPC; - } - BulkLoadEntities(t, {id}); + BulkLoadEntitiesToCache(t, {id}); return true; } bool DataBucket::DeleteData(const DataBucketKey &k) { - size_t size_before = g_data_bucket_cache.size(); + if (CanCache(k)) { + size_t size_before = g_data_bucket_cache.size(); - // delete from cache where contents match - g_data_bucket_cache.erase( - std::remove_if( - g_data_bucket_cache.begin(), - g_data_bucket_cache.end(), - [&](DataBucketCacheEntry &ce) { - bool match = CheckBucketMatch(ce.e, k); - if (match) { - ce.update_action = DataBucketCacheUpdateAction::Delete; - SendDataBucketCacheUpdate(ce); + // delete from cache where contents match + g_data_bucket_cache.erase( + std::remove_if( + g_data_bucket_cache.begin(), + g_data_bucket_cache.end(), + [&](DataBucketsRepository::DataBuckets &e) { + return CheckBucketMatch(e, k); } + ), + g_data_bucket_cache.end() + ); - return match; - } - ), - g_data_bucket_cache.end() - ); - - LogDataBuckets( - "Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]", - k.key, - k.bot_id, - k.character_id, - k.npc_id, - size_before, - g_data_bucket_cache.size() - ); + LogDataBuckets( + "Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]", + k.key, + k.bot_id, + k.character_id, + k.npc_id, + size_before, + g_data_bucket_cache.size() + ); + } return DataBucketsRepository::DeleteWhere( database, @@ -371,23 +351,21 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, ); } -void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector ids) +void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector ids) { if (ids.empty()) { return; } if (ids.size() == 1) { - bool has_cache = false; - for (const auto &ce: g_data_bucket_cache) { + bool has_cache = false; + + for (const auto &e: g_data_bucket_cache) { if (t == DataBucketLoadType::Bot) { - has_cache = ce.e.bot_id == ids[0]; + has_cache = e.bot_id == ids[0]; } else if (t == DataBucketLoadType::Client) { - has_cache = ce.e.character_id == ids[0]; - } - else if (t == DataBucketLoadType::NPC) { - has_cache = ce.e.npc_id == ids[0]; + has_cache = e.character_id == ids[0]; } } @@ -406,9 +384,6 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector(t)); break; @@ -442,12 +417,7 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector( - std::chrono::system_clock::now().time_since_epoch() - ).count(); -} - -bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &e) -{ - for (const auto &ce: g_data_bucket_cache) { - if (ce.e.id == e.id) { + for (const auto &e: g_data_bucket_cache) { + if (e.id == entry.id) { return true; } } @@ -507,134 +469,6 @@ bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &e) return false; } -bool DataBucket::SendDataBucketCacheUpdate(const DataBucketCacheEntry &e) -{ - if (!e.e.id) { - return false; - } - - EQ::Net::DynamicPacket p; - p.PutSerialize(0, e); - - auto pack_size = sizeof(ServerDataBucketCacheUpdate_Struct) + p.Length(); - auto pack = new ServerPacket(ServerOP_DataBucketCacheUpdate, static_cast(pack_size)); - auto buf = reinterpret_cast(pack->pBuffer); - - buf->cereal_size = static_cast(p.Length()); - - memcpy(buf->cereal_data, p.Data(), p.Length()); - - worldserver.SendPacket(pack); - - return true; -} - -void DataBucket::HandleWorldMessage(ServerPacket *p) -{ - DataBucketCacheEntry n; - auto s = (ServerDataBucketCacheUpdate_Struct *) p->pBuffer; - EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size); - cereal::BinaryInputArchive archive(ss); - archive(n); - - LogDataBucketsDetail( - "Received cache packet for id [{}] key [{}] value [{}] action [{}]", - n.e.id, - n.e.key_, - n.e.value, - static_cast(n.update_action) - ); - - // delete - if (n.update_action == DataBucketCacheUpdateAction::Delete) { - DeleteFromMissesCache(n.e); - - g_data_bucket_cache.erase( - std::remove_if( - g_data_bucket_cache.begin(), - g_data_bucket_cache.end(), - [&](DataBucketCacheEntry &ce) { - bool match = n.e.id > 0 && ce.e.id == n.e.id; - if (match) { - LogDataBuckets( - "[delete] cache key [{}] id [{}] cache_size before [{}] after [{}]", - ce.e.key_, - ce.e.id, - g_data_bucket_cache.size(), - g_data_bucket_cache.size() - 1 - ); - } - return match; - } - ), - g_data_bucket_cache.end() - ); - - return; - } - - // update - bool has_key = false; - for (auto &ce: g_data_bucket_cache) { - // update cache - if (ce.e.id == n.e.id) { - // reject old updates - int64 time_delta = ce.updated_time - n.updated_time; - if (ce.updated_time >= n.updated_time) { - LogDataBuckets( - "Attempted to update older cache key [{}] rejecting old time [{}] new time [{}] delta [{}] cache_size [{}]", - ce.e.key_, - ce.updated_time, - n.updated_time, - time_delta, - g_data_bucket_cache.size() - ); - return; - } - - DeleteFromMissesCache(n.e); - - LogDataBuckets( - "[update] cache id [{}] key [{}] value [{}] old time [{}] new time [{}] delta [{}] cache_size [{}]", - ce.e.id, - ce.e.key_, - n.e.value, - ce.updated_time, - n.updated_time, - time_delta, - g_data_bucket_cache.size() - ); - ce.e = n.e; - ce.updated_time = n.updated_time; - has_key = true; - break; - } - } - - // create - if (!has_key) { - DeleteFromMissesCache(n.e); - - size_t size_before = g_data_bucket_cache.size(); - - g_data_bucket_cache.emplace_back( - DataBucketCacheEntry{ - .e = n.e, - .updated_time = GetCurrentTimeUNIX() - } - ); - - LogDataBuckets( - "[create] Adding new cache id [{}] key [{}] value [{}] cache size before [{}] after [{}]", - n.e.id, - n.e.key_, - n.e.value, - size_before, - g_data_bucket_cache.size() - ); - } -} - void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e) { // delete from cache where there might have been a written bucket miss to the cache @@ -645,11 +479,11 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e) std::remove_if( g_data_bucket_cache.begin(), g_data_bucket_cache.end(), - [&](DataBucketCacheEntry &ce) { - return ce.e.id == 0 && ce.e.key_ == e.key_ && - ce.e.character_id == e.character_id && - ce.e.npc_id == e.npc_id && - ce.e.bot_id == e.bot_id; + [&](DataBucketsRepository::DataBuckets &ce) { + return ce.id == 0 && ce.key_ == e.key_ && + ce.character_id == e.character_id && + ce.npc_id == e.npc_id && + ce.bot_id == e.bot_id; } ), g_data_bucket_cache.end() @@ -667,3 +501,47 @@ void DataBucket::ClearCache() g_data_bucket_cache.clear(); LogInfo("Cleared data buckets cache"); } + +void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type) +{ + size_t size_before = g_data_bucket_cache.size(); + + g_data_bucket_cache.erase( + std::remove_if( + g_data_bucket_cache.begin(), + g_data_bucket_cache.end(), + [&](DataBucketsRepository::DataBuckets &e) { + switch (type) { + case DataBucketLoadType::Bot: + return e.bot_id == id; + case DataBucketLoadType::Client: + return e.character_id == id; + default: + return false; + } + } + ), + g_data_bucket_cache.end() + ); + + LogDataBuckets( + "Deleted [{}] id [{}] from cache size before [{}] after [{}]", + DataBucketLoadType::Name[type], + id, + size_before, + g_data_bucket_cache.size() + ); +} + +// CanCache returns whether a bucket can be cached or not +// characters are only in one zone at a time so we can cache locally to the zone +// bots (not implemented) are only in one zone at a time so we can cache locally to the zone +// npcs (ids) can be in multiple zones so we can't cache locally to the zone +bool DataBucket::CanCache(const DataBucketKey &key) +{ + if (key.character_id > 0 || key.bot_id > 0) { + return true; + } + + return false; +} diff --git a/zone/data_bucket.h b/zone/data_bucket.h index 4ae270e445..8503330f23 100644 --- a/zone/data_bucket.h +++ b/zone/data_bucket.h @@ -1,7 +1,3 @@ -// -// Created by Akkadius on 7/7/18. -// - #ifndef EQEMU_DATABUCKET_H #define EQEMU_DATABUCKET_H @@ -12,27 +8,6 @@ #include "../common/json/json_archive_single_line.h" #include "../common/servertalk.h" -enum DataBucketCacheUpdateAction : uint8 { - Upsert, - Delete -}; - -struct DataBucketCacheEntry { - DataBucketsRepository::DataBuckets e; - int64_t updated_time{}; - DataBucketCacheUpdateAction update_action{}; - - template - void serialize(Archive &ar) - { - ar( - CEREAL_NVP(e), - CEREAL_NVP(updated_time), - CEREAL_NVP(update_action) - ); - } -}; - struct DataBucketKey { std::string key; std::string value; @@ -46,14 +21,12 @@ namespace DataBucketLoadType { enum Type : uint8 { Bot, Client, - NPC, MaxType }; static const std::string Name[Type::MaxType] = { "Bot", "Client", - "NPC", }; } @@ -68,8 +41,6 @@ class DataBucket { static bool GetDataBuckets(Mob *mob); - static int64_t GetCurrentTimeUNIX(); - // scoped bucket methods static void SetData(const DataBucketKey &k); static bool DeleteData(const DataBucketKey &k); @@ -80,15 +51,15 @@ class DataBucket { // bucket repository versus key matching static bool CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k); - static bool ExistsInCache(const DataBucketsRepository::DataBuckets &e); + static bool ExistsInCache(const DataBucketsRepository::DataBuckets &entry); - static void BulkLoadEntities(DataBucketLoadType::Type t, std::vector ids); - static void DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id); + static void BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector ids); + static void DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id); - static bool SendDataBucketCacheUpdate(const DataBucketCacheEntry &e); - static void HandleWorldMessage(ServerPacket *p); static void DeleteFromMissesCache(DataBucketsRepository::DataBuckets e); static void ClearCache(); + static void DeleteFromCache(uint64 id, DataBucketLoadType::Type type); + static bool CanCache(const DataBucketKey &key); }; #endif //EQEMU_DATABUCKET_H diff --git a/zone/mob.cpp b/zone/mob.cpp index 8e42193196..dd62d93291 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -572,6 +572,8 @@ Mob::~Mob() m_close_mobs.clear(); + ClearDataBucketCache(); + LeaveHealRotationTargetPool(); } @@ -8606,3 +8608,21 @@ std::unordered_map &Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); } + +void Mob::ClearDataBucketCache() +{ + if (IsOfClientBot()) { + uint64 id = 0; + DataBucketLoadType::Type t{}; + if (IsBot()) { + id = CastToBot()->GetBotID(); + t = DataBucketLoadType::Bot; + } + else if (IsClient()) { + id = CastToClient()->CharacterID(); + t = DataBucketLoadType::Client; + } + + DataBucket::DeleteFromCache(id, t); + } +} diff --git a/zone/mob.h b/zone/mob.h index d1bf386d63..297aa80cba 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1491,6 +1491,8 @@ class Mob : public Entity { std::unordered_map &GetCloseMobList(float distance = 0.0f); void CheckScanCloseMobsMovingTimer(); + void ClearDataBucketCache(); + protected: void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None); static uint16 GetProcID(uint16 spell_id, uint8 effect_index); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index afff2a5342..7b6b870e21 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3644,11 +3644,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) SharedTaskZoneMessaging::HandleWorldMessage(pack); break; } - case ServerOP_DataBucketCacheUpdate: - { - DataBucket::HandleWorldMessage(pack); - break; - } case ServerOP_GuildTributeUpdate: { GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 3f0ac20e8c..f346fd57cd 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1923,8 +1923,6 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load } } - DataBucket::BulkLoadEntities(DataBucketLoadType::NPC, npc_ids); - if (!npc_faction_ids.empty()) { zone->LoadNPCFactions(npc_faction_ids); zone->LoadNPCFactionAssociations(npc_faction_ids); From 6172c49b08f56d05e2bfe1896ef650d53b1b7865 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Thu, 12 Dec 2024 03:25:12 -0400 Subject: [PATCH 23/73] [Feature] Enable bazaar window 'Find Trader' functionality (#4560) * First pass to enable trader 'Find Trader' functionality * Move SendBulkTraders out of zoning routines and send as part of the opening of the bazaar search window. Add zone instance to SendBulkTraders to support multi-instanced bazaars. --- common/bazaar.cpp | 48 +++--- common/bazaar.h | 2 +- common/database/database_update_manifest.cpp | 11 ++ common/eq_packet_structs.h | 6 +- common/patches/rof2.cpp | 21 ++- common/patches/rof2_structs.h | 3 +- .../base/base_trader_repository.h | 148 ++++++++++-------- common/repositories/trader_repository.h | 15 +- common/version.h | 2 +- zone/client_packet.cpp | 8 +- zone/trading.cpp | 49 +++--- zone/worldserver.cpp | 8 +- 12 files changed, 187 insertions(+), 134 deletions(-) diff --git a/common/bazaar.cpp b/common/bazaar.cpp index 7891828213..b24fcb1d19 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -8,13 +8,14 @@ std::vector Bazaar::GetSearchResults( SharedDatabase &db, BazaarSearchCriteria_Struct search, - uint32 char_zone_id + uint32 char_zone_id, + int32 char_zone_instance_id ) { LogTrading( "Searching for items with search criteria - item_name [{}] min_cost [{}] max_cost [{}] min_level [{}] " "max_level [{}] max_results [{}] prestige [{}] augment [{}] trader_entity_id [{}] trader_id [{}] " - "search_scope [{}] char_zone_id [{}]", + "search_scope [{}] char_zone_id [{}], char_zone_instance_id [{}]", search.item_name, search.min_cost, search.max_cost, @@ -26,7 +27,8 @@ Bazaar::GetSearchResults( search.trader_entity_id, search.trader_id, search.search_scope, - char_zone_id + char_zone_id, + char_zone_instance_id ); std::string search_criteria_trader("TRUE "); @@ -34,14 +36,19 @@ Bazaar::GetSearchResults( if (search.search_scope == NonRoFBazaarSearchScope) { search_criteria_trader.append( fmt::format( - " AND trader.char_entity_id = {} AND trader.char_zone_id = {}", + " AND trader.char_entity_id = {} AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}", search.trader_entity_id, - Zones::BAZAAR + Zones::BAZAAR, + char_zone_instance_id ) ); } else if (search.search_scope == Local_Scope) { - search_criteria_trader.append(fmt::format(" AND trader.char_zone_id = {}", char_zone_id)); + search_criteria_trader.append(fmt::format( + " AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}", + char_zone_id, + char_zone_instance_id) + ); } else if (search.trader_id > 0) { search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); @@ -62,7 +69,7 @@ Bazaar::GetSearchResults( std::string query = fmt::format( "SELECT COUNT(item_id), trader.char_id, trader.item_id, trader.item_sn, trader.item_charges, trader.item_cost, " "trader.slot_id, SUM(trader.item_charges), trader.char_zone_id, trader.char_entity_id, character_data.name, " - "aug_slot_1, aug_slot_2, aug_slot_3, aug_slot_4, aug_slot_5, aug_slot_6 " + "aug_slot_1, aug_slot_2, aug_slot_3, aug_slot_4, aug_slot_5, aug_slot_6, trader.char_zone_instance_id " "FROM trader, character_data " "WHERE {} AND trader.char_id = character_data.id " "GROUP BY trader.item_sn, trader.item_charges, trader.char_id", @@ -122,19 +129,20 @@ Bazaar::GetSearchResults( continue; } - r.count = Strings::ToInt(row[0]); - r.trader_id = Strings::ToInt(row[1]); - r.serial_number = Strings::ToInt(row[3]); - r.cost = Strings::ToInt(row[5]); - r.slot_id = Strings::ToInt(row[6]); - r.sum_charges = Strings::ToInt(row[7]); - r.stackable = item->Stackable; - r.icon_id = item->Icon; - r.trader_zone_id = Strings::ToInt(row[8]); - r.trader_entity_id = Strings::ToInt(row[9]); - r.serial_number_RoF = fmt::format("{:016}\0", Strings::ToInt(row[3])); - r.item_name = fmt::format("{:.63}\0", item->Name); - r.trader_name = fmt::format("{:.63}\0", std::string(row[10]).c_str()); + r.count = Strings::ToInt(row[0]); + r.trader_id = Strings::ToInt(row[1]); + r.serial_number = Strings::ToInt(row[3]); + r.cost = Strings::ToInt(row[5]); + r.slot_id = Strings::ToInt(row[6]); + r.sum_charges = Strings::ToInt(row[7]); + r.stackable = item->Stackable; + r.icon_id = item->Icon; + r.trader_zone_id = Strings::ToInt(row[8]); + r.trader_zone_instance_id = Strings::ToInt(row[17]); + r.trader_entity_id = Strings::ToInt(row[9]); + r.serial_number_RoF = fmt::format("{:016}\0", Strings::ToInt(row[3])); + r.item_name = fmt::format("{:.63}\0", item->Name); + r.trader_name = fmt::format("{:.63}\0", std::string(row[10]).c_str()); LogTradingDetail( "Searching against item [{}] ({}) for trader [{}]", diff --git a/common/bazaar.h b/common/bazaar.h index 6ce9067248..fca2573af5 100644 --- a/common/bazaar.h +++ b/common/bazaar.h @@ -7,7 +7,7 @@ class Bazaar { public: static std::vector - GetSearchResults(SharedDatabase &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id); + GetSearchResults(SharedDatabase &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id, int char_zone_instance_id); }; diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 636899094e..61186a2054 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5770,6 +5770,17 @@ MODIFY COLUMN `exp_modifier` float NOT NULL DEFAULT 1.0 AFTER `aa_modifier`; CREATE INDEX idx_character_expires ON data_buckets (character_id, expires); CREATE INDEX idx_npc_expires ON data_buckets (npc_id, expires); CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires); +)" + }, + ManifestEntry{ + .version = 9286, + .description = "2024_11_26_bazaar_find_trader.sql", + .check = "SHOW COLUMNS FROM `trader` LIKE 'char_zone_instance_id'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `trader` + ADD COLUMN `char_zone_instance_id` INT NULL DEFAULT '0' AFTER `char_zone_id`; )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 322bdf6392..33e8606545 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3742,7 +3742,8 @@ struct GetItems_Struct{ struct BecomeTrader_Struct { uint32 action; - uint32 zone_id; + uint16 zone_id; + uint16 zone_instance_id; uint32 trader_id; uint32 entity_id; char trader_name[64]; @@ -6347,6 +6348,7 @@ enum BazaarTraderBarterActions { TraderAck2 = 22, AddTraderToBazaarWindow = 24, RemoveTraderFromBazaarWindow = 25, + FirstOpenSearch = 26, ClickTrader = 28, DeliveryCostUpdate = 29 }; @@ -6386,6 +6388,7 @@ struct BazaarSearchResultsFromDB_Struct { uint32 icon_id; uint32 sum_charges; uint32 trader_zone_id; + int32 trader_zone_instance_id; uint32 trader_entity_id; uint32 item_stat; bool stackable; @@ -6407,6 +6410,7 @@ struct BazaarSearchResultsFromDB_Struct { CEREAL_NVP(icon_id), CEREAL_NVP(sum_charges), CEREAL_NVP(trader_zone_id), + CEREAL_NVP(trader_zone_instance_id), CEREAL_NVP(trader_entity_id), CEREAL_NVP(item_stat), CEREAL_NVP(stackable), diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 9024f93b93..0406225baa 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -583,19 +583,21 @@ namespace RoF2 auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(BecomeTrader_Struct)); auto eq = (BecomeTrader_Struct *) outapp->pBuffer; - eq->action = emu->action; - eq->entity_id = emu->entity_id; - eq->trader_id = emu->trader_id; - eq->zone_id = emu->zone_id; + eq->action = emu->action; + eq->entity_id = emu->entity_id; + eq->trader_id = emu->trader_id; + eq->zone_id = emu->zone_id; + eq->zone_instance_id = emu->zone_instance_id; strn0cpy(eq->trader_name, emu->trader_name, sizeof(eq->trader_name)); LogTrading( - "(RoF2) AddTraderToBazaarWindow action [{}] trader_id [{}] entity_id [{}] zone_id [{}]", + "(RoF2) AddTraderToBazaarWindow action [{}] trader_id [{}] entity_id [{}] " + "zone_id [{}] zone_instance_id [{}]", eq->action, eq->trader_id, eq->entity_id, - eq->zone_id - ); + eq->zone_id, + eq->zone_instance_id); dest->FastQueuePacket(&outapp); break; } @@ -6218,6 +6220,11 @@ namespace RoF2 FINISH_DIRECT_DECODE(); break; } + case structs::RoF2BazaarTraderBuyerActions::FirstOpenSearch: { + __packet->SetOpcode(OP_BazaarSearch); + LogTrading("(RoF2) First time opening Bazaar Search since zoning. Action [{}]", action); + break; + } case structs::RoF2BazaarTraderBuyerActions::WelcomeMessage: { __packet->SetOpcode(OP_BazaarSearch); LogTrading("(RoF2) WelcomeMessage action [{}]", action); diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index fa43b65b3c..1bbf7c26c2 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -3119,7 +3119,8 @@ enum RoF2BazaarTraderBuyerActions { BazaarInspect = 18, ClickTrader = 28, ItemMove = 19, - ReconcileItems = 20 + ReconcileItems = 20, + FirstOpenSearch = 26 }; enum RoF2BuyerActions { diff --git a/common/repositories/base/base_trader_repository.h b/common/repositories/base/base_trader_repository.h index d7cdbbefc7..7670587df2 100644 --- a/common/repositories/base/base_trader_repository.h +++ b/common/repositories/base/base_trader_repository.h @@ -28,13 +28,14 @@ class BaseTraderRepository { uint32_t aug_slot_4; uint32_t aug_slot_5; uint32_t aug_slot_6; - int32_t item_sn; + uint32_t item_sn; int32_t item_charges; - uint64_t item_cost; + uint32_t item_cost; uint8_t slot_id; uint32_t char_entity_id; uint32_t char_zone_id; - int8_t active_transaction; + int32_t char_zone_instance_id; + uint8_t active_transaction; }; static std::string PrimaryKey() @@ -60,6 +61,7 @@ class BaseTraderRepository { "slot_id", "char_entity_id", "char_zone_id", + "char_zone_instance_id", "active_transaction", }; } @@ -82,6 +84,7 @@ class BaseTraderRepository { "slot_id", "char_entity_id", "char_zone_id", + "char_zone_instance_id", "active_transaction", }; } @@ -123,22 +126,23 @@ class BaseTraderRepository { { Trader e{}; - e.id = 0; - e.char_id = 0; - e.item_id = 0; - e.aug_slot_1 = 0; - e.aug_slot_2 = 0; - e.aug_slot_3 = 0; - e.aug_slot_4 = 0; - e.aug_slot_5 = 0; - e.aug_slot_6 = 0; - e.item_sn = 0; - e.item_charges = 0; - e.item_cost = 0; - e.slot_id = 0; - e.char_entity_id = 0; - e.char_zone_id = 0; - e.active_transaction = 0; + e.id = 0; + e.char_id = 0; + e.item_id = 0; + e.aug_slot_1 = 0; + e.aug_slot_2 = 0; + e.aug_slot_3 = 0; + e.aug_slot_4 = 0; + e.aug_slot_5 = 0; + e.aug_slot_6 = 0; + e.item_sn = 0; + e.item_charges = 0; + e.item_cost = 0; + e.slot_id = 0; + e.char_entity_id = 0; + e.char_zone_id = 0; + e.char_zone_instance_id = 0; + e.active_transaction = 0; return e; } @@ -175,22 +179,23 @@ class BaseTraderRepository { if (results.RowCount() == 1) { Trader e{}; - e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? static_cast(atoi(row[9])) : 0; - e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; - e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0; - e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.active_transaction = row[15] ? static_cast(atoi(row[15])) : 0; + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.item_sn = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; + e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; + e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; return e; } @@ -238,7 +243,8 @@ class BaseTraderRepository { v.push_back(columns[12] + " = " + std::to_string(e.slot_id)); v.push_back(columns[13] + " = " + std::to_string(e.char_entity_id)); v.push_back(columns[14] + " = " + std::to_string(e.char_zone_id)); - v.push_back(columns[15] + " = " + std::to_string(e.active_transaction)); + v.push_back(columns[15] + " = " + std::to_string(e.char_zone_instance_id)); + v.push_back(columns[16] + " = " + std::to_string(e.active_transaction)); auto results = db.QueryDatabase( fmt::format( @@ -275,6 +281,7 @@ class BaseTraderRepository { v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.char_entity_id)); v.push_back(std::to_string(e.char_zone_id)); + v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); auto results = db.QueryDatabase( @@ -320,6 +327,7 @@ class BaseTraderRepository { v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.char_entity_id)); v.push_back(std::to_string(e.char_zone_id)); + v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); @@ -354,22 +362,23 @@ class BaseTraderRepository { for (auto row = results.begin(); row != results.end(); ++row) { Trader e{}; - e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? static_cast(atoi(row[9])) : 0; - e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; - e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0; - e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.active_transaction = row[15] ? static_cast(atoi(row[15])) : 0; + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.item_sn = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; + e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; + e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -394,22 +403,23 @@ class BaseTraderRepository { for (auto row = results.begin(); row != results.end(); ++row) { Trader e{}; - e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.item_sn = row[9] ? static_cast(atoi(row[9])) : 0; - e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; - e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0; - e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.active_transaction = row[15] ? static_cast(atoi(row[15])) : 0; + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.item_sn = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.item_charges = row[10] ? static_cast(atoi(row[10])) : 0; + e.item_cost = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.slot_id = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.char_entity_id = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; + e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -499,6 +509,7 @@ class BaseTraderRepository { v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.char_entity_id)); v.push_back(std::to_string(e.char_zone_id)); + v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); auto results = db.QueryDatabase( @@ -537,6 +548,7 @@ class BaseTraderRepository { v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.char_entity_id)); v.push_back(std::to_string(e.char_zone_id)); + v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 6c6ab35d2b..b273638115 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -16,6 +16,7 @@ class TraderRepository : public BaseTraderRepository { struct DistinctTraders_Struct { uint32 trader_id; uint32 zone_id; + uint32 zone_instance_id; uint32 entity_id; std::string trader_name; }; @@ -35,7 +36,8 @@ class TraderRepository : public BaseTraderRepository { GetBazaarSearchResults( SharedDatabase &db, BazaarSearchCriteria_Struct search, - uint32 char_zone_id + uint32 char_zone_id, + int32 char_zone_instance_id ); static BulkTraders_Struct GetDistinctTraders(Database &db) @@ -44,7 +46,7 @@ class TraderRepository : public BaseTraderRepository { std::vector distinct_traders; auto results = db.QueryDatabase( - "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_entity_id, c.name " + "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " "JOIN character_data AS c ON t.char_id = c.id;" ); @@ -54,10 +56,11 @@ class TraderRepository : public BaseTraderRepository { for (auto row: results) { DistinctTraders_Struct e{}; - e.trader_id = Strings::ToInt(row[0]); - e.zone_id = Strings::ToInt(row[1]); - e.entity_id = Strings::ToInt(row[2]); - e.trader_name = row[3] ? row[3] : ""; + e.trader_id = Strings::ToInt(row[0]); + e.zone_id = Strings::ToInt(row[1]); + e.zone_instance_id = Strings::ToInt(row[2]); + e.entity_id = Strings::ToInt(row[3]); + e.trader_name = row[4] ? row[4] : ""; all_entries.name_length += e.trader_name.length() + 1; all_entries.traders.push_back(e); diff --git a/common/version.h b/common/version.h index a144157a78..232d3afc5f 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9285 +#define CURRENT_BINARY_DATABASE_VERSION 9286 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 73a333f828..4236a4851a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -912,10 +912,6 @@ void Client::CompleteConnect() CastToClient()->FastQueuePacket(&outapp); } - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { - SendBulkBazaarTraders(); - } - // TODO: load these states // We at least will set them to the correct state for now if (m_ClientVersionBit & EQ::versions::maskUFAndLater && GetPet()) { @@ -3921,6 +3917,10 @@ void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) SendBazaarWelcome(); break; } + case FirstOpenSearch: { + SendBulkBazaarTraders(); + break; + } default: { LogError("Malformed BazaarSearch_Struct packet received, ignoring\n"); } diff --git a/zone/trading.cpp b/zone/trading.cpp index f963f7ca10..4be42c099f 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1093,15 +1093,16 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) inst->SetPrice(in->item_cost[i]); TraderRepository::Trader trader_item{}; - trader_item.id = 0; - trader_item.char_entity_id = GetID(); - trader_item.char_id = CharacterID(); - trader_item.char_zone_id = GetZoneID(); - trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); - trader_item.item_cost = inst->GetPrice(); - trader_item.item_id = inst->GetID(); - trader_item.item_sn = in->serial_number[i]; - trader_item.slot_id = i; + trader_item.id = 0; + trader_item.char_entity_id = GetID(); + trader_item.char_id = CharacterID(); + trader_item.char_zone_id = GetZoneID(); + trader_item.char_zone_instance_id = GetInstanceID(); + trader_item.item_charges = inst->GetCharges() == 0 ? 1 : inst->GetCharges(); + trader_item.item_cost = inst->GetPrice(); + trader_item.item_id = inst->GetID(); + trader_item.item_sn = in->serial_number[i]; + trader_item.slot_id = i; if (inst->IsAugmented()) { auto augs = inst->GetAugmentIDs(); trader_item.aug_slot_1 = augs.at(0); @@ -1796,7 +1797,7 @@ void Client::SendBarterWelcome() void Client::DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria) { - auto results = Bazaar::GetSearchResults(database, search_criteria, GetZoneID()); + auto results = Bazaar::GetSearchResults(database, search_criteria, GetZoneID(), GetInstanceID()); if (results.empty()) { SendBazaarDone(GetID()); return; @@ -2940,9 +2941,11 @@ void Client::SendBecomeTrader(BazaarTraderBarterActions action, uint32 entity_id auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); auto data = (BecomeTrader_Struct *) outapp->pBuffer; - data->action = action; - data->entity_id = trader->GetID(); - data->trader_id = trader->CharacterID(); + data->action = action; + data->entity_id = trader->GetID(); + data->trader_id = trader->CharacterID(); + data->zone_id = trader->GetZoneID(); + data->zone_instance_id = trader->GetInstanceID(); strn0cpy(data->trader_name, trader->GetCleanName(), sizeof(data->trader_name)); QueuePacket(outapp); @@ -3088,14 +3091,15 @@ void Client::TraderPriceUpdate(const EQApplicationPacket *app) auto item_detail = FindTraderItemBySerialNumber(newgis->serial_number[i]); TraderRepository::Trader trader_item{}; - trader_item.id = 0; - trader_item.char_entity_id = GetID(); - trader_item.char_id = CharacterID(); - trader_item.char_zone_id = GetZoneID(); - trader_item.item_charges = newgis->charges[i]; - trader_item.item_cost = tpus->NewPrice; - trader_item.item_id = newgis->items[i]; - trader_item.item_sn = newgis->serial_number[i]; + trader_item.id = 0; + trader_item.char_entity_id = GetID(); + trader_item.char_id = CharacterID(); + trader_item.char_zone_id = GetZoneID(); + trader_item.char_zone_instance_id = GetInstanceID(); + trader_item.item_charges = newgis->charges[i]; + trader_item.item_cost = tpus->NewPrice; + trader_item.item_id = newgis->items[i]; + trader_item.item_sn = newgis->serial_number[i]; if (item_detail->IsAugmented()) { auto augs = item_detail->GetAugmentIDs(); trader_item.aug_slot_1 = augs.at(0); @@ -3224,7 +3228,8 @@ void Client::SendBulkBazaarTraders() VARSTRUCT_ENCODE_TYPE(uint32, bufptr, results.count); for (auto t : results.traders) { - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, t.zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, bufptr, t.zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, bufptr, t.zone_instance_id); VARSTRUCT_ENCODE_TYPE(uint32, bufptr, t.trader_id); VARSTRUCT_ENCODE_TYPE(uint32, bufptr, t.entity_id); VARSTRUCT_ENCODE_STRING(bufptr, t.trader_name.c_str()); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 7b6b870e21..cb1d6ffa3d 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3929,9 +3929,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) out->action = 0; } } - out->entity_id = in->entity_id; - out->zone_id = in->zone_id; - out->trader_id = in->trader_id; + + out->entity_id = in->entity_id; + out->zone_id = in->zone_id; + out->zone_instance_id = in->instance_id; + out->trader_id = in->trader_id; strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); c.second->QueuePacket(outapp); From 0f164c456e03715ccfe7ed79ac081ed50fa40890 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 12 Dec 2024 02:25:36 -0500 Subject: [PATCH 24/73] [Cleanup] Remove Unused Group Methods (#4559) --- zone/groups.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/groups.h b/zone/groups.h index 28954ee75c..9cd1a594f7 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -63,7 +63,6 @@ class Group : public GroupIDConsumer { void AddMember(const std::string& new_member_name); void SendUpdate(uint32 type,Mob* member); void SendLeadershipAAUpdate(); - void SendWorldGroup(uint32 zone_id,Mob* zoningmember); bool DelMemberOOZ(const char *Name); bool DelMember(Mob* oldmember,bool ignoresender = false); void DisbandGroup(bool joinraid = false); @@ -73,7 +72,6 @@ class Group : public GroupIDConsumer { bool IsGroupMember(Mob* c); bool IsGroupMember(const char* name); bool Process(); - bool IsGroup() { return true; } void SendGroupJoinOOZ(Mob* NewMember); void CastGroupSpell(Mob* caster,uint16 spellid); void SplitExp(ExpSource exp_source, const uint64 exp, Mob* other); From 3f3c0f2fda51de1eba392b660ac18c1d533ec081 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 12 Dec 2024 02:27:25 -0500 Subject: [PATCH 25/73] [Commands] Add #find ldon_theme Subcommand (#4564) --- common/emu_constants.cpp | 38 ++++------- common/emu_constants.h | 32 ++++++++- common/eq_constants.h | 18 ----- .../repositories/adventure_stats_repository.h | 10 +-- zone/client.cpp | 68 +++++++++---------- zone/client_packet.cpp | 50 +++++++------- zone/gm_commands/find.cpp | 2 + zone/gm_commands/find/class.cpp | 2 +- zone/gm_commands/find/ldon_theme.cpp | 65 ++++++++++++++++++ zone/gm_commands/set/adventure_points.cpp | 28 ++++---- zone/gm_commands/show/currencies.cpp | 4 +- zone/questmgr.cpp | 2 +- 12 files changed, 194 insertions(+), 125 deletions(-) create mode 100644 zone/gm_commands/find/ldon_theme.cpp diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index fd4983216f..670aeb9813 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -140,29 +140,6 @@ std::string EQ::constants::GetLanguageName(uint8 language_id) return EQ::constants::GetLanguageMap().find(language_id)->second; } -const std::map& EQ::constants::GetLDoNThemeMap() -{ - static const std::map ldon_theme_map = { - { LDoNThemes::Unused, "Unused" }, - { LDoNThemes::GUK, "Deepest Guk" }, - { LDoNThemes::MIR, "Miragul's Menagerie" }, - { LDoNThemes::MMC, "Mistmoore Catacombs" }, - { LDoNThemes::RUJ, "Rujarkian Hills" }, - { LDoNThemes::TAK, "Takish-Hiz" }, - }; - - return ldon_theme_map; -} - -std::string EQ::constants::GetLDoNThemeName(uint32 theme_id) -{ - if (!EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) { - return std::string(); - } - - return EQ::constants::GetLDoNThemeMap().find(theme_id)->second; -} - const std::map& EQ::constants::GetFlyModeMap() { static const std::map flymode_map = { @@ -459,3 +436,18 @@ bool ComparisonType::IsValid(uint8 type) { return comparison_types.find(type) != comparison_types.end(); } + +uint32 LDoNTheme::GetBitmask(uint32 theme_id) +{ + return IsValid(theme_id) ? ldon_theme_names[theme_id].second : LDoNTheme::UnusedBit; +} + +std::string LDoNTheme::GetName(uint32 theme_id) +{ + return IsValid(theme_id) ? ldon_theme_names[theme_id].first : "UNKNOWN LDON THEME"; +} + +bool LDoNTheme::IsValid(uint32 theme_id) +{ + return ldon_theme_names.find(theme_id) != ldon_theme_names.end(); +} diff --git a/common/emu_constants.h b/common/emu_constants.h index 83056cfb2e..7da8196d34 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -352,9 +352,6 @@ namespace EQ extern const std::map& GetLanguageMap(); std::string GetLanguageName(uint8 language_id); - extern const std::map& GetLDoNThemeMap(); - std::string GetLDoNThemeName(uint32 theme_id); - extern const std::map& GetFlyModeMap(); std::string GetFlyModeName(int8 flymode_id); @@ -751,6 +748,35 @@ static std::map stance_names = { { Stance::AEBurn, "AE Burn" } }; +namespace LDoNTheme { + constexpr uint32 Unused = 0; + constexpr uint32 GUK = 1; + constexpr uint32 MIR = 2; + constexpr uint32 MMC = 3; + constexpr uint32 RUJ = 4; + constexpr uint32 TAK = 5; + + constexpr uint32 UnusedBit = 0; + constexpr uint32 GUKBit = 1; + constexpr uint32 MIRBit = 2; + constexpr uint32 MMCBit = 4; + constexpr uint32 RUJBit = 8; + constexpr uint32 TAKBit = 16; + + uint32 GetBitmask(uint32 theme_id); + std::string GetName(uint32 theme_id); + bool IsValid(uint32 theme_id); +} + +static std::map> ldon_theme_names = { + { LDoNTheme::Unused, { "Unused", LDoNTheme::UnusedBit }, }, + { LDoNTheme::GUK, { "Deepest Guk", LDoNTheme::GUKBit }, }, + { LDoNTheme::MIR, { "Miragul's Menagerie", LDoNTheme::MIRBit }, }, + { LDoNTheme::MMC, { "Mistmoore Catacombs", LDoNTheme::MMCBit }, }, + { LDoNTheme::RUJ, { "Rujarkian Hills", LDoNTheme::RUJBit }, }, + { LDoNTheme::TAK, { "Takish-Hiz", LDoNTheme::TAKBit }, }, +}; + namespace PCNPCOnlyFlagType { constexpr int PC = 1; constexpr int NPC = 2; diff --git a/common/eq_constants.h b/common/eq_constants.h index 44beffd25b..fd9cd346a6 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -993,24 +993,6 @@ enum class DynamicZoneMemberStatus : uint8_t LinkDead }; -enum LDoNThemes { - Unused = 0, - GUK, - MIR, - MMC, - RUJ, - TAK -}; - -enum LDoNThemeBits { - UnusedBit = 0, - GUKBit = 1, - MIRBit = 2, - MMCBit = 4, - RUJBit = 8, - TAKBit = 16 -}; - enum StartZoneIndex { Odus = 0, Qeynos, diff --git a/common/repositories/adventure_stats_repository.h b/common/repositories/adventure_stats_repository.h index a4025ed2bc..5b93a7fe13 100644 --- a/common/repositories/adventure_stats_repository.h +++ b/common/repositories/adventure_stats_repository.h @@ -49,23 +49,23 @@ class AdventureStatsRepository: public BaseAdventureStatsRepository { std::string field; switch (theme_id) { - case LDoNThemes::GUK: { + case LDoNTheme::GUK: { field = "guk_"; break; } - case LDoNThemes::MIR: { + case LDoNTheme::MIR: { field = "mir_"; break; } - case LDoNThemes::MMC: { + case LDoNTheme::MMC: { field = "mmc_"; break; } - case LDoNThemes::RUJ: { + case LDoNTheme::RUJ: { field = "ruj_"; break; } - case LDoNThemes::TAK: { + case LDoNTheme::TAK: { field = "tak_"; break; } diff --git a/zone/client.cpp b/zone/client.cpp index d0a8d97b0b..920553f5ca 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -329,7 +329,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( adventure_stats_timer = nullptr; adventure_leaderboard_timer = nullptr; adv_data = nullptr; - adv_requested_theme = LDoNThemes::Unused; + adv_requested_theme = LDoNTheme::Unused; adv_requested_id = 0; adv_requested_member_count = 0; @@ -1469,7 +1469,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) bool is_loss = false; switch (theme_id) { - case LDoNThemes::Unused: { // No theme, so distribute evenly across all + case LDoNTheme::Unused: { // No theme, so distribute evenly across all int split_points = (points / 5); int guk_points = (split_points + (points % 5)); @@ -1522,12 +1522,12 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) points -= split_points; if (split_points != 0) { // if anything left, recursively loop thru again - UpdateLDoNPoints(LDoNThemes::Unused, split_points); + UpdateLDoNPoints(LDoNTheme::Unused, split_points); } break; } - case LDoNThemes::GUK: { + case LDoNTheme::GUK: { if (points < 0) { if (m_pp.ldon_points_guk < (0 - points)) { return false; @@ -1539,7 +1539,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) m_pp.ldon_points_guk += points; break; } - case LDoNThemes::MIR: { + case LDoNTheme::MIR: { if (points < 0) { if (m_pp.ldon_points_mir < (0 - points)) { return false; @@ -1551,7 +1551,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) m_pp.ldon_points_mir += points; break; } - case LDoNThemes::MMC: { + case LDoNTheme::MMC: { if (points < 0) { if (m_pp.ldon_points_mmc < (0 - points)) { return false; @@ -1563,7 +1563,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) m_pp.ldon_points_mmc += points; break; } - case LDoNThemes::RUJ: { + case LDoNTheme::RUJ: { if (points < 0) { if (m_pp.ldon_points_ruj < (0 - points)) { return false; @@ -1575,7 +1575,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) m_pp.ldon_points_ruj += points; break; } - case LDoNThemes::TAK: { + case LDoNTheme::TAK: { if (points < 0) { if (m_pp.ldon_points_tak < (0 - points)) { return false; @@ -1623,23 +1623,23 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) void Client::SetLDoNPoints(uint32 theme_id, uint32 points) { switch (theme_id) { - case LDoNThemes::GUK: { + case LDoNTheme::GUK: { m_pp.ldon_points_guk = points; break; } - case LDoNThemes::MIR: { + case LDoNTheme::MIR: { m_pp.ldon_points_mir = points; break; } - case LDoNThemes::MMC: { + case LDoNTheme::MMC: { m_pp.ldon_points_mmc = points; break; } - case LDoNThemes::RUJ: { + case LDoNTheme::RUJ: { m_pp.ldon_points_ruj = points; break; } - case LDoNThemes::TAK: { + case LDoNTheme::TAK: { m_pp.ldon_points_tak = points; break; } @@ -5676,15 +5676,15 @@ uint32 Client::GetLDoNPointsTheme(uint32 t) { switch(t) { - case LDoNThemes::GUK: + case LDoNTheme::GUK: return m_pp.ldon_points_guk; - case LDoNThemes::MIR: + case LDoNTheme::MIR: return m_pp.ldon_points_mir; - case LDoNThemes::MMC: + case LDoNTheme::MMC: return m_pp.ldon_points_mmc; - case LDoNThemes::RUJ: + case LDoNTheme::RUJ: return m_pp.ldon_points_ruj; - case LDoNThemes::TAK: + case LDoNTheme::TAK: return m_pp.ldon_points_tak; default: return 0; @@ -5695,15 +5695,15 @@ uint32 Client::GetLDoNWinsTheme(uint32 t) { switch(t) { - case LDoNThemes::GUK: + case LDoNTheme::GUK: return m_pp.ldon_wins_guk; - case LDoNThemes::MIR: + case LDoNTheme::MIR: return m_pp.ldon_wins_mir; - case LDoNThemes::MMC: + case LDoNTheme::MMC: return m_pp.ldon_wins_mmc; - case LDoNThemes::RUJ: + case LDoNTheme::RUJ: return m_pp.ldon_wins_ruj; - case LDoNThemes::TAK: + case LDoNTheme::TAK: return m_pp.ldon_wins_tak; default: return 0; @@ -5714,15 +5714,15 @@ uint32 Client::GetLDoNLossesTheme(uint32 t) { switch(t) { - case LDoNThemes::GUK: + case LDoNTheme::GUK: return m_pp.ldon_losses_guk; - case LDoNThemes::MIR: + case LDoNTheme::MIR: return m_pp.ldon_losses_mir; - case LDoNThemes::MMC: + case LDoNTheme::MMC: return m_pp.ldon_losses_mmc; - case LDoNThemes::RUJ: + case LDoNTheme::RUJ: return m_pp.ldon_losses_ruj; - case LDoNThemes::TAK: + case LDoNTheme::TAK: return m_pp.ldon_losses_tak; default: return 0; @@ -5731,35 +5731,35 @@ uint32 Client::GetLDoNLossesTheme(uint32 t) void Client::UpdateLDoNWinLoss(uint32 theme_id, bool win, bool remove) { switch (theme_id) { - case LDoNThemes::GUK: + case LDoNTheme::GUK: if (win) { m_pp.ldon_wins_guk += (remove ? -1 : 1); } else { m_pp.ldon_losses_guk += (remove ? -1 : 1); } break; - case LDoNThemes::MIR: + case LDoNTheme::MIR: if (win) { m_pp.ldon_wins_mir += (remove ? -1 : 1); } else { m_pp.ldon_losses_mir += (remove ? -1 : 1); } break; - case LDoNThemes::MMC: + case LDoNTheme::MMC: if (win) { m_pp.ldon_wins_mmc += (remove ? -1 : 1); } else { m_pp.ldon_losses_mmc += (remove ? -1 : 1); } break; - case LDoNThemes::RUJ: + case LDoNTheme::RUJ: if (win) { m_pp.ldon_wins_ruj += (remove ? -1 : 1); } else { m_pp.ldon_losses_ruj += (remove ? -1 : 1); } break; - case LDoNThemes::TAK: + case LDoNTheme::TAK: if (win) { m_pp.ldon_wins_tak += (remove ? -1 : 1); } else { @@ -6227,7 +6227,7 @@ void Client::NewAdventure(int id, int theme, const char *text, int member_count, void Client::ClearPendingAdventureData() { adv_requested_id = 0; - adv_requested_theme = LDoNThemes::Unused; + adv_requested_theme = LDoNTheme::Unused; safe_delete_array(adv_requested_data); adv_requested_member_count = 0; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4236a4851a..4afe679356 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2019,38 +2019,38 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) return; } - if (item->LDoNTheme <= LDoNThemeBits::TAKBit) { + if (item->LDoNTheme <= LDoNTheme::TAKBit) { uint32 ldon_theme; - if (item->LDoNTheme & LDoNThemeBits::TAKBit) { + if (item->LDoNTheme & LDoNTheme::TAKBit) { if (m_pp.ldon_points_tak < item_cost) { cannot_afford = true; - ldon_theme = LDoNThemes::TAK; + ldon_theme = LDoNTheme::TAK; } - } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { + } else if (item->LDoNTheme & LDoNTheme::RUJBit) { if (m_pp.ldon_points_ruj < item_cost) { cannot_afford = true; - ldon_theme = LDoNThemes::RUJ; + ldon_theme = LDoNTheme::RUJ; } - } else if (item->LDoNTheme & LDoNThemeBits::MMCBit) { + } else if (item->LDoNTheme & LDoNTheme::MMCBit) { if (m_pp.ldon_points_mmc < item_cost) { cannot_afford = true; - ldon_theme = LDoNThemes::MMC; + ldon_theme = LDoNTheme::MMC; } - } else if (item->LDoNTheme & LDoNThemeBits::MIRBit) { + } else if (item->LDoNTheme & LDoNTheme::MIRBit) { if (m_pp.ldon_points_mir < item_cost) { cannot_afford = true; - ldon_theme = LDoNThemes::MIR; + ldon_theme = LDoNTheme::MIR; } - } else if (item->LDoNTheme & LDoNThemeBits::GUKBit) { + } else if (item->LDoNTheme & LDoNTheme::GUKBit) { if (m_pp.ldon_points_guk < item_cost) { cannot_afford = true; - ldon_theme = LDoNThemes::GUK; + ldon_theme = LDoNTheme::GUK; } } merchant_type = fmt::format( "{} Point{}", - EQ::constants::GetLDoNThemeName(ldon_theme), + LDoNTheme::GetName(ldon_theme), item_cost != 1 ? "s" : "" ); } @@ -2194,19 +2194,19 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) item = database.GetItem(ml.item); if (item) { - uint32 theme = LDoNThemes::Unused; - if (item->LDoNTheme > LDoNThemeBits::TAKBit) { - theme = LDoNThemes::Unused; - } else if (item->LDoNTheme & LDoNThemeBits::TAKBit) { - theme = LDoNThemes::TAK; - } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { - theme = LDoNThemes::RUJ; - } else if (item->LDoNTheme & LDoNThemeBits::MMCBit) { - theme = LDoNThemes::MMC; - } else if (item->LDoNTheme & LDoNThemeBits::MIRBit) { - theme = LDoNThemes::MIR; - } else if (item->LDoNTheme & LDoNThemeBits::GUKBit) { - theme = LDoNThemes::GUK; + uint32 theme = LDoNTheme::Unused; + if (item->LDoNTheme > LDoNTheme::TAKBit) { + theme = LDoNTheme::Unused; + } else if (item->LDoNTheme & LDoNTheme::TAKBit) { + theme = LDoNTheme::TAK; + } else if (item->LDoNTheme & LDoNTheme::RUJBit) { + theme = LDoNTheme::RUJ; + } else if (item->LDoNTheme & LDoNTheme::MMCBit) { + theme = LDoNTheme::MMC; + } else if (item->LDoNTheme & LDoNTheme::MIRBit) { + theme = LDoNTheme::MIR; + } else if (item->LDoNTheme & LDoNTheme::GUKBit) { + theme = LDoNTheme::GUK; } ss << "^" << item->Name << "|"; ss << item->ID << "|"; diff --git a/zone/gm_commands/find.cpp b/zone/gm_commands/find.cpp index c2e1ee5593..84f33ac0d0 100644 --- a/zone/gm_commands/find.cpp +++ b/zone/gm_commands/find.cpp @@ -11,6 +11,7 @@ #include "find/faction.cpp" #include "find/item.cpp" #include "find/language.cpp" +#include "find/ldon_theme.cpp" #include "find/npctype.cpp" #include "find/object_type.cpp" #include "find/race.cpp" @@ -47,6 +48,7 @@ void command_find(Client *c, const Seperator *sep) Cmd{.cmd = "faction", .u = "faction [Search Criteria]", .fn = FindFaction, .a = {"#findfaction"}}, Cmd{.cmd = "item", .u = "item [Search Criteria]", .fn = FindItem, .a = {"#fi", "#finditem"}}, Cmd{.cmd = "language", .u = "language [Search Criteria]", .fn = FindLanguage, .a = {"#findlanguage"}}, + Cmd{.cmd = "ldon_theme", .u = "ldon_theme [Search Criteria]", .fn = FindLDoNTheme, .a = {"#findldontheme"}}, Cmd{ .cmd = "npctype", .u = "npctype [Search Criteria]", .fn = FindNPCType, .a = { "#fn", diff --git a/zone/gm_commands/find/class.cpp b/zone/gm_commands/find/class.cpp index 2ce3b17079..a89211e774 100644 --- a/zone/gm_commands/find/class.cpp +++ b/zone/gm_commands/find/class.cpp @@ -57,7 +57,7 @@ void FindClass(Client *c, const Seperator *sep) ( IsPlayerClass(class_id) ? fmt::format( - " | ({})", + " ({})", Strings::Commify(GetPlayerClassBit(class_id)) ) : "" diff --git a/zone/gm_commands/find/ldon_theme.cpp b/zone/gm_commands/find/ldon_theme.cpp new file mode 100644 index 0000000000..aa7231297a --- /dev/null +++ b/zone/gm_commands/find/ldon_theme.cpp @@ -0,0 +1,65 @@ +#include "../../client.h" + +void FindLDoNTheme(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(2)) { + const uint32 theme_id = Strings::ToUnsignedInt(sep->arg[2]); + if (LDoNTheme::IsValid(theme_id)) { + c->Message( + Chat::White, + fmt::format( + "Theme {} | {} ({})", + theme_id, + LDoNTheme::GetName(theme_id), + LDoNTheme::GetBitmask(theme_id) + ).c_str() + ); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "Theme ID {} was not found.", + theme_id + ).c_str() + ); + + return; + } + + const std::string& search_criteria = Strings::ToLower(sep->argplus[2]); + + uint32 found_count = 0; + + for (const auto& l : ldon_theme_names) { + const std::string& ldon_theme_name_lower = Strings::ToLower(l.second.first); + if (!Strings::Contains(ldon_theme_name_lower, search_criteria)) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Theme {} | {} ({})", + l.first, + l.second.first, + l.second.second + ).c_str() + ); + + found_count++; + } + + c->Message( + Chat::White, + fmt::format( + "{} Theme{} found matching '{}'.", + found_count, + found_count != 1 ? "s" : "", + sep->argplus[2] + ).c_str() + ); +} + diff --git a/zone/gm_commands/set/adventure_points.cpp b/zone/gm_commands/set/adventure_points.cpp index 3b49d06ec7..55465039a6 100755 --- a/zone/gm_commands/set/adventure_points.cpp +++ b/zone/gm_commands/set/adventure_points.cpp @@ -3,20 +3,21 @@ void SetAdventurePoints(Client *c, const Seperator *sep) { - const auto arguments = sep->argnum; + const uint16 arguments = sep->argnum; if (arguments < 3 || !sep->IsNumber(2) || !sep->IsNumber(3)) { c->Message(Chat::White, "Usage: #set adventure_points [Theme] [Points]"); c->Message(Chat::White, "Valid themes are as follows:"); - for (const auto& e : EQ::constants::GetLDoNThemeMap()) { - if (e.first != LDoNThemes::Unused) { + for (const auto& e : ldon_theme_names) { + if (e.first != LDoNTheme::Unused) { c->Message( Chat::White, fmt::format( - "Theme {} | {}", + "Theme {} | {} ({})", e.first, - e.second + e.second.first, + e.second.second ).c_str() ); } @@ -25,25 +26,26 @@ void SetAdventurePoints(Client *c, const Seperator *sep) return; } - auto t = c; + Client* t = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { t = c->GetTarget()->CastToClient(); } const uint32 theme_id = Strings::ToUnsignedInt(sep->arg[2]); - const uint32 points = Strings::ToUnsignedInt(sep->arg[3]); + const uint32 points = Strings::ToUnsignedInt(sep->arg[3]); - if (!EQ::ValueWithin(theme_id, LDoNThemes::GUK, LDoNThemes::TAK)) { + if (!LDoNTheme::IsValid(theme_id)) { c->Message(Chat::White, "Valid themes are as follows:"); - for (const auto& e : EQ::constants::GetLDoNThemeMap()) { - if (e.first != LDoNThemes::Unused) { + for (const auto& e : ldon_theme_names) { + if (e.first != LDoNTheme::Unused) { c->Message( Chat::White, fmt::format( - "Theme {} | {}", + "Theme {} | {} ({})", e.first, - e.second + e.second.first, + e.second.second ).c_str() ); } @@ -56,7 +58,7 @@ void SetAdventurePoints(Client *c, const Seperator *sep) Chat::White, fmt::format( "Set {} Points to {} for {}.", - EQ::constants::GetLDoNThemeName(theme_id), + LDoNTheme::GetName(theme_id), Strings::Commify(points), c->GetTargetDescription(t) ).c_str() diff --git a/zone/gm_commands/show/currencies.cpp b/zone/gm_commands/show/currencies.cpp index d633958229..4f4d3cfdac 100644 --- a/zone/gm_commands/show/currencies.cpp +++ b/zone/gm_commands/show/currencies.cpp @@ -89,11 +89,11 @@ void ShowCurrencies(Client *c, const Seperator *sep) } } - for (const auto& l : EQ::constants::GetLDoNThemeMap()) { + for (const auto& l : ldon_theme_names) { const uint32 ldon_currency_value = t->GetLDoNPointsTheme(l.first); if (ldon_currency_value) { currency_table += DialogueWindow::TableRow( - DialogueWindow::TableCell(l.second) + + DialogueWindow::TableCell(l.second.first) + DialogueWindow::TableCell(Strings::Commify(ldon_currency_value)) ); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index e1a859addd..ad29a76a94 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1287,7 +1287,7 @@ std::string QuestManager::getskillname(int skill_id) { } std::string QuestManager::getldonthemename(uint32 theme_id) { - return EQ::constants::GetLDoNThemeName(theme_id); + return LDoNTheme::GetName(theme_id); } std::string QuestManager::getfactionname(int faction_id) { From 3b779ef301bb49525bac15cb96c584e8845b58dc Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Wed, 11 Dec 2024 23:47:50 -0800 Subject: [PATCH 26/73] [Rules] Add rules for requiring custom files from client (#4561) * rules for enabling requiring custom files * shorten default * variable name * check account status for enforcing client key * rule for custom files admin level --------- Co-authored-by: Paul Johnson --- common/ruletypes.h | 3 +++ world/client.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index a4a1994004..60560345ee 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -340,6 +340,9 @@ RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be u RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame") RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag") RULE_STRING(World, SupportedClients, "", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") +RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1") +RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files") +RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified") RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/world/client.cpp b/world/client.cpp index a6f5cfb5f4..1c4b61632c 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -541,6 +541,17 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) skip_char_info = true; } } + const auto& custom_files_key = RuleS(World, CustomFilesKey); + if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) { + // Modified clients can utilize this unused block in login_info to send custom payloads on login + // which indicates they are using custom client files with the correct version, based on key payload. + const auto client_key = std::string(reinterpret_cast(login_info->unknown064)); + if (custom_files_key != client_key) { + std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) ); + SendUnsupportedClientPacket(message); + skip_char_info = true; + } + } if (!skip_char_info) { SendExpansionInfo(); From e258aaa068beadb9782eb933ead5768a2449d93e Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:39:38 -0500 Subject: [PATCH 27/73] [Bug Fix] Allow Items in ROF2 to Stack to 32,767 (#4556) * [Bug Fix] Allow Items in ROF2 to Stack to 32,767 * Update rof2.cpp --- common/patches/rof2.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 0406225baa..38106fd2a6 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -6365,9 +6365,18 @@ namespace RoF2 //sprintf(hdr.unknown000, "06e0002Y1W00"); strn0cpy(hdr.unknown000, fmt::format("{:016}\0", inst->GetSerialNumber()).c_str(),sizeof(hdr.unknown000)); - hdr.stacksize = - item->ID == PARCEL_MONEY_ITEM_ID ? inst->GetPrice() : (inst->IsStackable() ? ((inst->GetCharges() > 1000) - ? 0xFFFFFFFF : inst->GetCharges()) : 1); + hdr.stacksize = 1; + + if (item->ID == PARCEL_MONEY_ITEM_ID) { + hdr.stacksize = inst->GetPrice(); + } else if (inst->IsStackable()) { + if (inst->GetCharges() > std::numeric_limits::max()) { + hdr.stacksize = std::numeric_limits::max(); + } else { + hdr.stacksize = inst->GetCharges(); + } + } + hdr.unknown004 = 0; structs::InventorySlot_Struct slot_id{}; From 77793f364eeb0e610564cf1af22ef7552ea53381 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:55:33 -0500 Subject: [PATCH 28/73] [Commands] Add #find bot Subcommand (#4563) * [Commands] Add #find bot Subcommand * Update find.cpp * Update find.cpp --- zone/gm_commands/find.cpp | 11 ++++ zone/gm_commands/find/bot.cpp | 95 +++++++++++++++++++++++++++++ zone/gm_commands/find/character.cpp | 2 +- 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 zone/gm_commands/find/bot.cpp diff --git a/zone/gm_commands/find.cpp b/zone/gm_commands/find.cpp index 84f33ac0d0..0bdbcbe709 100644 --- a/zone/gm_commands/find.cpp +++ b/zone/gm_commands/find.cpp @@ -1,6 +1,7 @@ #include "../client.h" #include "find/aa.cpp" #include "find/body_type.cpp" +#include "find/bot.cpp" #include "find/bug_category.cpp" #include "find/character.cpp" #include "find/class.cpp" @@ -67,6 +68,16 @@ void command_find(Client *c, const Seperator *sep) Cmd{.cmd = "zone", .u = "zone [Search Criteria]", .fn = FindZone, .a = {"#fz", "#findzone"}}, }; + if (RuleB(Bots, Enabled)) { + commands.emplace_back( + Cmd{.cmd = "bot", .u = "bot [Search Criteria]", .fn = FindBot, .a = {"#findbot"}} + ); + + std::sort(commands.begin(), commands.end(), [](const Cmd& a, const Cmd& b) { + return a.cmd < b.cmd; + }); + } + // Check for arguments const auto arguments = sep->argnum; if (!arguments) { diff --git a/zone/gm_commands/find/bot.cpp b/zone/gm_commands/find/bot.cpp new file mode 100644 index 0000000000..061d4c4287 --- /dev/null +++ b/zone/gm_commands/find/bot.cpp @@ -0,0 +1,95 @@ +#include "../../client.h" +#include "../../common/repositories/bot_data_repository.h" + +void FindBot(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(2)) { + const auto bot_id = Strings::ToUnsignedInt(sep->arg[2]); + + const auto& e = BotDataRepository::FindOne(content_db, bot_id); + if (!e.bot_id) { + c->Message( + Chat::White, + fmt::format( + "Bot ID {} does not exist or is invalid.", + bot_id + ).c_str() + ); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "Bot ID {} | {}", + bot_id, + e.name + ).c_str() + ); + + return; + } + + const auto search_criteria = Strings::ToLower(sep->argplus[2]); + + const auto& l = BotDataRepository::GetWhere( + content_db, + fmt::format( + "LOWER(`name`) LIKE '%%{}%%' AND `name` NOT LIKE '%-deleted-%' ORDER BY `bot_id` ASC LIMIT 50", + search_criteria + ) + ); + + if (l.empty()) { + c->Message( + Chat::White, + fmt::format( + "No bots found matching '{}'.", + sep->argplus[2] + ).c_str() + ); + } + + auto found_count = 0; + + for (const auto& e : l) { + c->Message( + Chat::White, + fmt::format( + "Bot ID {} | {}", + Strings::Commify(e.bot_id), + e.name + ).c_str() + ); + + found_count++; + + if (found_count == 50) { + break; + } + } + + if (found_count == 50) { + c->Message( + Chat::White, + fmt::format( + "50 Bots found matching '{}', max reached.", + sep->argplus[2] + ).c_str() + ); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "{} Bot{} found matching '{}'.", + found_count, + found_count != 1 ? "s" : "", + sep->argplus[2] + ).c_str() + ); +} + diff --git a/zone/gm_commands/find/character.cpp b/zone/gm_commands/find/character.cpp index fcd1b7c922..3225cc2c8a 100644 --- a/zone/gm_commands/find/character.cpp +++ b/zone/gm_commands/find/character.cpp @@ -36,7 +36,7 @@ void FindCharacter(Client *c, const Seperator *sep) const auto& l = CharacterDataRepository::GetWhere( content_db, fmt::format( - "LOWER(`name`) LIKE '%%{}%%' ORDER BY `id` ASC LIMIT 50", + "LOWER(`name`) LIKE '%%{}%%' AND `name` NOT LIKE '%-deleted-%' ORDER BY `id` ASC LIMIT 50", search_criteria ) ); From ea5cbdd0d05f2243a4e357f70e6082785c030a38 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:44:41 -0500 Subject: [PATCH 29/73] Update shareddb.cpp --- common/shareddb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index d9f57f8bed..ba8a20914c 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -447,7 +447,7 @@ bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) return SharedbankRepository::DeleteWhere( *this, fmt::format( - "`account_id` = {} AND `slotid` BETWEEN {} AND {}", + "`account_id` = {} AND `slot_id` BETWEEN {} AND {}", account_id, base_slot_id, base_slot_id + (EQ::invbag::SLOT_COUNT - 1) From 62c742a7e94481a14b05e33808e7212043b09609 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 14 Dec 2024 13:25:01 -0500 Subject: [PATCH 30/73] Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 69558cefbe..771c772360 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5831,7 +5831,30 @@ UPDATE `inventory` SET `slot_id` = ((`slot_id` - 321) + 5410) WHERE `slot_id` BE UPDATE `inventory` SET `slot_id` = ((`slot_id` - 331) + 5610) WHERE `slot_id` BETWEEN 331 AND 340; -- Bag 9 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 341) + 5810) WHERE `slot_id` BETWEEN 341 AND 350; -- Bag 10 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 351) + 6010) WHERE `slot_id` BETWEEN 351 AND 360; -- Cursor Bag -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2270; -- Bank Bags +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2040; -- Bank Bag 1 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2041) + 6410) WHERE `slot_id` BETWEEN 2041 AND 2050; -- Bank Bag 2 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2051) + 6610) WHERE `slot_id` BETWEEN 2051 AND 2060; -- Bank Bag 3 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2061) + 6810) WHERE `slot_id` BETWEEN 2061 AND 2070; -- Bank Bag 4 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2071) + 7010) WHERE `slot_id` BETWEEN 2071 AND 2080; -- Bank Bag 5 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2081) + 7210) WHERE `slot_id` BETWEEN 2081 AND 2090; -- Bank Bag 6 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2091) + 7410) WHERE `slot_id` BETWEEN 2091 AND 2100; -- Bank Bag 7 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2101) + 7610) WHERE `slot_id` BETWEEN 2101 AND 2110; -- Bank Bag 8 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2111) + 7810) WHERE `slot_id` BETWEEN 2111 AND 2120; -- Bank Bag 9 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2121) + 8010) WHERE `slot_id` BETWEEN 2121 AND 2130; -- Bank Bag 10 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2131) + 8210) WHERE `slot_id` BETWEEN 2131 AND 2140; -- Bank Bag 11 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2141) + 8410) WHERE `slot_id` BETWEEN 2141 AND 2150; -- Bank Bag 12 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2151) + 8610) WHERE `slot_id` BETWEEN 2151 AND 2160; -- Bank Bag 13 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2161) + 8810) WHERE `slot_id` BETWEEN 2161 AND 2170; -- Bank Bag 14 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2171) + 9010) WHERE `slot_id` BETWEEN 2171 AND 2180; -- Bank Bag 15 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2181) + 9210) WHERE `slot_id` BETWEEN 2181 AND 2190; -- Bank Bag 16 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2191) + 9410) WHERE `slot_id` BETWEEN 2191 AND 2200; -- Bank Bag 17 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2201) + 9610) WHERE `slot_id` BETWEEN 2201 AND 2210; -- Bank Bag 18 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2211) + 9810) WHERE `slot_id` BETWEEN 2211 AND 2220; -- Bank Bag 19 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2221) + 10010) WHERE `slot_id` BETWEEN 2221 AND 2230; -- Bank Bag 20 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2231) + 10210) WHERE `slot_id` BETWEEN 2231 AND 2240; -- Bank Bag 21 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2241) + 10410) WHERE `slot_id` BETWEEN 2241 AND 2250; -- Bank Bag 22 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` BETWEEN 2251 AND 2260; -- Bank Bag 23 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags )" }, From 0504426dca05e78d19cbfeb7e27a85076cf1a837 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:04:56 -0500 Subject: [PATCH 31/73] Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 771c772360..bb7ab7f740 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5855,7 +5855,8 @@ UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2231) + 10210) WHERE `slot_id` UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2241) + 10410) WHERE `slot_id` BETWEEN 2241 AND 2250; -- Bank Bag 22 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` BETWEEN 2251 AND 2260; -- Bank Bag 23 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 )" }, ManifestEntry{ From 4493ebebabbefef404404f9b487241e65e5e8300 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:27:43 -0400 Subject: [PATCH 32/73] [Bug Fix] Resolve a client crash when logging in or zoning (#4572) --- zone/worldserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index cb1d6ffa3d..143b690a34 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3936,7 +3936,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) out->trader_id = in->trader_id; strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); - c.second->QueuePacket(outapp); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); safe_delete(outapp); } if (zone && zone->GetZoneID() == Zones::BAZAAR && in->instance_id == zone->GetInstanceID()) { From 8bd1d51674b0aae3ba1a751ee8bc34b4bded61bd Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 15 Dec 2024 14:46:02 -0500 Subject: [PATCH 33/73] Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index bb7ab7f740..bade8f6ea1 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5855,8 +5855,8 @@ UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2231) + 10210) WHERE `slot_id` UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2241) + 10410) WHERE `slot_id` BETWEEN 2241 AND 2250; -- Bank Bag 22 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` BETWEEN 2251 AND 2260; -- Bank Bag 23 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 +UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 +UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 )" }, ManifestEntry{ From 8a7d5e72cb527e74013f4a7d87b66d55e8d74e63 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:44:16 -0600 Subject: [PATCH 34/73] [Filesystem] Path Manager Improvements (#4557) * [Filesystem] Path Manager Improvements * Update path_manager.cpp * Use native fs path building syntax --- common/eqemu_logsys.cpp | 7 +++ common/path_manager.cpp | 132 ++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index c4c14f82f5..a6a3e283dc 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -25,6 +25,8 @@ #include "repositories/discord_webhooks_repository.h" #include "repositories/logsys_categories_repository.h" #include "termcolor/rang.hpp" +#include "path_manager.h" +#include "file.h" #include #include @@ -532,6 +534,11 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name) { EQEmuLogSys::CloseFileLogs(); + if (!File::Exists(path.GetLogPath())) { + LogInfo("Logs directory not found, creating [{}]", path.GetLogPath()); + File::Makedir(path.GetLogPath()); + } + /** * When loading settings, we must have been given a reason in category based logging to output to a file in order to even create or open one... */ diff --git a/common/path_manager.cpp b/common/path_manager.cpp index 452722c844..b767b80030 100644 --- a/common/path_manager.cpp +++ b/common/path_manager.cpp @@ -5,31 +5,19 @@ #include "strings.h" #include -namespace fs = std::filesystem; - -inline std::string striptrailingslash(const std::string &file_path) -{ - if (file_path.back() == '/' || file_path.back() == '\\') { - return file_path.substr(0, file_path.length() - 1); - } - return file_path; -} +namespace fs = std::filesystem; void PathManager::LoadPaths() { m_server_path = File::FindEqemuConfigPath(); - if (!m_server_path.empty()) { - std::filesystem::current_path(m_server_path); - } - if (m_server_path.empty()) { LogInfo("Failed to load server path"); return; } - LogInfo("server [{}]", m_server_path); + std::filesystem::current_path(m_server_path); if (!EQEmuConfig::LoadConfig()) { LogError("Failed to load eqemu config"); @@ -38,66 +26,64 @@ void PathManager::LoadPaths() const auto c = EQEmuConfig::get(); - // maps - if (File::Exists(fs::path{m_server_path + "/" + c->MapDir}.string())) { - m_maps_path = fs::relative(fs::path{m_server_path + "/" + c->MapDir}).string(); - } - else if (File::Exists(fs::path{m_server_path + "/maps"}.string())) { - m_maps_path = fs::relative(fs::path{m_server_path + "/maps"}).string(); - } - else if (File::Exists(fs::path{m_server_path + "/Maps"}.string())) { - m_maps_path = fs::relative(fs::path{m_server_path + "/Maps"}).string(); - } - - // quests - if (File::Exists(fs::path{m_server_path + "/" + c->QuestDir}.string())) { - m_quests_path = fs::relative(fs::path{m_server_path + "/" + c->QuestDir}).string(); - } - - // plugins - if (File::Exists(fs::path{m_server_path + "/" + c->PluginDir}.string())) { - m_plugins_path = fs::relative(fs::path{m_server_path + "/" + c->PluginDir}).string(); + auto resolve_path = [&](const std::string& dir, const std::vector& fallback_dirs = {}) -> std::string { + // relative + if (File::Exists((fs::path{m_server_path} / dir).string())) { + return fs::relative(fs::path{m_server_path} / dir).lexically_normal().string(); + } + + // absolute + if (File::Exists(fs::path{dir}.string())) { + return fs::absolute(fs::path{dir}).string(); + } + + // fallback search options if specified + for (const auto& fallback : fallback_dirs) { + if (File::Exists((fs::path{m_server_path} / fallback).string())) { + return fs::relative(fs::path{m_server_path} / fallback).lexically_normal().string(); + } + } + + // if all else fails, just set it to the config value + return dir; + }; + + m_maps_path = resolve_path(c->MapDir, {"maps", "Maps"}); + m_quests_path = resolve_path(c->QuestDir); + m_plugins_path = resolve_path(c->PluginDir); + m_lua_modules_path = resolve_path(c->LuaModuleDir); + m_lua_mods_path = resolve_path("mods"); + m_patch_path = resolve_path(c->PatchDir); + m_opcode_path = resolve_path(c->OpcodeDir); + m_shared_memory_path = resolve_path(c->SharedMemDir); + m_log_path = resolve_path(c->LogDir, {"logs"}); + + // Log all paths in a loop + std::vector> paths = { + {"server", m_server_path}, + {"logs", m_log_path}, + {"lua mods", m_lua_mods_path}, + {"lua_modules", m_lua_modules_path}, + {"maps", m_maps_path}, + {"patches", m_patch_path}, + {"opcode", m_opcode_path}, + {"plugins", m_plugins_path}, + {"quests", m_quests_path}, + {"shared_memory", m_shared_memory_path} + }; + + constexpr int name_width = 15; + constexpr int path_width = 0; + constexpr int break_length = 70; + + std::cout << std::endl; + LogInfo("{}", Strings::Repeat("-", break_length)); + for (const auto& [name, in_path] : paths) { + if (!in_path.empty()) { + LogInfo("{:>{}} > [{:<{}}]", name, name_width, in_path, path_width); + } } - - // lua_modules - if (File::Exists(fs::path{m_server_path + "/" + c->LuaModuleDir}.string())) { - m_lua_modules_path = fs::relative(fs::path{m_server_path + "/" + c->LuaModuleDir}).string(); - } - - // lua mods - if (File::Exists(fs::path{ m_server_path + "/mods" }.string())) { - m_lua_mods_path = fs::relative(fs::path{ m_server_path + "/mods" }).string(); - } - - // patches - if (File::Exists(fs::path{m_server_path + "/" + c->PatchDir}.string())) { - m_patch_path = fs::relative(fs::path{m_server_path + "/" + c->PatchDir}).string(); - } - - // patches - if (File::Exists(fs::path{ m_server_path + "/" + c->OpcodeDir }.string())) { - m_opcode_path = fs::relative(fs::path{ m_server_path + "/" + c->OpcodeDir }).string(); - } - - // shared_memory_path - if (File::Exists(fs::path{m_server_path + "/" + c->SharedMemDir}.string())) { - m_shared_memory_path = fs::relative(fs::path{ m_server_path + "/" + c->SharedMemDir }).string(); - } - - // logging path - if (File::Exists(fs::path{m_server_path + "/" + c->LogDir}.string())) { - m_log_path = fs::relative(fs::path{m_server_path + "/" + c->LogDir}).string(); - } - - LogInfo("logs path [{}]", m_log_path); - LogInfo("lua mods path [{}]", m_lua_mods_path); - LogInfo("lua_modules path [{}]", m_lua_modules_path); - LogInfo("maps path [{}]", m_maps_path); - LogInfo("patches path [{}]", m_patch_path); - LogInfo("opcode path [{}]", m_opcode_path); - LogInfo("plugins path [{}]", m_plugins_path); - LogInfo("quests path [{}]", m_quests_path); - LogInfo("shared_memory path [{}]", m_shared_memory_path); + LogInfo("{}", Strings::Repeat("-", break_length)); } const std::string &PathManager::GetServerPath() const From 20ff3250133a281014fe7c13a9af6fe32441aa65 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:44:51 -0400 Subject: [PATCH 35/73] [Fix] Repair a LoadNPCEmote MemoryLeak (#4586) --- zone/zone.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 0280ee8f52..f02be2e2bd 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1100,7 +1100,12 @@ Zone::~Zone() { if (worldserver.Connected()) { worldserver.SetZoneData(0); } + + for (auto &e: npc_emote_list) { + safe_delete(e); + } npc_emote_list.clear(); + zone_point_list.Clear(); entity_list.Clear(); parse->ReloadQuests(); @@ -1257,7 +1262,6 @@ void Zone::ReloadStaticData() { LoadVeteranRewards(); LoadAlternateCurrencies(); - npc_emote_list.clear(); LoadNPCEmotes(&npc_emote_list); //load the zone config file. @@ -2575,6 +2579,10 @@ void Zone::DoAdventureActions() void Zone::LoadNPCEmotes(std::vector* v) { + for (auto &e: *v) { + safe_delete(e); + } + v->clear(); const auto& l = NpcEmotesRepository::All(content_db); From b40e4ce7cd73e5d5d7c14ced38e3157b07acf421 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:45:09 -0400 Subject: [PATCH 36/73] [Fix] Repair a memory leak in GuildsList (#4585) There was a leak generated when sending the GuildsList Co-authored-by: Mitch Freeman --- common/patches/rof2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 38106fd2a6..68aaa7cfa9 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1845,11 +1845,11 @@ namespace RoF2 } } - auto outapp = new EQApplicationPacket(OP_GuildsList); - outapp->size = packet_size; - outapp->pBuffer = buffer; + safe_delete_array(in->pBuffer); - dest->FastQueuePacket(&outapp); + in->pBuffer = buffer; + in->size = packet_size; + dest->FastQueuePacket(&in); } ENCODE(OP_GuildTributeDonateItem) From 33db85f2eefe15fac8f795aff5e3d3e554bfe83d Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:45:28 -0400 Subject: [PATCH 37/73] [Fix] Repair a EQEMUConfig Memory Leak (#4584) Co-authored-by: Mitch Freeman --- common/eqemu_config.h | 2 +- zone/zone_config.h | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/common/eqemu_config.h b/common/eqemu_config.h index 7e9e3b3417..0ce295ece7 100644 --- a/common/eqemu_config.h +++ b/common/eqemu_config.h @@ -137,9 +137,9 @@ class EQEmuConfig { } - virtual ~EQEmuConfig() {} public: + virtual ~EQEmuConfig() {} // Produce a const singleton static const EQEmuConfig *get() diff --git a/zone/zone_config.h b/zone/zone_config.h index a8e7401b69..e527e06016 100644 --- a/zone/zone_config.h +++ b/zone/zone_config.h @@ -43,11 +43,13 @@ class ZoneConfig : public EQEmuConfig { } // Load the config - static bool LoadConfig(const std::string& path = "") { - if (_zone_config != nullptr) - delete _zone_config; - _zone_config=new ZoneConfig; - _config=_zone_config; + static bool LoadConfig(const std::string &path = "") + { + safe_delete(_zone_config); + safe_delete(_config); + + _zone_config = new ZoneConfig; + _config = _zone_config; return _config->parseFile(path); } From c82dee575a46ce55567063157d118515b194d36c Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:46:19 -0400 Subject: [PATCH 38/73] [Fix] Guild Membership Update Fix (#4581) When the guild membership was large (1k+) the client would studder for half a sec when a guild member would login or logout. This was reproduceable with a guild size of 2k members though floor would be client dependent most likely. --- zone/client_packet.cpp | 2 +- zone/client_process.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4afe679356..52211dbedd 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -860,7 +860,7 @@ void Client::CompleteConnect() if (IsInAGuild()) { if (firstlogon == 1) { guild_mgr.UpdateDbMemberOnline(CharacterID(), true); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + SendGuildMembersList(); } guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c2e20fe5b6..fd0028268e 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -179,7 +179,7 @@ bool Client::Process() { } if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); @@ -202,7 +202,7 @@ bool Client::Process() { Save(); if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } if (GetMerc()) @@ -578,7 +578,7 @@ bool Client::Process() { } if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } return false; From 4c6aaa6995b94d12b414fb90c69c07e323947af2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:46:43 -0600 Subject: [PATCH 39/73] [Logs] Improve Crash log defaults (#4579) --- common/crash.cpp | 2 ++ common/eqemu_logsys.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/common/crash.cpp b/common/crash.cpp index cb02a0bb4b..3828a9af0a 100644 --- a/common/crash.cpp +++ b/common/crash.cpp @@ -294,6 +294,8 @@ void print_trace() SendCrashReport(crash_report); } + LogSys.CloseFileLogs(); + exit(1); } diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index a6a3e283dc..18ef3efcc9 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -87,6 +87,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() * Set Defaults */ log_settings[Logs::Crash].log_to_console = static_cast(Logs::General); + log_settings[Logs::Crash].log_to_file = static_cast(Logs::General); log_settings[Logs::MySQLError].log_to_console = static_cast(Logs::General); log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); From 3155b82abbbaa731a278a7e80642b03fdaeddc75 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:46:59 -0600 Subject: [PATCH 40/73] [Command] Fix #copycharacter (#4582) * [Command] Fix #copycharacter * Update copy_character.cpp --- common/database.cpp | 50 +++++++++++++++++++++++++++++++++++- world/cli/copy_character.cpp | 4 +-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index c0193b9277..a3405970dd 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1860,7 +1860,35 @@ bool Database::CopyCharacter( const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1); - std::vector tables_to_zero_id = { "keyring", "data_buckets", "character_instance_safereturns" }; + // validate destination name doesn't exist already + const auto& destination_characters = CharacterDataRepository::GetWhere( + *this, + fmt::format( + "`name` = '{}' AND `deleted_at` IS NULL LIMIT 1", + Strings::Escape(destination_character_name) + ) + ); + if (!destination_characters.empty()) { + LogError("Character [{}] already exists", destination_character_name); + return false; + } + + std::vector tables_to_zero_id = { + "keyring", + "data_buckets", + "character_instance_safereturns", + "character_expedition_lockouts", + "character_instance_lockouts", + "character_parcels", + "character_tribute", + "player_titlesets", + }; + + std::vector ignore_tables = { + "guilds", + }; + + size_t total_rows_copied = 0; TransactionBegin(); @@ -1868,6 +1896,10 @@ bool Database::CopyCharacter( const std::string& table_name = t.first; const std::string& character_id_column_name = t.second; + if (Strings::Contains(ignore_tables, table_name)) { + continue; + } + auto results = QueryDatabase( fmt::format( "SHOW COLUMNS FROM {}", @@ -1918,6 +1950,10 @@ bool Database::CopyCharacter( value = std::to_string(destination_account_id); } + if (!Strings::IsNumber(value)) { + value = Strings::Escape(value); + } + new_values.emplace_back(value); } @@ -1950,6 +1986,11 @@ bool Database::CopyCharacter( ) ); + size_t rows_copied = insert_rows.size(); // Rows copied for this table + total_rows_copied += rows_copied; // Increment grand total + + LogInfo("Copying table [{}] rows [{}]", table_name, Strings::Commify(rows_copied)); + if (!insert.ErrorMessage().empty()) { TransactionRollback(); return false; @@ -1959,6 +2000,13 @@ bool Database::CopyCharacter( TransactionCommit(); + LogInfo( + "Character [{}] copied to [{}] total rows [{}]", + source_character_name, + destination_character_name, + Strings::Commify(total_rows_copied) + ); + return true; } diff --git a/world/cli/copy_character.cpp b/world/cli/copy_character.cpp index 7d915ad15e..86c2682170 100644 --- a/world/cli/copy_character.cpp +++ b/world/cli/copy_character.cpp @@ -12,12 +12,12 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std }; std::vector options = {}; + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + if (cmd[{"-h", "--help"}]) { return; } - EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - std::string source_character_name = cmd(2).str(); std::string destination_character_name = cmd(3).str(); std::string destination_account_name = cmd(4).str(); From fe43d26dd6979d309c2fad60c4d756bc27d5050d Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:47:22 -0400 Subject: [PATCH 41/73] [Fix] Guild creation to propagate across zones (#4575) Fix guild creation to propagate across zones --- zone/client_packet.cpp | 1 + zone/guild_mgr.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 52211dbedd..4175d56ff3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -7937,6 +7937,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) SetGuildID(new_guild_id); SendGuildList(); guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName()); + guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true); guild_mgr.SendToWorldSendGuildList(); SendGuildSpawnAppearance(); diff --git a/zone/guild_mgr.h b/zone/guild_mgr.h index 3256d82d24..00ca287b9f 100644 --- a/zone/guild_mgr.h +++ b/zone/guild_mgr.h @@ -83,9 +83,9 @@ class ZoneGuildManager : public BaseGuildManager { 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); + virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); protected: - virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); virtual void SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid); virtual void SendRankUpdate(uint32 CharID); virtual void SendGuildDelete(uint32 guild_id); From c44596b38a01acfb0b9389f499e274e2f516787b Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:48:00 -0600 Subject: [PATCH 42/73] [Network] Prune / disconnect TCP connections gracefully (#4574) --- common/net/tcp_connection.cpp | 2 ++ loginserver/world_server.cpp | 12 ------------ loginserver/world_server.h | 7 ------- world/login_server.cpp | 10 ---------- world/login_server.h | 1 - world/queryserv.cpp | 6 ------ world/queryserv.h | 1 - world/ucs.cpp | 12 ------------ world/ucs.h | 5 ----- world/zonelist.cpp | 8 -------- world/zonelist.h | 1 - zone/worldserver.cpp | 8 -------- zone/worldserver.h | 2 -- 13 files changed, 2 insertions(+), 73 deletions(-) diff --git a/common/net/tcp_connection.cpp b/common/net/tcp_connection.cpp index 0c2512dccd..ae1f14badc 100644 --- a/common/net/tcp_connection.cpp +++ b/common/net/tcp_connection.cpp @@ -80,6 +80,8 @@ void EQ::Net::TCPConnection::Start() { } } else if (nread == UV_EOF) { + connection->Disconnect(); + if (buf->base) { delete[] buf->base; } diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index b43651fd75..e4b7ac358c 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -51,12 +51,6 @@ WorldServer::WorldServer(std::shared_ptr wo ServerOP_LSAccountUpdate, std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2) ); - - m_keepalive = std::make_unique( - 1000, - true, - std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1) - ); } WorldServer::~WorldServer() = default; @@ -1307,12 +1301,6 @@ const std::string &WorldServer::GetVersion() const return m_version; } -void WorldServer::OnKeepAlive(EQ::Timer *t) -{ - ServerPacket pack(ServerOP_KeepAlive, 0); - m_connection->SendPacket(&pack); -} - void WorldServer::FormatWorldServerName(char *name, int8 server_list_type) { std::string server_long_name = name; diff --git a/loginserver/world_server.h b/loginserver/world_server.h index df5324240b..45cecfebd6 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -187,13 +187,6 @@ class WorldServer { bool m_is_server_logged_in; bool m_is_server_trusted; - /** - * Keepalive - * @param t - */ - void OnKeepAlive(EQ::Timer *t); - std::unique_ptr m_keepalive; - static void FormatWorldServerName(char *name, int8 server_list_type); }; diff --git a/world/login_server.cpp b/world/login_server.cpp index b2017f094b..b647aedf08 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -595,11 +595,6 @@ bool LoginServer::Connect() ); } - m_keepalive = std::make_unique( - 1000, - true, - std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1)); - return true; } @@ -716,8 +711,3 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack) } } -void LoginServer::OnKeepAlive(EQ::Timer *t) -{ - ServerPacket pack(ServerOP_KeepAlive, 0); - SendPacket(&pack); -} diff --git a/world/login_server.h b/world/login_server.h index 21e25870fb..0a5e6a107a 100644 --- a/world/login_server.h +++ b/world/login_server.h @@ -50,7 +50,6 @@ class LoginServer{ void ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p); - void OnKeepAlive(EQ::Timer *t); std::unique_ptr m_keepalive; std::unique_ptr m_client; diff --git a/world/queryserv.cpp b/world/queryserv.cpp index 0611e7d503..847cb90e04 100644 --- a/world/queryserv.cpp +++ b/world/queryserv.cpp @@ -22,7 +22,6 @@ void QueryServConnection::AddConnection(std::shared_ptrOnMessage(ServerOP_QueryServGeneric, std::bind(&QueryServConnection::HandleGenericMessage, this, std::placeholders::_1, std::placeholders::_2)); connection->OnMessage(ServerOP_LFGuildUpdate, std::bind(&QueryServConnection::HandleLFGuildUpdateMessage, this, std::placeholders::_1, std::placeholders::_2)); m_streams.emplace(std::make_pair(connection->GetUUID(), connection)); - m_keepalive = std::make_unique(1000, true, std::bind(&QueryServConnection::OnKeepAlive, this, std::placeholders::_1)); } void QueryServConnection::RemoveConnection(std::shared_ptr connection) @@ -54,8 +53,3 @@ bool QueryServConnection::SendPacket(ServerPacket* pack) return true; } -void QueryServConnection::OnKeepAlive(EQ::Timer *t) -{ - ServerPacket pack(ServerOP_KeepAlive, 0); - SendPacket(&pack); -} diff --git a/world/queryserv.h b/world/queryserv.h index a02aab647a..028fdcbbda 100644 --- a/world/queryserv.h +++ b/world/queryserv.h @@ -15,7 +15,6 @@ class QueryServConnection void HandleGenericMessage(uint16_t opcode, EQ::Net::Packet &p); void HandleLFGuildUpdateMessage(uint16_t opcode, EQ::Net::Packet &p); bool SendPacket(ServerPacket* pack); - void OnKeepAlive(EQ::Timer *t); private: std::map> m_streams; std::unique_ptr m_keepalive; diff --git a/world/ucs.cpp b/world/ucs.cpp index 0af363051b..0cc70a0ea3 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -31,8 +31,6 @@ void UCSConnection::SetConnection(std::shared_ptr(1000, true, std::bind(&UCSConnection::OnKeepAlive, this, std::placeholders::_1)); } const std::shared_ptr &UCSConnection::GetConnection() const @@ -92,13 +90,3 @@ void UCSConnection::SendMessage(const char *From, const char *Message) SendPacket(pack); safe_delete(pack); } - -void UCSConnection::OnKeepAlive(EQ::Timer *t) -{ - if (!connection) { - return; - } - - ServerPacket pack(ServerOP_KeepAlive, 0); - connection->SendPacket(&pack); -} diff --git a/world/ucs.h b/world/ucs.h index d226e4dc94..04ec17471e 100644 --- a/world/ucs.h +++ b/world/ucs.h @@ -23,11 +23,6 @@ class UCSConnection inline std::string GetIP() const { return (connection && connection->Handle()) ? connection->Handle()->RemoteIP() : 0; } std::shared_ptr connection; - /** - * Keepalive - */ - std::unique_ptr m_keepalive; - void OnKeepAlive(EQ::Timer *t); }; #endif /*UCS_H_*/ diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 31427552a3..c2e572a68d 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -42,7 +42,6 @@ ZSList::ZSList() memset(pLockedZones, 0, sizeof(pLockedZones)); m_tick = std::make_unique(5000, true, std::bind(&ZSList::OnTick, this, std::placeholders::_1)); - m_keepalive = std::make_unique(1000, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)); } ZSList::~ZSList() { @@ -846,13 +845,6 @@ void ZSList::OnTick(EQ::Timer *t) web_interface.SendEvent(out); } -void ZSList::OnKeepAlive(EQ::Timer *t) -{ - for (auto &zone : zone_server_list) { - zone->SendKeepAlive(); - } -} - const std::list> &ZSList::getZoneServerList() const { return zone_server_list; diff --git a/world/zonelist.h b/world/zonelist.h index 71d364c9bf..e096f03638 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -72,7 +72,6 @@ class ZSList private: void OnTick(EQ::Timer *t); - void OnKeepAlive(EQ::Timer *t); uint32 NextID; uint16 pLockedZones[MaxLockedZones]; uint32 CurGroupID; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 143b690a34..768fe4a4fc 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -91,8 +91,6 @@ void WorldServer::Connect() }); m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2)); - - m_keepalive = std::make_unique(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); } bool WorldServer::SendPacket(ServerPacket *pack) @@ -4690,12 +4688,6 @@ void WorldServer::RequestTellQueue(const char *who) return; } -void WorldServer::OnKeepAlive(EQ::Timer *t) -{ - ServerPacket pack(ServerOP_KeepAlive, 0); - SendPacket(&pack); -} - ZoneEventScheduler *WorldServer::GetScheduler() const { return m_zone_scheduler; diff --git a/zone/worldserver.h b/zone/worldserver.h index 49b1514008..de3ac5e971 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -73,8 +73,6 @@ class WorldServer { uint32 cur_groupid; uint32 last_groupid; - void OnKeepAlive(EQ::Timer *t); - std::unique_ptr m_connection; std::unique_ptr m_keepalive; From a3a498634f12ef0579be56e714d4fe3063312a8c Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:48:09 -0600 Subject: [PATCH 43/73] [Maps] Fix broken Map MMFS implementation (#4576) --- CMakeLists.txt | 3 +++ zone/map.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83dacc20fe..f93cabb220 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ IF(EQEMU_ADD_PROFILER) SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed,-lprofiler,--as-needed") ENDIF(EQEMU_ADD_PROFILER) +IF(USE_MAP_MMFS) + ADD_DEFINITIONS(-DUSE_MAP_MMFS) +ENDIF (USE_MAP_MMFS) IF(MSVC) ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) diff --git a/zone/map.cpp b/zone/map.cpp index 410392e8a7..96feddb9c5 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -320,7 +320,7 @@ bool Map::Load(const std::string &filename) } #ifdef USE_MAP_MMFS - if (v) + if (loaded_map_file) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ @@ -338,7 +338,7 @@ bool Map::Load(const std::string &filename) } #ifdef USE_MAP_MMFS - if (v) + if (loaded_map_file) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ @@ -1064,7 +1064,7 @@ bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) fclose(f); std::vector rm_buffer(rm_buffer_size); - uint32 v = InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); + uint32 v = EQ::InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); if (imp) { imp->rm->release(); @@ -1120,11 +1120,11 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) } uint32 rm_buffer_size = rm_buffer.size(); - uint32 mmf_buffer_size = EstimateDeflateBuffer(rm_buffer.size()); + uint32 mmf_buffer_size = EQ::EstimateDeflateBuffer(rm_buffer.size()); std::vector mmf_buffer(mmf_buffer_size); - mmf_buffer_size = DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); + mmf_buffer_size = EQ::DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); if (!mmf_buffer_size) { LogInfo("Failed to save Map MMF file: [{}] - null MMF buffer size", mmf_file_name.c_str()); return false; From 1ed282f6ff995c29bd915062f3ef33355c150b50 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 6 Jan 2025 00:48:39 -0500 Subject: [PATCH 44/73] [Inventory] Add GetInventorySlots() Method (#4566) * [Inventory] Add GetInventorySlots() Method * Update client.cpp * Push --- common/inventory_profile.cpp | 10 +- common/inventory_profile.h | 4 +- zone/client.cpp | 221 +++++++++++++--------------- zone/client.h | 3 +- zone/corpse.cpp | 6 +- zone/corpse.h | 2 +- zone/embparser_api.cpp | 2 +- zone/gm_commands/emptyinventory.cpp | 40 ++--- zone/loot.cpp | 4 +- zone/lua_bot.cpp | 7 +- zone/lua_bot.h | 4 +- zone/lua_client.cpp | 28 +++- zone/lua_client.h | 7 +- zone/lua_corpse.cpp | 4 +- zone/lua_corpse.h | 2 +- zone/lua_inventory.cpp | 8 +- zone/lua_inventory.h | 4 +- zone/lua_npc.cpp | 4 +- zone/lua_npc.h | 2 +- zone/npc.h | 2 +- zone/perl_bot.cpp | 6 +- zone/perl_client.cpp | 19 ++- zone/perl_inventory.cpp | 4 +- zone/perl_npc.cpp | 2 +- zone/perl_player_corpse.cpp | 2 +- zone/questmgr.cpp | 2 +- zone/questmgr.h | 2 +- 27 files changed, 204 insertions(+), 197 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index df81e516c4..595b9f0258 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -612,9 +612,9 @@ bool EQ::InventoryProfile::HasAugmentEquippedByID(uint32 item_id) return has_equipped; } -int EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) +uint32 EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; ItemInstance* item = nullptr; for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { @@ -643,9 +643,9 @@ bool EQ::InventoryProfile::HasItemEquippedByID(uint32 item_id) return has_equipped; } -int EQ::InventoryProfile::CountItemEquippedByID(uint32 item_id) +uint32 EQ::InventoryProfile::CountItemEquippedByID(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; ItemInstance* item = nullptr; for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { @@ -1807,4 +1807,4 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it } } return 0; -} \ No newline at end of file +} diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 100d5ebd8d..4761e1b978 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -147,13 +147,13 @@ namespace EQ bool HasItemEquippedByID(uint32 item_id); // Check how many of a specific item the player has equipped by Item ID - int CountItemEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); // Check if player has a specific augment equipped by Item ID bool HasAugmentEquippedByID(uint32 item_id); // Check how many of a specific augment the player has equipped by Item ID - int CountAugmentEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); // Get a list of augments from a specific slot ID std::vector GetAugmentIDsBySlotID(int16 slot_id); diff --git a/zone/client.cpp b/zone/client.cpp index 920553f5ca..c989468469 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10461,27 +10461,15 @@ void Client::SendToInstance(std::string instance_type, std::string zone_short_na MovePC(zone_id, instance_id, x, y, z, heading); } -int Client::CountItem(uint32 item_id) +uint32 Client::CountItem(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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 }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - quantity += (item->IsStackable() ? item->GetCharges() : 1); - } + + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + quantity += (item->IsStackable() ? item->GetCharges() : 1); } } @@ -10498,30 +10486,26 @@ void Client::ResetItemCooldown(uint32 item_id) int recast_type = item_d->RecastType; bool found_item = false; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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::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 }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item) { - item_d = item->GetItem(); - if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) { - item->SetRecastTimestamp(0); - DeleteItemRecastTimer(item_d->ID); - SendItemPacket(slot_id, item, ItemPacketCharmUpdate); - found_item = true; - } + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item) { + item_d = item->GetItem(); + if ( + item_d && + item->GetID() == item_id || + ( + item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && + item_d->RecastType == recast_type + ) + ) { + item->SetRecastTimestamp(0); + DeleteItemRecastTimer(item_d->ID); + SendItemPacket(slot_id, item, ItemPacketCharmUpdate); + found_item = true; } } } + if (!found_item) { DeleteItemRecastTimer(item_id); //We didn't find the item but we still want to remove the timer } @@ -10556,25 +10540,20 @@ void Client::SetItemCooldown(uint32 item_id, bool use_saved_timer, uint32 in_sec final_time = total_time - current_time; } - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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::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 }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item) { - item_d = item->GetItem(); - if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) { - item->SetRecastTimestamp(total_time); - SendItemPacket(slot_id, item, ItemPacketCharmUpdate); - } + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item) { + item_d = item->GetItem(); + if ( + item_d && + item->GetID() == item_id || + ( + item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && + item_d->RecastType == recast_type + ) + ) { + item->SetRecastTimestamp(total_time); + SendItemPacket(slot_id, item, ItemPacketCharmUpdate); } } } @@ -10617,38 +10596,26 @@ uint32 Client::GetItemCooldown(uint32 item_id) void Client::RemoveItem(uint32 item_id, uint32 quantity) { + uint32 removed_count = 0; EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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 }, - }; - int16 removed_count = 0; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - if (removed_count == quantity) { - break; - } - item = GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - int16 charges = item->IsStackable() ? item->GetCharges() : 0; - int16 stack_size = std::max(charges, static_cast(1)); - if ((removed_count + stack_size) <= quantity) { - removed_count += stack_size; - DeleteItemInInventory(slot_id, charges, true); - } else { - int16 amount_left = (quantity - removed_count); - if (amount_left > 0 && stack_size >= amount_left) { - removed_count += amount_left; - DeleteItemInInventory(slot_id, amount_left, true); - } + for (const int16& slot_id : GetInventorySlots()) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + uint32 charges = item->IsStackable() ? item->GetCharges() : 0; + uint32 stack_size = std::max(charges, static_cast(1)); + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, charges, true); + } else { + uint32 amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); } } } @@ -12820,37 +12787,28 @@ uint16 Client::GetSkill(EQ::skills::SkillType skill_id) const void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity) { EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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 }, - }; - int16 removed_count = 0; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - if (removed_count == quantity) { - break; - } - item = GetInv().GetItem(slot_id); - if (item && item->GetSerialNumber() == serial_number) { - int16 charges = item->IsStackable() ? item->GetCharges() : 0; - int16 stack_size = std::max(charges, static_cast(1)); - if ((removed_count + stack_size) <= quantity) { - removed_count += stack_size; - DeleteItemInInventory(slot_id, charges, true); - } else { - int16 amount_left = (quantity - removed_count); - if (amount_left > 0 && stack_size >= amount_left) { - removed_count += amount_left; - DeleteItemInInventory(slot_id, amount_left, true); - } + uint32 removed_count = 0; + + const auto& slot_ids = GetInventorySlots(); + + for (const int16& slot_id : slot_ids) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetSerialNumber() == serial_number) { + uint32 charges = item->IsStackable() ? item->GetCharges() : 0; + uint32 stack_size = std::max(charges, static_cast(1)); + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, charges, true); + } else { + uint32 amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); } } } @@ -13075,3 +13033,28 @@ void Client::ClientToNpcAggroProcess() LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); } } + +const std::vector& Client::GetInventorySlots() +{ + static const std::vector> slots = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { 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::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 }, + }; + + static std::vector slot_ids; + + if (slot_ids.empty()) { + for (const auto& [begin, end] : slots) { + for (int16 slot_id = begin; slot_id <= end; ++slot_id) { + slot_ids.emplace_back(slot_id); + } + } + } + + return slot_ids; +} diff --git a/zone/client.h b/zone/client.h index 5acc388b3e..03202af2f9 100644 --- a/zone/client.h +++ b/zone/client.h @@ -467,6 +467,7 @@ class Client : public Mob inline ExtendedProfile_Struct& GetEPP() { return m_epp; } inline EQ::InventoryProfile& GetInv() { return m_inv; } inline const EQ::InventoryProfile& GetInv() const { return m_inv; } + const std::vector& GetInventorySlots(); inline PetInfo* GetPetInfo(int pet_info_type) { return pet_info_type == PetInfoType::Suspended ? &m_suspendedminion : &m_petinfo; } inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; } inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; } @@ -1093,7 +1094,7 @@ class Client : public Mob bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false); void SendCursorBuffer(); void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); - int CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); void ResetItemCooldown(uint32 item_id); void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1); uint32 GetItemCooldown(uint32 item_id); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e54dd2e635..4a0d8de09e 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1871,9 +1871,10 @@ bool Corpse::HasItem(uint32 item_id) return false; } -uint16 Corpse::CountItem(uint32 item_id) +uint32 Corpse::CountItem(uint32 item_id) { - uint16 item_count = 0; + uint32 item_count = 0; + if (!database.GetItem(item_id)) { return item_count; } @@ -1893,6 +1894,7 @@ uint16 Corpse::CountItem(uint32 item_id) item_count += i->charges > 0 ? i->charges : 1; } } + return item_count; } diff --git a/zone/corpse.h b/zone/corpse.h index 28fc8c91a6..c1925c325d 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -196,7 +196,7 @@ class Corpse : public Mob { /* Corpse: Loot */ void QueryLoot(Client *to); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); std::vector GetLootList(); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index d18796a4e7..c451db90b8 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1446,7 +1446,7 @@ int Perl__collectitems(uint32_t item_id, bool remove_item) return quest_manager.collectitems(item_id, remove_item); } -int Perl__countitem(uint32_t item_id) +uint32 Perl__countitem(uint32_t item_id) { return quest_manager.countitem(item_id); } diff --git a/zone/gm_commands/emptyinventory.cpp b/zone/gm_commands/emptyinventory.cpp index 582512289c..e37167db6a 100644 --- a/zone/gm_commands/emptyinventory.cpp +++ b/zone/gm_commands/emptyinventory.cpp @@ -2,31 +2,21 @@ void command_emptyinventory(Client *c, const Seperator *sep) { - auto target = c; + Client* t = c; if (c->GetGM() && c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); + t = c->GetTarget()->CastToClient(); } EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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::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 }, - }; - int removed_count = 0; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = target->GetInv().GetItem(slot_id); - if (item) { - int stack_size = std::max(static_cast(item->GetCharges()), 1); - removed_count += stack_size; - target->DeleteItemInInventory(slot_id, 0, true); - } + uint32 removed_count = 0; + + for (const int16& slot_id : t->GetInventorySlots()) { + item = t->GetInv().GetItem(slot_id); + + if (item) { + uint32 stack_size = std::max(static_cast(item->GetCharges()), static_cast(1)); + removed_count += stack_size; + t->DeleteItemInInventory(slot_id, 0, true); } } @@ -35,8 +25,8 @@ void command_emptyinventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "{} {} no items to delete.", - c->GetTargetDescription(target, TargetDescriptionType::UCYou), - c == target ? "have" : "has" + c->GetTargetDescription(t, TargetDescriptionType::UCYou), + c == t ? "have" : "has" ).c_str() ); return; @@ -46,9 +36,9 @@ void command_emptyinventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Inventory cleared for {}, {} item{} deleted.", - c->GetTargetDescription(target), + c->GetTargetDescription(t), removed_count, removed_count != 1 ? "s" : "" ).c_str() ); -} \ No newline at end of file +} diff --git a/zone/loot.cpp b/zone/loot.cpp index 6fcf22cca9..a330914e28 100644 --- a/zone/loot.cpp +++ b/zone/loot.cpp @@ -859,9 +859,9 @@ bool NPC::HasItem(uint32 item_id) return false; } -uint16 NPC::CountItem(uint32 item_id) +uint32 NPC::CountItem(uint32 item_id) { - uint16 item_count = 0; + uint32 item_count = 0; if (!database.GetItem(item_id)) { return item_count; } diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 2cf0e7db3f..5b37a1344e 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -274,7 +274,7 @@ void Lua_Bot::SetSpellDurationRaid(int spell_id, int duration, int level, bool a self->SetSpellDuration(spell_id, duration, level, ApplySpellType::Raid, allow_pets, is_raid_group_only); } -int Lua_Bot::CountAugmentEquippedByID(uint32 item_id) { +uint32 Lua_Bot::CountAugmentEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -284,7 +284,7 @@ bool Lua_Bot::HasAugmentEquippedByID(uint32 item_id) { return self->GetInv().HasAugmentEquippedByID(item_id); } -int Lua_Bot::CountItemEquippedByID(uint32 item_id) { +uint32 Lua_Bot::CountItemEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountItemEquippedByID(item_id); } @@ -701,8 +701,9 @@ luabind::scope lua_register_bot() { .def("ClearItemReuseTimer", (void(Lua_Bot::*)(uint32))&Lua_Bot::ClearItemReuseTimer) .def("ClearSpellRecastTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearSpellRecastTimer) .def("ClearSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::ClearSpellRecastTimer) + .def("CountAugmentEquippedByID", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountAugmentEquippedByID) .def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem) - .def("CountItemEquippedByID", (int(Lua_Bot::*)(uint32))&Lua_Bot::CountItemEquippedByID) + .def("CountItemEquippedByID", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountItemEquippedByID) .def("DeleteBot", (void(Lua_Bot::*)(void))&Lua_Bot::DeleteBot) .def("DeleteBucket", (void(Lua_Bot::*)(std::string))&Lua_Bot::DeleteBucket) .def("Escape", (void(Lua_Bot::*)(void))&Lua_Bot::Escape) diff --git a/zone/lua_bot.h b/zone/lua_bot.h index b07d14abce..6825d2d295 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -127,8 +127,8 @@ class Lua_Bot : public Lua_Mob void SetSpellRecastTimer(uint16 spell_id); void SetSpellRecastTimer(uint16 spell_id, uint32 reuse_timer); - int CountAugmentEquippedByID(uint32 item_id); - int CountItemEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); bool HasAugmentEquippedByID(uint32 item_id); bool HasItemEquippedByID(uint32 item_id); int GetHealAmount(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index f8d5ca64d0..13982fcc06 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2287,7 +2287,7 @@ void Lua_Client::SendToInstance(std::string instance_type, std::string zone_shor self->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); } -int Lua_Client::CountItem(uint32 item_id) { +uint32 Lua_Client::CountItem(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItem(item_id); } @@ -2480,7 +2480,7 @@ void Lua_Client::AddItem(luabind::object item_table) { ); } -int Lua_Client::CountAugmentEquippedByID(uint32 item_id) { +uint32 Lua_Client::CountAugmentEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -2490,7 +2490,7 @@ bool Lua_Client::HasAugmentEquippedByID(uint32 item_id) { return self->GetInv().HasAugmentEquippedByID(item_id); } -int Lua_Client::CountItemEquippedByID(uint32 item_id) { +uint32 Lua_Client::CountItemEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountItemEquippedByID(item_id); } @@ -3436,6 +3436,21 @@ void Lua_Client::AreaTaunt(float range, int bonus_hate) entity_list.AETaunt(self, range, bonus_hate); } +luabind::object Lua_Client::GetInventorySlots(lua_State* L) { + auto lua_table = luabind::newtable(L); + + if (d_) { + auto self = reinterpret_cast(d_); + int index = 1; + for (const int16& slot_id : self->GetInventorySlots()) { + lua_table[index] = slot_id; + index++; + } + } + + return lua_table; +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3514,9 +3529,9 @@ luabind::scope lua_register_client() { .def("ClearXTargets", (void(Lua_Client::*)(void))&Lua_Client::ClearXTargets) .def("ClearZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::ClearZoneFlag) .def("Connected", (bool(Lua_Client::*)(void))&Lua_Client::Connected) - .def("CountAugmentEquippedByID", (int(Lua_Client::*)(uint32))&Lua_Client::CountAugmentEquippedByID) - .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) - .def("CountItemEquippedByID", (int(Lua_Client::*)(uint32))&Lua_Client::CountItemEquippedByID) + .def("CountAugmentEquippedByID", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountAugmentEquippedByID) + .def("CountItem", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountItem) + .def("CountItemEquippedByID", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountItemEquippedByID) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(luabind::object))&Lua_Client::CreateExpedition) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32))&Lua_Client::CreateExpedition) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition) @@ -3650,6 +3665,7 @@ luabind::scope lua_register_client() { .def("GetInstrumentMod", (int(Lua_Client::*)(int))&Lua_Client::GetInstrumentMod) .def("GetIntoxication", (int(Lua_Client::*)(void))&Lua_Client::GetIntoxication) .def("GetInventory", (Lua_Inventory(Lua_Client::*)(void))&Lua_Client::GetInventory) + .def("GetInventorySlots", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetInventorySlots) .def("GetInvulnerableEnvironmentDamage", (bool(Lua_Client::*)(void))&Lua_Client::GetInvulnerableEnvironmentDamage) .def("GetItemIDAt", (int(Lua_Client::*)(int))&Lua_Client::GetItemIDAt) .def("GetItemCooldown", (uint32(Lua_Client::*)(uint32))&Lua_Client::GetItemCooldown) diff --git a/zone/lua_client.h b/zone/lua_client.h index faff8b1572..fb012b3bca 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -441,14 +441,14 @@ class Lua_Client : public Lua_Mob void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two, uint32 sound_controls); - int CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); void RemoveItem(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity); void SetGMStatus(int new_status); int16 GetGMStatus(); void AddItem(luabind::object item_table); - int CountAugmentEquippedByID(uint32 item_id); - int CountItemEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); bool HasAugmentEquippedByID(uint32 item_id); bool HasItemEquippedByID(uint32 item_id); int GetHealAmount(); @@ -509,6 +509,7 @@ class Lua_Client : public Lua_Mob void AreaTaunt(); void AreaTaunt(float range); void AreaTaunt(float range, int bonus_hate); + luabind::object GetInventorySlots(lua_State* L); void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 321e4e543d..2db6bad074 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -177,7 +177,7 @@ bool Lua_Corpse::HasItem(uint32 item_id) { return self->HasItem(item_id); } -uint16 Lua_Corpse::CountItem(uint32 item_id) { +uint32 Lua_Corpse::CountItem(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItem(item_id); } @@ -226,7 +226,7 @@ luabind::scope lua_register_corpse() { .def("AllowMobLoot", (void(Lua_Corpse::*)(Lua_Mob, uint8))&Lua_Corpse::AllowMobLoot) .def("Bury", (void(Lua_Corpse::*)(void))&Lua_Corpse::Bury) .def("CanMobLoot", (bool(Lua_Corpse::*)(int))&Lua_Corpse::CanMobLoot) - .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) + .def("CountItem", (uint32(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) .def("CountItems", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::CountItems) .def("Delete", (void(Lua_Corpse::*)(void))&Lua_Corpse::Delete) .def("Depop", (void(Lua_Corpse::*)(void))&Lua_Corpse::Depop) diff --git a/zone/lua_corpse.h b/zone/lua_corpse.h index 9ad63e9791..58bfb7d056 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -62,7 +62,7 @@ class Lua_Corpse : public Lua_Mob uint32 GetPlatinum(); void AddLooter(Lua_Mob who); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); Lua_Corpse_Loot_List GetLootList(lua_State* L); diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index 48987de683..3d464a9152 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -164,7 +164,7 @@ int Lua_Inventory::GetSlotByItemInst(Lua_ItemInst inst) { return self->GetSlotByItemInst(inst); } -int Lua_Inventory::CountAugmentEquippedByID(uint32 item_id) { +uint32 Lua_Inventory::CountAugmentEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountAugmentEquippedByID(item_id); } @@ -174,7 +174,7 @@ bool Lua_Inventory::HasAugmentEquippedByID(uint32 item_id) { return self->HasAugmentEquippedByID(item_id); } -int Lua_Inventory::CountItemEquippedByID(uint32 item_id) { +uint32 Lua_Inventory::CountItemEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItemEquippedByID(item_id); } @@ -208,8 +208,8 @@ luabind::scope lua_register_inventory() { .def("CalcSlotId", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::CalcSlotId) .def("CanItemFitInContainer", (bool(Lua_Inventory::*)(Lua_Item,Lua_Item))&Lua_Inventory::CanItemFitInContainer) .def("CheckNoDrop", (bool(Lua_Inventory::*)(int))&Lua_Inventory::CheckNoDrop) - .def("CountAugmentEquippedByID", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::CountAugmentEquippedByID) - .def("CountItemEquippedByID", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::CountItemEquippedByID) + .def("CountAugmentEquippedByID", (uint32(Lua_Inventory::*)(uint32))&Lua_Inventory::CountAugmentEquippedByID) + .def("CountItemEquippedByID", (uint32(Lua_Inventory::*)(uint32))&Lua_Inventory::CountItemEquippedByID) .def("DeleteItem", (bool(Lua_Inventory::*)(int))&Lua_Inventory::DeleteItem) .def("DeleteItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::DeleteItem) .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool))&Lua_Inventory::FindFreeSlot) diff --git a/zone/lua_inventory.h b/zone/lua_inventory.h index 472447e623..a9c5c8d5e7 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -43,8 +43,8 @@ class Lua_Inventory : public Lua_Ptr bool DeleteItem(int slot_id); bool DeleteItem(int slot_id, int quantity); bool CheckNoDrop(int slot_id); - int CountAugmentEquippedByID(uint32 item_id); - int CountItemEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); Lua_ItemInst PopItem(int slot_id); bool HasAugmentEquippedByID(uint32 item_id); bool HasItemEquippedByID(uint32 item_id); diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index b17804edce..aa09732927 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -597,7 +597,7 @@ bool Lua_NPC::HasItem(uint32 item_id) return self->HasItem(item_id); } -uint16 Lua_NPC::CountItem(uint32 item_id) +uint32 Lua_NPC::CountItem(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItem(item_id); @@ -862,7 +862,7 @@ luabind::scope lua_register_npc() { .def("CheckNPCFactionAlly", (int(Lua_NPC::*)(int))&Lua_NPC::CheckNPCFactionAlly) .def("ClearItemList", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLootItems) .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) - .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) + .def("CountItem", (uint32(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) .def("CountLoot", (int(Lua_NPC::*)(void))&Lua_NPC::CountLoot) .def("DeleteBucket", (void(Lua_NPC::*)(std::string))&Lua_NPC::DeleteBucket) .def("DescribeSpecialAbilities", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::DescribeSpecialAbilities) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index ec916593ad..180bddad09 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -146,7 +146,7 @@ class Lua_NPC : public Lua_Mob void ChangeLastName(std::string last_name); void ClearLastName(); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetLootItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); float GetHealScale(); diff --git a/zone/npc.h b/zone/npc.h index a20d975b12..9b17afaba6 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -222,7 +222,7 @@ class NPC : public Mob void RemoveLootCash(); void QueryLoot(Client *to, bool is_pet_query = false); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetLootItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); std::vector GetLootList(); diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 86f1045653..bd0747e948 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -145,7 +145,7 @@ EQ::ItemInstance* Perl_Bot_GetAugmentAt(Bot* self, int16 slot_id, uint8 augment_ return nullptr; } -int Perl_Bot_CountAugmentEquippedByID(Bot* self, uint32 item_id) +uint32 Perl_Bot_CountAugmentEquippedByID(Bot* self, uint32 item_id) { return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -155,7 +155,7 @@ bool Perl_Bot_HasAugmentEquippedByID(Bot* self, uint32 item_id) return self->GetInv().HasAugmentEquippedByID(item_id); } -int Perl_Bot_CountItemEquippedByID(Bot* self, uint32 item_id) +uint32 Perl_Bot_CountItemEquippedByID(Bot* self, uint32 item_id) { return self->GetInv().CountItemEquippedByID(item_id); } @@ -650,7 +650,7 @@ void perl_register_bot() package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool))&Perl_Bot_ApplySpellRaid); package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool, bool))&Perl_Bot_ApplySpellRaid); package.add("Camp", (void(*)(Bot*))&Perl_Bot_Camp); - package.add("Camp", (void(*)(Bot*, bool))&Perl_Bot_Camp); + package.add("Camp", (void(*)(Bot*, bool))&Perl_Bot_Camp); package.add("ClearDisciplineReuseTimer", (void(*)(Bot*))&Perl_Bot_ClearDisciplineReuseTimer); package.add("ClearDisciplineReuseTimer", (void(*)(Bot*, uint16))&Perl_Bot_ClearDisciplineReuseTimer); package.add("ClearItemReuseTimer", (void(*)(Bot*))&Perl_Bot_ClearItemReuseTimer); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 8bb39afda2..c41b39ba58 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2209,7 +2209,7 @@ void Perl_Client_SendToInstance(Client* self, std::string instance_type, std::st self->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); } -int Perl_Client_CountItem(Client* self, uint32 item_id) +uint32 Perl_Client_CountItem(Client* self, uint32 item_id) { return self->CountItem(item_id); } @@ -2388,7 +2388,7 @@ void Perl_Client_AddItem(Client* self, perl::reference table_ref) augment_four, augment_five, augment_six, attuned, slot_id); } -int Perl_Client_CountAugmentEquippedByID(Client* self, uint32 item_id) +uint32 Perl_Client_CountAugmentEquippedByID(Client* self, uint32 item_id) { return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -2398,7 +2398,7 @@ bool Perl_Client_HasAugmentEquippedByID(Client* self, uint32 item_id) return self->GetInv().HasAugmentEquippedByID(item_id); } -int Perl_Client_CountItemEquippedByID(Client* self, uint32 item_id) +uint32 Perl_Client_CountItemEquippedByID(Client* self, uint32 item_id) { return self->GetInv().CountItemEquippedByID(item_id); } @@ -3212,6 +3212,18 @@ Merc* Perl_Client_GetMerc(Client* self) return self->GetMerc(); } +perl::array Perl_Client_GetInventorySlots(Client* self) +{ + perl::array result; + const auto& v = self->GetInventorySlots(); + + for (int i = 0; i < v.size(); ++i) { + result.push_back(v[i]); + } + + return result; +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3426,6 +3438,7 @@ void perl_register_client() package.add("GetInstanceID", &Perl_Client_GetInstanceID); package.add("GetInstrumentMod", &Perl_Client_GetInstrumentMod); package.add("GetInventory", &Perl_Client_GetInventory); + package.add("GetInventorySlots", &Perl_Client_GetInventorySlots); package.add("GetInvulnerableEnvironmentDamage", &Perl_Client_GetInvulnerableEnvironmentDamage); package.add("GetItemAt", &Perl_Client_GetItemAt); package.add("GetItemCooldown", &Perl_Client_GetItemCooldown); diff --git a/zone/perl_inventory.cpp b/zone/perl_inventory.cpp index ba083e936b..a002a24a71 100644 --- a/zone/perl_inventory.cpp +++ b/zone/perl_inventory.cpp @@ -150,7 +150,7 @@ bool Perl_Inventory_HasAugmentEquippedByID(EQ::InventoryProfile* self, uint32_t return self->HasAugmentEquippedByID(item_id); } -int Perl_Inventory_CountAugmentEquippedByID(EQ::InventoryProfile* self, uint32_t item_id) +uint32 Perl_Inventory_CountAugmentEquippedByID(EQ::InventoryProfile* self, uint32_t item_id) { return self->CountAugmentEquippedByID(item_id); } @@ -160,7 +160,7 @@ bool Perl_Inventory_HasItemEquippedByID(EQ::InventoryProfile* self, uint32_t ite return self->HasItemEquippedByID(item_id); } -int Perl_Inventory_CountItemEquippedByID(EQ::InventoryProfile* self, uint32_t item_id) +uint32 Perl_Inventory_CountItemEquippedByID(EQ::InventoryProfile* self, uint32_t item_id) { return self->CountItemEquippedByID(item_id); } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index b47b8c0e8d..a44bd146ed 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -615,7 +615,7 @@ bool Perl_NPC_HasItem(NPC* self, uint32 item_id) // @categories Script Utility return self->HasItem(item_id); } -int Perl_NPC_CountItem(NPC* self, uint32 item_id) +uint32 Perl_NPC_CountItem(NPC* self, uint32 item_id) { return self->CountItem(item_id); } diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index 60de30201f..c034e0312b 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -161,7 +161,7 @@ bool Perl_Corpse_HasItem(Corpse* self, uint32_t item_id) // @categories Script U return self->HasItem(item_id); } -int Perl_Corpse_CountItem(Corpse* self, uint32_t item_id) // @categories Script Utility +uint32 Perl_Corpse_CountItem(Corpse* self, uint32_t item_id) // @categories Script Utility { return self->CountItem(item_id); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ad29a76a94..cd44abf8b0 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3304,7 +3304,7 @@ int QuestManager::collectitems(uint32 item_id, bool remove) return quantity; } -int QuestManager::countitem(uint32 item_id) { +uint32 QuestManager::countitem(uint32 item_id) { QuestManagerCurrentQuestVars(); if (!initiator) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 1f872aaf54..c73596f4ee 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -249,7 +249,7 @@ class QuestManager { int getlevel(uint8 type); int collectitems(uint32 item_id, bool remove); int collectitems_processSlot(int16 slot_id, uint32 item_id, bool remove); - int countitem(uint32 item_id); + uint32 countitem(uint32 item_id); void removeitem(uint32 item_id, uint32 quantity = 1); std::string getitemcomment(uint32 item_id); std::string getitemlore(uint32 item_id); From d95b64e0b8c0050607ee8d84e0e02e44dca8bd45 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:18:25 -0500 Subject: [PATCH 45/73] [Cleanup] Fix GM Flag Spell Restriction Bypasses (#4571) * [Cleanup] Fix GM Flag Spell Restriction Bypasses * Update spells.cpp --- zone/spells.cpp | 185 ++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 101 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 15d1e55745..4ca95255c3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -645,6 +645,11 @@ bool Mob::DoCastingChecksOnCaster(int32 spell_id, CastingSlot slot) { return true; } +struct SpellCheck { + std::function condition; // The condition to check + std::function action; // The action if the condition fails +}; + bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) { /* @@ -652,114 +657,92 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) - levitate zone restriction (client blocks) [cancel before begin cast message] - can not cast outdoor [cancels after spell finishes channeling] - If the spell is a casted spell, check on CastSpell and ignore on SpellFinished. - If the spell is a initiated from SpellFinished, then check at start of SpellFinished. + If the spell is a cast spell, check on CastSpell and ignore on SpellFinished. + If the spell is initiated from SpellFinished, then check at start of SpellFinished. */ - bool bypass_casting_restrictions = false; - - if (!IsClient()) { - bypass_casting_restrictions = true; - } - - if (IsClient() && CastToClient()->GetGM()) { - bypass_casting_restrictions = true; - Message( - Chat::White, - fmt::format( - "Your GM flag allows you to bypass zone casting restrictions and cast {} in this zone.", - Saylink::Silent( - fmt::format( - "#castspell {}", - spell_id - ), - GetSpellName(spell_id) - ) - ).c_str() - ); - } + bool bypass_casting_restrictions = !IsClient(); + glm::vec3 position = glm::vec3(GetPosition()); - /* - Zone ares that prevent blocked spells from being cast. - If on cast iniated then check any mob casting, if on spellfinished only check if is from client. - */ - if ((check_on_casting && !bypass_casting_restrictions) || (!check_on_casting && IsClient())) { - if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - if (IsClient()) { - if (!CastToClient()->GetGM()) { - const char *msg = zone->GetSpellBlockedMessage(spell_id, glm::vec3(GetPosition())); - if (msg) { - Message(Chat::Red, msg); - return false; - } - else { - Message(Chat::Red, "You can't cast this spell here."); - return false; - } - LogSpells("Spell casting canceled [{}] : can not cast in this zone location blocked spell.", spell_id); - } - else { - Message( - Chat::White, - fmt::format( - "Your GM flag allows you to bypass zone blocked spells and cast {} in this zone.", - Saylink::Silent( - fmt::format( - "#castspell {}", - spell_id - ), - GetSpellName(spell_id) - ) - ).c_str() - ); - LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); - } - } - return false; + auto gm_bypass_message = [&](const std::string& restriction) { + if (CastToClient()->GetGM()) { + Message( + Chat::White, + fmt::format( + "Your GM flag allows you to bypass {} and cast {}.", + restriction, + Saylink::Silent( + fmt::format("#castspell {}", spell_id), + GetSpellName(spell_id) + ) + ).c_str() + ); + return true; } - } - /* - Zones where you can not use levitate spells. - */ - if (!bypass_casting_restrictions && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { //check on spellfinished. - Message(Chat::Red, "You have entered an area where levitation effects do not function."); - LogSpells("Spell casting canceled [{}] : can not cast levitation in this zone.", spell_id); - return false; - } - /* - Zones where you can not use detrimental spells. - */ - if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - Message(Chat::Red, "You cannot cast detrimental spells here."); return false; - } - /* - Zones where you can not cast a spell that is for daytime or nighttime only - */ - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - MessageString(Chat::Red, CAST_DAYTIME); - return false; - } + }; - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - MessageString(Chat::Red, CAST_NIGHTTIME); - return false; - } - - if (check_on_casting) { - /* - Zones where you can not cast out door only spells. This is only checked when casting is completed. - */ - if (!bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - if (IsClient()) { - if (!CastToClient()->GetGM()) { - MessageString(Chat::Red, CAST_OUTDOORS); - LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); - return false; - } else { - Message(Chat::White, "Your GM flag allows you to cast outdoor spells when indoors."); - } + std::vector spell_checks = { + // Blocked spells + { + [&]() { return !bypass_casting_restrictions && zone->IsSpellBlocked(spell_id, position); }, + [&]() { + if (gm_bypass_message("zone blocked spells")) { return true; } + const char* msg = zone->GetSpellBlockedMessage(spell_id, position); + Message(Chat::Red, msg ? msg : "You can't cast this spell here."); + return false; + } + }, + // Levitation restriction + { + [&]() { return !bypass_casting_restrictions && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate); }, + [&]() { + if (gm_bypass_message("zone levitation restrictions")) { return true; } + Message(Chat::Red, "You have entered an area where levitation effects do not function."); + return false; } + }, + // Detrimental spells restriction + { + [&]() { return !bypass_casting_restrictions && IsDetrimentalSpell(spell_id) && !zone->CanDoCombat(); }, + [&]() { + if (gm_bypass_message("no combat zone restrictions")) { return true; } + Message(Chat::Red, "You cannot cast detrimental spells here."); + return false; + } + }, + // Daytime-only spells + { + [&]() { return !bypass_casting_restrictions && spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime(); }, + [&]() { + if (gm_bypass_message("spell daytime restrictions")) { return true; } + MessageString(Chat::Red, CAST_DAYTIME); + return false; + } + }, + // Nighttime-only spells + { + [&]() { return !bypass_casting_restrictions && spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime(); }, + [&]() { + if (gm_bypass_message("spell nighttime restrictions")) return true; + MessageString(Chat::Red, CAST_NIGHTTIME); + return false; + } + }, + // Outdoor-only spells + { + [&]() { return check_on_casting && !bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor(); }, + [&]() { + if (gm_bypass_message("zone outdoor restrictions")) return true; + MessageString(Chat::Red, CAST_OUTDOORS); + return false; + } + } + }; + + for (const auto& check : spell_checks) { + if (check.condition() && !check.action()) { + return false; } } From 490cffb5ea9aaf675c931dbd06422df6b1c6a959 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 6 Jan 2025 00:27:11 -0600 Subject: [PATCH 46/73] [Release] 22.61.0 (#4587) --- CHANGELOG.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ common/version.h | 2 +- package.json | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfa1f1bcc..a7ea71c2fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,68 @@ +## [22.61.0] 1/6/2025 + +### Bots + +* Fix AA ranks to account for level ([#4567](https://github.com/EQEmu/Server/pull/4567)) @nytmyr 2024-12-07 + +### Code + +* Convert Event Parses to Single Line ([#4569](https://github.com/EQEmu/Server/pull/4569)) @Kinglykrab 2024-12-12 +* Fix GM Flag Spell Restriction Bypasses ([#4571](https://github.com/EQEmu/Server/pull/4571)) @Kinglykrab 2025-01-06 +* Remove Unused Group Methods ([#4559](https://github.com/EQEmu/Server/pull/4559)) @Kinglykrab 2024-12-12 + +### Commands + +* Add #find bot Subcommand ([#4563](https://github.com/EQEmu/Server/pull/4563)) @Kinglykrab 2024-12-12 +* Add #find ldon_theme Subcommand ([#4564](https://github.com/EQEmu/Server/pull/4564)) @Kinglykrab 2024-12-12 +* Fix #copycharacter ([#4582](https://github.com/EQEmu/Server/pull/4582)) @Akkadius 2025-01-06 + +### Databuckets + +* Improved Reliability and Performance of Databuckets ([#4562](https://github.com/EQEmu/Server/pull/4562)) @Akkadius 2024-12-12 + +### Feature + +* Enable bazaar window 'Find Trader' functionality ([#4560](https://github.com/EQEmu/Server/pull/4560)) @neckkola 2024-12-12 + +### Filesystem + +* Path Manager Improvements ([#4557](https://github.com/EQEmu/Server/pull/4557)) @Akkadius 2025-01-06 + +### Fixes + +* Allow Items in ROF2 to Stack to 32,767 ([#4556](https://github.com/EQEmu/Server/pull/4556)) @Kinglykrab 2024-12-12 +* Fix EVENT_COMBAT on NPC Death ([#4558](https://github.com/EQEmu/Server/pull/4558)) @Kinglykrab 2024-11-28 +* Guild Membership Update Fix ([#4581](https://github.com/EQEmu/Server/pull/4581)) @neckkola 2025-01-06 +* Guild creation to propagate across zones ([#4575](https://github.com/EQEmu/Server/pull/4575)) @neckkola 2025-01-06 +* Repair a EQEMUConfig Memory Leak ([#4584](https://github.com/EQEmu/Server/pull/4584)) @neckkola 2025-01-06 +* Repair a LoadNPCEmote MemoryLeak ([#4586](https://github.com/EQEmu/Server/pull/4586)) @neckkola 2025-01-06 +* Repair a memory leak in GuildsList ([#4585](https://github.com/EQEmu/Server/pull/4585)) @neckkola 2025-01-06 +* Resolve a client crash when logging in or zoning ([#4572](https://github.com/EQEmu/Server/pull/4572)) @neckkola 2024-12-14 + +### Groups + +* Fix AmIMainAssist incorrectly checking for MainTankName ([#4565](https://github.com/EQEmu/Server/pull/4565)) @nytmyr 2024-12-04 + +### Inventory + +* Add GetInventorySlots() Method ([#4566](https://github.com/EQEmu/Server/pull/4566)) @Kinglykrab 2025-01-06 + +### Logs + +* Improve Crash log defaults ([#4579](https://github.com/EQEmu/Server/pull/4579)) @Akkadius 2025-01-06 + +### Maps + +* Fix broken Map MMFS implementation ([#4576](https://github.com/EQEmu/Server/pull/4576)) @Akkadius 2025-01-06 + +### Network + +* Prune / disconnect TCP connections gracefully ([#4574](https://github.com/EQEmu/Server/pull/4574)) @Akkadius 2025-01-06 + +### Rules + +* Add rules for requiring custom files from client ([#4561](https://github.com/EQEmu/Server/pull/4561)) @knervous 2024-12-12 + ## [22.60.0] 11/25/2024 ### Bazaar diff --git a/common/version.h b/common/version.h index 232d3afc5f..919b8f64ca 100644 --- a/common/version.h +++ b/common/version.h @@ -25,7 +25,7 @@ // Build variables // these get injected during the build pipeline -#define CURRENT_VERSION "22.60.0-dev" // always append -dev to the current version for custom-builds +#define CURRENT_VERSION "22.61.0-dev" // always append -dev to the current version for custom-builds #define LOGIN_VERSION "0.8.0" #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ diff --git a/package.json b/package.json index 26dfe3d29b..68a2da55e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eqemu-server", - "version": "22.60.0", + "version": "22.61.0", "repository": { "type": "git", "url": "https://github.com/EQEmu/Server.git" From d89f9bdcc7af7da1579b428174a1171142658d03 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Tue, 7 Jan 2025 01:01:49 -0400 Subject: [PATCH 47/73] [Fix] Repair an incorrect safe_delete call memory leak. (#4588) --- common/patches/rof2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 68aaa7cfa9..ef27f06c24 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -487,7 +487,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_stat); //itemstat } - safe_delete(in->pBuffer); + safe_delete_array(in->pBuffer); in->size = p_size; in->pBuffer = (uchar *) buffer.get(); dest->QueuePacket(in); @@ -508,7 +508,7 @@ namespace RoF2 eq->num_of_traders = emu->traders; eq->num_of_items = emu->items; - safe_delete(in->pBuffer); + safe_delete_array(in->pBuffer); in->SetOpcode(OP_TraderShop); in->size = sizeof(structs::BazaarWelcome_Struct); in->pBuffer = (uchar *) buffer.get(); From 03b30d5c7af0aecfab5459d40c6777b6b62fabf3 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 7 Jan 2025 13:46:32 -0600 Subject: [PATCH 48/73] [Database] Change npc_types walkspeed to be of type float (#4589) * [Database] Change npc_types walkspeed to be of type float * Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 11 +++++++++++ common/version.h | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 61186a2054..fde8820f05 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5782,6 +5782,17 @@ CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires); ALTER TABLE `trader` ADD COLUMN `char_zone_instance_id` INT NULL DEFAULT '0' AFTER `char_zone_id`; )" + }, + ManifestEntry{ + .version = 9287, + .description = "2024_11_26_bazaar_find_trader.sql", + .check = "SHOW COLUMNS FROM `npc_types` LIKE 'walkspeed'", + .condition = "missing", + .match = "float", + .sql = R"( +ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0; +)", + .content_schema_update = true } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/version.h b/common/version.h index 919b8f64ca..192a206141 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9286 +#define CURRENT_BINARY_DATABASE_VERSION 9287 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif From 533dc997fd8fc7e71b848add173be61d9d251911 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:50:34 -0400 Subject: [PATCH 49/73] [Fix] Repair a memory leak in #summonitem (#4591) --- zone/gm_commands/summonitem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/gm_commands/summonitem.cpp b/zone/gm_commands/summonitem.cpp index ddee8ab079..b72292a97e 100755 --- a/zone/gm_commands/summonitem.cpp +++ b/zone/gm_commands/summonitem.cpp @@ -132,4 +132,6 @@ void command_summonitem(Client *c, const Seperator *sep) item_link ).c_str() ); + + safe_delete(new_item); } From 15684567cf8dd4b99a1e7200f2751cc03fb8ee56 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:28:44 -0400 Subject: [PATCH 50/73] [Fix] Add Bazaar BulkSendTrader Limit for RoF2 (#4590) Add Bazaar BulkSendTrader Limit --- common/eq_limits.cpp | 20 ++++++++++++++------ common/eq_limits.h | 7 +++++-- common/patches/rof2_limits.h | 1 + common/patches/titanium_limits.h | 4 ++-- common/repositories/trader_repository.h | 9 ++++++--- zone/trading.cpp | 7 ++++++- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/common/eq_limits.cpp b/common/eq_limits.cpp index 0518576816..eda4d6a196 100644 --- a/common/eq_limits.cpp +++ b/common/eq_limits.cpp @@ -47,6 +47,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers ClientUnknown::constants::EXPANSION_BIT, ClientUnknown::constants::EXPANSIONS_MASK, ClientUnknown::INULL, + ClientUnknown::INULL, ClientUnknown::INULL ), /*[ClientVersion::Client62] =*/ @@ -55,6 +56,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers Client62::constants::EXPANSION_BIT, Client62::constants::EXPANSIONS_MASK, Client62::INULL, + Client62::INULL, Client62::INULL ), /*[ClientVersion::Titanium] =*/ @@ -63,7 +65,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers Titanium::constants::EXPANSION_BIT, Titanium::constants::EXPANSIONS_MASK, Titanium::constants::CHARACTER_CREATION_LIMIT, - Titanium::constants::SAY_LINK_BODY_SIZE + Titanium::constants::SAY_LINK_BODY_SIZE, + Titanium::INULL ), /*[ClientVersion::SoF] =*/ EQ::constants::LookupEntry( @@ -71,7 +74,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers SoF::constants::EXPANSION_BIT, SoF::constants::EXPANSIONS_MASK, SoF::constants::CHARACTER_CREATION_LIMIT, - SoF::constants::SAY_LINK_BODY_SIZE + SoF::constants::SAY_LINK_BODY_SIZE, + SoF::INULL ), /*[ClientVersion::SoD] =*/ EQ::constants::LookupEntry( @@ -79,7 +83,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers SoD::constants::EXPANSION_BIT, SoD::constants::EXPANSIONS_MASK, SoD::constants::CHARACTER_CREATION_LIMIT, - SoD::constants::SAY_LINK_BODY_SIZE + SoD::constants::SAY_LINK_BODY_SIZE, + SoD::INULL ), /*[ClientVersion::UF] =*/ EQ::constants::LookupEntry( @@ -87,7 +92,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers UF::constants::EXPANSION_BIT, UF::constants::EXPANSIONS_MASK, UF::constants::CHARACTER_CREATION_LIMIT, - UF::constants::SAY_LINK_BODY_SIZE + UF::constants::SAY_LINK_BODY_SIZE, + UF::INULL ), /*[ClientVersion::RoF] =*/ EQ::constants::LookupEntry( @@ -95,7 +101,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers RoF::constants::EXPANSION_BIT, RoF::constants::EXPANSIONS_MASK, RoF::constants::CHARACTER_CREATION_LIMIT, - RoF::constants::SAY_LINK_BODY_SIZE + RoF::constants::SAY_LINK_BODY_SIZE, + RoF::INULL ), /*[ClientVersion::RoF2] =*/ EQ::constants::LookupEntry( @@ -103,7 +110,8 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers RoF2::constants::EXPANSION_BIT, RoF2::constants::EXPANSIONS_MASK, RoF2::constants::CHARACTER_CREATION_LIMIT, - RoF2::constants::SAY_LINK_BODY_SIZE + RoF2::constants::SAY_LINK_BODY_SIZE, + RoF2::constants::MAX_BAZAAR_TRADERS ) }; diff --git a/common/eq_limits.h b/common/eq_limits.h index 40b095b15c..fed7652072 100644 --- a/common/eq_limits.h +++ b/common/eq_limits.h @@ -42,6 +42,7 @@ namespace EQ uint32 ExpansionsMask; int16 CharacterCreationLimit; size_t SayLinkBodySize; + uint32 BazaarTraderLimit; LookupEntry(const LookupEntry *lookup_entry) { } LookupEntry( @@ -49,13 +50,15 @@ namespace EQ uint32 ExpansionBit, uint32 ExpansionsMask, int16 CharacterCreationLimit, - size_t SayLinkBodySize + size_t SayLinkBodySize, + uint32 BazaarTraderLimit ) : Expansion(Expansion), ExpansionBit(ExpansionBit), ExpansionsMask(ExpansionsMask), CharacterCreationLimit(CharacterCreationLimit), - SayLinkBodySize(SayLinkBodySize) + SayLinkBodySize(SayLinkBodySize), + BazaarTraderLimit(BazaarTraderLimit) { } }; diff --git a/common/patches/rof2_limits.h b/common/patches/rof2_limits.h index 7e7774de65..a2fe623e09 100644 --- a/common/patches/rof2_limits.h +++ b/common/patches/rof2_limits.h @@ -272,6 +272,7 @@ namespace RoF2 const size_t SAY_LINK_BODY_SIZE = 56; const uint32 MAX_GUILD_ID = 50000; + const uint32 MAX_BAZAAR_TRADERS = 600; } /*constants*/ diff --git a/common/patches/titanium_limits.h b/common/patches/titanium_limits.h index cdd3171f42..cd7d9e5d8f 100644 --- a/common/patches/titanium_limits.h +++ b/common/patches/titanium_limits.h @@ -286,8 +286,8 @@ namespace Titanium const size_t CHARACTER_CREATION_LIMIT = 8; // Hard-coded in client - DO NOT ALTER - const size_t SAY_LINK_BODY_SIZE = 45; - const uint32 MAX_GUILD_ID = 1500; + const size_t SAY_LINK_BODY_SIZE = 45; + const uint32 MAX_GUILD_ID = 1500; } /*constants*/ diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index b273638115..2999b738a0 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -40,15 +40,18 @@ class TraderRepository : public BaseTraderRepository { int32 char_zone_instance_id ); - static BulkTraders_Struct GetDistinctTraders(Database &db) + static BulkTraders_Struct GetDistinctTraders(Database &db, uint32 char_zone_instance_id, uint32 max_results) { BulkTraders_Struct all_entries{}; std::vector distinct_traders; - auto results = db.QueryDatabase( + auto results = db.QueryDatabase(fmt::format( "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " - "JOIN character_data AS c ON t.char_id = c.id;" + "JOIN character_data AS c ON t.char_id = c.id " + "ORDER BY t.char_zone_instance_id = {} DESC LIMIT {};", + char_zone_instance_id, + max_results) ); distinct_traders.reserve(results.RowCount()); diff --git a/zone/trading.cpp b/zone/trading.cpp index 4be42c099f..785fd2aec6 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -3219,7 +3219,12 @@ void Client::SendBulkBazaarTraders() return; } - auto results = TraderRepository::GetDistinctTraders(database); + auto results = TraderRepository::GetDistinctTraders( + database, + GetInstanceID(), + EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit + ); + auto p_size = 4 + 12 * results.count + results.name_length; auto buffer = std::make_unique(p_size); memset(buffer.get(), 0, p_size); From c82f1b9afc9bf6be74afcff052d9516d0d2aa80b Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 8 Jan 2025 17:41:16 -0600 Subject: [PATCH 51/73] [Zone] Implement zone player count sharding (#4536) * [Zone] Implement zone player count sharding * Update client.cpp * Update database_instances.cpp * You must request a shard change from the zone you are currently in. * // zone sharding * You cannot request a shard change while in combat. * Query adjustment * Use safe coords * Changes * Fixes to instance query * Push * Push * Final push * Update client.cpp * Update eq_packet_structs.h * Remove pick menu * Comment * Update character_data_repository.h * Update zoning.cpp --------- Co-authored-by: Kinglykrab --- common/database/database_update_manifest.cpp | 12 ++ common/database_instances.cpp | 10 +- common/emu_oplist.h | 6 +- common/eq_packet_structs.h | 20 +++ common/opcode_dispatch.h | 1 + .../repositories/base/base_zone_repository.h | 12 ++ .../repositories/character_data_repository.h | 87 +++++++++++ common/ruletypes.h | 1 + common/version.h | 2 +- utils/patches/patch_RoF2.conf | 5 +- zone/client.cpp | 64 +++++++- zone/client.h | 1 + zone/client_packet.cpp | 17 +++ zone/client_packet.h | 1 + zone/command.cpp | 2 + zone/command.h | 1 + zone/gm_commands/zone_shard.cpp | 137 ++++++++++++++++++ zone/lua_client.cpp | 16 +- zone/lua_client.h | 1 + zone/lua_packet.cpp | 3 +- zone/perl_client.cpp | 6 + zone/zoning.cpp | 60 ++++++++ 22 files changed, 443 insertions(+), 22 deletions(-) create mode 100644 zone/gm_commands/zone_shard.cpp diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index fde8820f05..cd484122b9 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5791,6 +5791,18 @@ ALTER TABLE `trader` .match = "float", .sql = R"( ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0; +)", + .content_schema_update = true + }, + ManifestEntry{ + .version = 9288, + .description = "2024_11_10_zone_player_partitioning.sql", + .check = "SHOW CREATE TABLE `zone`", + .condition = "missing", + .match = "shard_at_player_count", + .sql = R"( +ALTER TABLE `zone` +ADD COLUMN `shard_at_player_count` int(11) NULL DEFAULT 0 AFTER `seconds_before_idle`; )", .content_schema_update = true } diff --git a/common/database_instances.cpp b/common/database_instances.cpp index e9068a5342..2e3b4fd0a0 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -114,7 +114,9 @@ bool Database::CheckInstanceExpired(uint16 instance_id) timeval tv{}; gettimeofday(&tv, nullptr); - return (i.start_time + i.duration) <= tv.tv_sec; + // Use uint64_t for the addition to prevent overflow + uint64_t expiration_time = static_cast(i.start_time) + static_cast(i.duration); + return expiration_time <= tv.tv_sec; } bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) @@ -469,15 +471,13 @@ void Database::AssignRaidToInstance(uint32 raid_id, uint32 instance_id) void Database::DeleteInstance(uint16 instance_id) { + // I'm not sure why this isn't in here but we should add it in a later change and make sure it's tested + // InstanceListRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id)); InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id)); - RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); - SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); - DynamicZoneMembersRepository::DeleteByInstance(*this, instance_id); DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); - CharacterCorpsesRepository::BuryInstance(*this, instance_id); } diff --git a/common/emu_oplist.h b/common/emu_oplist.h index aa8190720d..e41e4994b0 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -35,7 +35,7 @@ N(OP_AltCurrencyMerchantRequest), N(OP_AltCurrencyPurchase), N(OP_AltCurrencyReclaim), N(OP_AltCurrencySell), -N(OP_AltCurrencySellSelection), // Used by eqstr_us.txt 8066, 8068, 8069 +N(OP_AltCurrencySellSelection), // Used by eqstr_us.txt 8066, 8068, 8069 N(OP_Animation), N(OP_AnnoyingZoneUnknown), N(OP_ApplyPoison), @@ -400,6 +400,8 @@ N(OP_PetitionSearchText), N(OP_PetitionUnCheckout), N(OP_PetitionUpdate), N(OP_PickPocket), +N(OP_PickZone), +N(OP_PickZoneWindow), N(OP_PlayerProfile), N(OP_PlayerStateAdd), N(OP_PlayerStateRemove), @@ -514,7 +516,7 @@ N(OP_ShopPlayerSell), N(OP_ShopSendParcel), N(OP_ShopDeleteParcel), N(OP_ShopRespondParcel), -N(OP_ShopRetrieveParcel), +N(OP_ShopRetrieveParcel), N(OP_ShopParcelIcon), N(OP_ShopRequest), N(OP_SimpleMessage), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 33e8606545..e4e3c28f81 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -6440,6 +6440,26 @@ struct BuylineItemDetails_Struct { uint32 item_quantity; }; +struct PickZoneEntry_Struct { + int16 zone_id; + int16 unknown; + int32 player_count; + int32 instance_id; +}; + +struct PickZoneWindow_Struct { + char padding000[64]; + int64 session_id; + int8 option_count; + char padding073[23]; + PickZoneEntry_Struct entries[10]; +}; + +struct PickZone_Struct { + int64 session_id; + int32 selection_id; +}; + // Restore structure packing to default #pragma pack() diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index a19cb79a38..38212acb91 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -250,6 +250,7 @@ IN(OP_TraderBuy, TraderBuy_Struct); IN(OP_Trader, Trader_ShowItems_Struct); IN(OP_GMFind, GMSummon_Struct); IN(OP_PickPocket, PickPocket_Struct); +IN(OP_PickZone, PickZone_Struct); IN(OP_Bind_Wound, BindWound_Struct); INr(OP_TrackTarget); INr(OP_Track); diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 0e23c0b85f..3ba11466d6 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -117,6 +117,7 @@ class BaseZoneRepository { int32_t min_lava_damage; uint8_t idle_when_empty; uint32_t seconds_before_idle; + int32_t shard_at_player_count; }; static std::string PrimaryKey() @@ -225,6 +226,7 @@ class BaseZoneRepository { "min_lava_damage", "idle_when_empty", "seconds_before_idle", + "shard_at_player_count", }; } @@ -329,6 +331,7 @@ class BaseZoneRepository { "min_lava_damage", "idle_when_empty", "seconds_before_idle", + "shard_at_player_count", }; } @@ -467,6 +470,7 @@ class BaseZoneRepository { e.min_lava_damage = 10; e.idle_when_empty = 1; e.seconds_before_idle = 60; + e.shard_at_player_count = 0; return e; } @@ -601,6 +605,7 @@ class BaseZoneRepository { e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; + e.shard_at_player_count = row[98] ? static_cast(atoi(row[98])) : 0; return e; } @@ -731,6 +736,7 @@ class BaseZoneRepository { v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage)); v.push_back(columns[96] + " = " + std::to_string(e.idle_when_empty)); v.push_back(columns[97] + " = " + std::to_string(e.seconds_before_idle)); + v.push_back(columns[98] + " = " + std::to_string(e.shard_at_player_count)); auto results = db.QueryDatabase( fmt::format( @@ -850,6 +856,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.min_lava_damage)); v.push_back(std::to_string(e.idle_when_empty)); v.push_back(std::to_string(e.seconds_before_idle)); + v.push_back(std::to_string(e.shard_at_player_count)); auto results = db.QueryDatabase( fmt::format( @@ -977,6 +984,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.min_lava_damage)); v.push_back(std::to_string(e.idle_when_empty)); v.push_back(std::to_string(e.seconds_before_idle)); + v.push_back(std::to_string(e.shard_at_player_count)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -1108,6 +1116,7 @@ class BaseZoneRepository { e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; + e.shard_at_player_count = row[98] ? static_cast(atoi(row[98])) : 0; all_entries.push_back(e); } @@ -1230,6 +1239,7 @@ class BaseZoneRepository { e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; + e.shard_at_player_count = row[98] ? static_cast(atoi(row[98])) : 0; all_entries.push_back(e); } @@ -1402,6 +1412,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.min_lava_damage)); v.push_back(std::to_string(e.idle_when_empty)); v.push_back(std::to_string(e.seconds_before_idle)); + v.push_back(std::to_string(e.shard_at_player_count)); auto results = db.QueryDatabase( fmt::format( @@ -1522,6 +1533,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.min_lava_damage)); v.push_back(std::to_string(e.idle_when_empty)); v.push_back(std::to_string(e.seconds_before_idle)); + v.push_back(std::to_string(e.shard_at_player_count)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/character_data_repository.h b/common/repositories/character_data_repository.h index 249218279d..74f884cf36 100644 --- a/common/repositories/character_data_repository.h +++ b/common/repositories/character_data_repository.h @@ -80,6 +80,93 @@ class CharacterDataRepository: public BaseCharacterDataRepository { return l.empty() ? CharacterDataRepository::NewEntity() : l.front(); } + + struct InstancePlayerCount { + int32_t instance_id; + uint32_t zone_id; + uint32_t player_count; + }; + + static std::vector GetInstanceZonePlayerCounts(Database& db, int zone_id) { + std::vector zone_player_counts; + + uint64_t shard_instance_duration = 3155760000; + + auto query = fmt::format(SQL( + SELECT + zone_id, + 0 AS instance_id, + COUNT(id) AS player_count + FROM + character_data + WHERE + zone_instance = 0 + AND zone_id = {} + AND last_login >= UNIX_TIMESTAMP(NOW()) - 600 + GROUP BY + zone_id + ORDER BY + zone_id, player_count DESC + ), zone_id); + + auto results = db.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + InstancePlayerCount e{}; + e.zone_id = std::stoi(row[0]); + e.instance_id = 0; + e.player_count = std::stoi(row[2]); + zone_player_counts.push_back(e); + } + + if (zone_player_counts.empty()) { + InstancePlayerCount e{}; + e.zone_id = zone_id; + e.instance_id = 0; + e.player_count = 0; + zone_player_counts.push_back(e); + } + + // duration 3155760000 is for shards explicitly + query = fmt::format( + SQL( + SELECT + i.id AS instance_id, + i.zone AS zone_id, + COUNT(c.id) AS player_count + FROM + instance_list i + LEFT JOIN + character_data c + ON + i.zone = c.zone_id + AND i.id = c.zone_instance + AND c.last_login >= UNIX_TIMESTAMP(NOW()) - 600 + AND (i.start_time + i.duration >= UNIX_TIMESTAMP(NOW()) OR i.never_expires = 0) + AND i.duration = {} + WHERE + i.zone IS NOT NULL AND i.zone = {} + GROUP BY + i.id, i.zone, i.version + ORDER BY + i.id ASC; + ), shard_instance_duration, zone_id + ); + + results = db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + return zone_player_counts; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + InstancePlayerCount e{}; + e.instance_id = std::stoi(row[0]); + e.zone_id = std::stoi(row[1]); + e.player_count = std::stoi(row[2]); + zone_player_counts.push_back(e); + } + + return zone_player_counts; + } }; #endif //EQEMU_CHARACTER_DATA_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 60560345ee..c0682979b4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -372,6 +372,7 @@ RULE_INT(Zone, FishingChance, 399, "Chance of fishing from zone table vs global RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross zone spells (cast/remove) to affect bots") RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries") RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets") +RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu") RULE_CATEGORY_END() RULE_CATEGORY(Map) diff --git a/common/version.h b/common/version.h index 192a206141..9ca78821e1 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9287 +#define CURRENT_BINARY_DATABASE_VERSION 9288 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 0995b83bab..a63916911d 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -566,7 +566,7 @@ OP_RaidUpdate=0x3973 OP_RaidJoin=0x0000 OP_RaidDelegateAbility=0x2b33 OP_MarkRaidNPC=0x5a58 -OP_RaidClearNPCMarks=0x20d3 +OP_RaidClearNPCMarks=0x20d3 # Button-push commands OP_Taunt=0x2703 @@ -728,3 +728,6 @@ OP_InitialHPUpdate=0x0000 #aura related OP_UpdateAura=0x1456 OP_RemoveTrap=0x71da + +OP_PickZoneWindow=0x72d8 +OP_PickZone=0xaaba diff --git a/zone/client.cpp b/zone/client.cpp index c989468469..be693e453e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13037,19 +13037,19 @@ void Client::ClientToNpcAggroProcess() const std::vector& Client::GetInventorySlots() { static const std::vector> slots = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { 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::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 }, + {EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END}, + {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::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}, }; static std::vector slot_ids; if (slot_ids.empty()) { - for (const auto& [begin, end] : slots) { + for (const auto &[begin, end]: slots) { for (int16 slot_id = begin; slot_id <= end; ++slot_id) { slot_ids.emplace_back(slot_id); } @@ -13058,3 +13058,51 @@ const std::vector& Client::GetInventorySlots() return slot_ids; } + +void Client::ShowZoneShardMenu() +{ + auto z = GetZone(GetZoneID()); + if (z && !z->shard_at_player_count) { + return; + } + + auto results = CharacterDataRepository::GetInstanceZonePlayerCounts(database, GetZoneID()); + LogZoning("Zone sharding results count [{}]", results.size()); + + if (results.empty()) { + Message(Chat::White, "No zone shards found."); + return; + } + + if (!results.empty()) { + Message(Chat::White, "Available Zone Shards:"); + } + + int number = 1; + for (auto &e: results) { + std::string teleport_link = Saylink::Silent( + fmt::format("#zoneshard {} {}", e.zone_id, (e.instance_id == 0 ? -1 : e.instance_id)), + "Teleport" + ); + + std::string yours; + if (e.zone_id == GetZoneID() && e.instance_id == GetInstanceID()) { + teleport_link = "Teleport"; + yours = " (Yours)"; + } + + Message( + Chat::White, fmt::format( + " --> [{}] #{} {} ({}) [{}/{}] players {}", + teleport_link, + number, + z->long_name, + e.instance_id, + e.player_count, + z->shard_at_player_count, + yours + ).c_str() + ); + number++; + } +} diff --git a/zone/client.h b/zone/client.h index 03202af2f9..a60b01b43e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2236,6 +2236,7 @@ class Client : public Mob public: const std::string &GetMailKeyFull() const; const std::string &GetMailKey() const; + void ShowZoneShardMenu(); }; #endif diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4175d56ff3..55085620ff 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -339,6 +339,7 @@ void MapOpcodes() ConnectedOpcodes[OP_PlayerStateAdd] = &Client::Handle_OP_PlayerStateAdd; ConnectedOpcodes[OP_PlayerStateRemove] = &Client::Handle_OP_PlayerStateRemove; ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; + ConnectedOpcodes[OP_PickZone] = &Client::Handle_OP_PickZone; ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; @@ -941,6 +942,11 @@ void Client::CompleteConnect() ShowDevToolsMenu(); } + auto z = GetZone(GetZoneID(), GetInstanceVersion()); + if (z && z->shard_at_player_count > 0 && !RuleB(Zone, ZoneShardQuestMenuOnly)) { + ShowZoneShardMenu(); + } + // shared tasks memberlist if (RuleB(TaskSystem, EnableTaskSystem) && GetTaskState()->HasActiveSharedTask()) { @@ -11839,6 +11845,17 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) SendPickPocketResponse(victim, 0, PickPocketFailed); } +void Client::Handle_OP_PickZone(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PickZone_Struct)) { + LogDebug("Size mismatch in OP_PickZone expected [{}] got [{}]", sizeof(PickZone_Struct), app->size); + DumpPacket(app); + return; + } + + // handle +} + void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 57a4bfcf48..4ef62bdf9d 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -242,6 +242,7 @@ void Handle_OP_PlayerStateAdd(const EQApplicationPacket *app); void Handle_OP_PlayerStateRemove(const EQApplicationPacket *app); void Handle_OP_PickPocket(const EQApplicationPacket *app); + void Handle_OP_PickZone(const EQApplicationPacket *app); void Handle_OP_PopupResponse(const EQApplicationPacket *app); void Handle_OP_PotionBelt(const EQApplicationPacket *app); void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); diff --git a/zone/command.cpp b/zone/command.cpp index 513cd02180..1007e09d32 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -243,6 +243,7 @@ int command_init(void) command_add("zone", "[Zone ID|Zone Short Name] [X] [Y] [Z] - Teleport to specified Zone by ID or Short Name (coordinates are optional)", AccountStatus::Guide, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) || command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) || + command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) ) { @@ -936,4 +937,5 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/zonebootup.cpp" #include "gm_commands/zoneshutdown.cpp" #include "gm_commands/zone_instance.cpp" +#include "gm_commands/zone_shard.cpp" #include "gm_commands/zsave.cpp" diff --git a/zone/command.h b/zone/command.h index d59fdf9a5e..1899095169 100644 --- a/zone/command.h +++ b/zone/command.h @@ -195,6 +195,7 @@ void command_wpadd(Client *c, const Seperator *sep); void command_worldwide(Client *c, const Seperator *sep); void command_zone(Client *c, const Seperator *sep); void command_zone_instance(Client *c, const Seperator *sep); +void command_zone_shard(Client *c, const Seperator *sep); void command_zonebootup(Client *c, const Seperator *sep); void command_zoneshutdown(Client *c, const Seperator *sep); void command_zopp(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/zone_shard.cpp b/zone/gm_commands/zone_shard.cpp new file mode 100644 index 0000000000..07ef0541e7 --- /dev/null +++ b/zone/gm_commands/zone_shard.cpp @@ -0,0 +1,137 @@ +#include "../client.h" + +void command_zone_shard(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + if (!RuleB(Zone, ZoneShardQuestMenuOnly)) { + c->ShowZoneShardMenu(); + } + + return; + } + + if (c->GetAggroCount() > 0) { + c->Message(Chat::White, "You cannot request a shard change while in combat."); + return; + } + + std::string zone_input = sep->arg[1]; + uint32 zone_id = 0; + + // if input is id + if (Strings::IsNumber(zone_input)) { + zone_id = Strings::ToInt(zone_input); + + // validate + if (zone_id != 0 && !GetZone(zone_id)) { + c->Message(Chat::White, fmt::format("Could not find zone by id [{}]", zone_id).c_str()); + return; + } + } + else { + // validate + if (!zone_store.GetZone(zone_input)) { + c->Message(Chat::White, fmt::format("Could not find zone by short_name [{}]", zone_input).c_str()); + return; + } + + // validate we got id + zone_id = ZoneID(zone_input); + if (zone_id == 0) { + c->Message(Chat::White, fmt::format("Could not find zone id by short_name [{}]", zone_input).c_str()); + return; + } + } + + auto z = GetZone(zone_id); + if (z && z->shard_at_player_count == 0) { + c->Message(Chat::White, "Zone does not have sharding enabled."); + return; + } + + auto instance_id = sep->arg[2] ? Strings::ToBigInt(sep->arg[2]) : 0; + if (instance_id < -1) { + c->Message(Chat::White, "You must enter a valid Instance ID."); + return; + } + + if (zone_id == c->GetZoneID() && c->GetInstanceID() == instance_id) { + c->Message(Chat::White, "You are already in this shard."); + return; + } + + if (zone_id != c->GetZoneID()) { + c->Message(Chat::White, "You must request a shard change from the zone you are currently in."); + return; + } + + auto results = CharacterDataRepository::GetInstanceZonePlayerCounts(database, c->GetZoneID()); + if (results.size() <= 1) { + c->Message(Chat::White, "No shards found."); + return; + } + + if (instance_id > 0) { + if (!database.CheckInstanceExists(instance_id)) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} does not exist.", + instance_id + ).c_str() + ); + return; + } + + auto instance_zone_id = database.GetInstanceZoneID(instance_id); + if (!instance_zone_id) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} not found or zone is set to null.", + instance_id + ).c_str() + ); + return; + } + + if (instance_zone_id != zone_id) { + c->Message( + Chat::White, + fmt::format( + "Instance Zone ID {} does not match zone ID {}.", + instance_id, + zone_id + ).c_str() + ); + return; + } + + if (!database.CheckInstanceByCharID(instance_id, c->CharacterID())) { + database.AddClientToInstance(instance_id, c->CharacterID()); + } + + if (!database.VerifyInstanceAlive(instance_id, c->CharacterID())) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} expired.", + instance_id + ).c_str() + ); + return; + } + } + + c->MovePC( + zone_id, + instance_id, + c->GetX(), + c->GetY(), + c->GetZ(), + c->GetHeading(), + 0, + ZoneSolicited + ); +} diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 13982fcc06..97747d6425 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3436,13 +3436,14 @@ void Lua_Client::AreaTaunt(float range, int bonus_hate) entity_list.AETaunt(self, range, bonus_hate); } -luabind::object Lua_Client::GetInventorySlots(lua_State* L) { +luabind::object Lua_Client::GetInventorySlots(lua_State* L) +{ auto lua_table = luabind::newtable(L); if (d_) { - auto self = reinterpret_cast(d_); - int index = 1; - for (const int16& slot_id : self->GetInventorySlots()) { + auto self = reinterpret_cast(d_); + int index = 1; + for (const int16 &slot_id: self->GetInventorySlots()) { lua_table[index] = slot_id; index++; } @@ -3451,6 +3452,12 @@ luabind::object Lua_Client::GetInventorySlots(lua_State* L) { return lua_table; } +void Lua_Client::ShowZoneShardMenu() +{ + Lua_Safe_Call_Void(); + self->ShowZoneShardMenu(); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3970,6 +3977,7 @@ luabind::scope lua_register_client() { .def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint) .def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix) .def("SetZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::SetZoneFlag) + .def("ShowZoneShardMenu", (void(Lua_Client::*)(void))&Lua_Client::ShowZoneShardMenu) .def("Signal", (void(Lua_Client::*)(int))&Lua_Client::Signal) .def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit) .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) diff --git a/zone/lua_client.h b/zone/lua_client.h index fb012b3bca..4374fe199b 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -587,6 +587,7 @@ class Lua_Client : public Lua_Mob void DialogueWindow(std::string markdown); bool ReloadDataBuckets(); + void ShowZoneShardMenu(); Lua_Expedition CreateExpedition(luabind::object expedition_info); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index c6a1ab6e20..3e8325e4f2 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -915,7 +915,8 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("Marquee", static_cast(OP_Marquee)), luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), luabind::value("GuildPromote", static_cast(OP_GuildPromote)), - luabind::value("Fling", static_cast(OP_Fling)) + luabind::value("Fling", static_cast(OP_Fling)), + luabind::value("PickZoneWindow", static_cast(OP_PickZoneWindow)) )]; } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index c41b39ba58..fdeece8f80 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1756,6 +1756,11 @@ int Perl_Client_GetClientMaxLevel(Client* self) return self->GetClientMaxLevel(); } +void Perl_Client_ShowZoneShardMenu(Client* self) // @categories Script Utility +{ + self->ShowZoneShardMenu(); +} + DynamicZoneLocation GetDynamicZoneLocationFromHash(perl::hash table) { // dynamic zone helper method, defaults invalid/missing keys to 0 @@ -3742,6 +3747,7 @@ void perl_register_client() package.add("SetTitleSuffix", (void(*)(Client*, std::string))&Perl_Client_SetTitleSuffix); package.add("SetTitleSuffix", (void(*)(Client*, std::string, bool))&Perl_Client_SetTitleSuffix); package.add("SetZoneFlag", &Perl_Client_SetZoneFlag); + package.add("ShowZoneShardMenu", &Perl_Client_ShowZoneShardMenu); package.add("Signal", &Perl_Client_Signal); package.add("SignalClient", &Perl_Client_SignalClient); package.add("SilentMessage", &Perl_Client_SilentMessage); diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 225d21a91c..a02ab943a4 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -778,6 +778,66 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z } } + // zone sharding + if (zoneID == zd->zoneidnumber && + instance_id == 0 && + zd->shard_at_player_count > 0) { + + bool found_shard = false; + auto results = CharacterDataRepository::GetInstanceZonePlayerCounts(database, zoneID); + + LogZoning("Zone sharding results count [{}]", results.size()); + + uint64_t shard_instance_duration = 3155760000; + + for (auto &e: results) { + LogZoning( + "Zone sharding results [{}] ({}) instance_id [{}] player_count [{}]", + ZoneName(e.zone_id) ? ZoneName(e.zone_id) : "Unknown", + e.zone_id, + e.instance_id, + e.player_count + ); + + if (e.player_count < zd->shard_at_player_count) { + instance_id = e.instance_id; + + database.AddClientToInstance(instance_id, CharacterID()); + + LogZoning( + "Client [{}] attempting zone to sharded zone > instance_id [{}] zone [{}] ({})", + GetCleanName(), + instance_id, + ZoneName(zoneID) ? ZoneName(zoneID) : "Unknown", + zoneID + ); + + found_shard = true; + break; + } + } + + if (!found_shard) { + uint16 new_instance_id = 0; + database.GetUnusedInstanceID(new_instance_id); + database.CreateInstance(new_instance_id, zoneID, zd->version, shard_instance_duration); + database.AddClientToInstance(new_instance_id, CharacterID()); + instance_id = new_instance_id; + LogZoning( + "Client [{}] creating new sharded zone > instance_id [{}] zone [{}] ({})", + GetCleanName(), + new_instance_id, + ZoneName(zoneID) ? ZoneName(zoneID) : "Unknown", + zoneID + ); + } + } + + // passed from zone shard request to normal zone + if (instance_id == -1) { + instance_id = 0; + } + LogInfo( "Client [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]", GetCleanName(), From c2ebc2540a52798e10237b864772415b7cbbfca9 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:45:03 -0400 Subject: [PATCH 52/73] [Fix] Update a few Bazaar RoF2 routines for memory leaks (#4592) Memory leak repairs --- common/patches/rof2.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index ef27f06c24..783a9d77fa 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -433,7 +433,9 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon); VARSTRUCT_SKIP_TYPE(uint32, eq); } + dest->QueuePacket(outapp.get()); + safe_delete(in); break; } default: { @@ -468,8 +470,8 @@ namespace RoF2 } auto p_size = 41 * results.size() + name_size + 14; - auto buffer = std::make_unique(p_size); - auto bufptr = buffer.get(); + auto buffer = new char[p_size]; + auto bufptr = buffer; VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); VARSTRUCT_ENCODE_TYPE(uint16, bufptr, results[0].trader_zone_id); @@ -489,8 +491,9 @@ namespace RoF2 safe_delete_array(in->pBuffer); in->size = p_size; - in->pBuffer = (uchar *) buffer.get(); + in->pBuffer = (uchar*)buffer; dest->QueuePacket(in); + safe_delete(in); break; } @@ -500,9 +503,9 @@ namespace RoF2 break; } case WelcomeMessage: { - auto buffer = std::make_unique(sizeof(structs::BazaarWelcome_Struct)); + auto buffer = new char[sizeof(structs::BazaarWelcome_Struct)]; auto emu = (BazaarWelcome_Struct *) in->pBuffer; - auto eq = (structs::BazaarWelcome_Struct *) buffer.get(); + auto eq = (structs::BazaarWelcome_Struct *) buffer; eq->action = structs::RoF2BazaarTraderBuyerActions::WelcomeMessage; eq->num_of_traders = emu->traders; @@ -511,10 +514,11 @@ namespace RoF2 safe_delete_array(in->pBuffer); in->SetOpcode(OP_TraderShop); in->size = sizeof(structs::BazaarWelcome_Struct); - in->pBuffer = (uchar *) buffer.get(); + in->pBuffer = (uchar *)buffer; LogTrading("(RoF2) WelcomeMessage action [{}]", action); dest->QueuePacket(in); + safe_delete(in); break; } @@ -892,7 +896,9 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_instance_id); VARSTRUCT_ENCODE_STRING(eq, b.buyer_name.c_str()); } + dest->QueuePacket(outapp.get()); + safe_delete(inapp); break; } case Barter_RemoveFromMerchantWindow: { @@ -963,6 +969,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity); dest->QueuePacket(outapp.get()); + safe_delete(inapp); break; } default: { From 035c51944f6e969592cb19dd9a6ee7b1bb87207f Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Jan 2025 17:36:48 -0500 Subject: [PATCH 53/73] [Quest API] Add SetAAEXPPercentage to Perl/Lua (#4597) --- zone/client.cpp | 16 ++++++++++++++++ zone/client.h | 2 ++ zone/client_packet.cpp | 3 ++- zone/lua_client.cpp | 7 +++++++ zone/lua_client.h | 1 + zone/perl_client.cpp | 6 ++++++ 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index be693e453e..6b8fb31444 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13106,3 +13106,19 @@ void Client::ShowZoneShardMenu() number++; } } + +void Client::SetAAEXPPercentage(uint8 percentage) +{ + const uint32 before_percentage = m_epp.perAA; + + if (before_percentage > 0 && percentage == 0) { + MessageString(Chat::White, AA_OFF); + } else if (before_percentage == 0 && percentage > 0) { + MessageString(Chat::White, AA_ON); + } + + m_epp.perAA = EQ::Clamp(static_cast(percentage), 0, 100); + + SendAlternateAdvancementStats(); + SendAlternateAdvancementTable(); +} diff --git a/zone/client.h b/zone/client.h index a60b01b43e..b050c34fa7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -692,6 +692,8 @@ class Client : public Mob void SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version = -1); void SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version = -1); + void SetAAEXPPercentage(uint8 percentage); + bool UpdateLDoNPoints(uint32 theme_id, int points); void SetLDoNPoints(uint32 theme_id, uint32 points); void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 55085620ff..ef671ef124 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1864,8 +1864,9 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app) PurchaseAlternateAdvancementRank(action->ability); } else if (action->action == aaActionDisableEXP) { //Turn Off AA Exp - if (m_epp.perAA > 0) + if (m_epp.perAA > 0) { MessageString(Chat::White, AA_OFF); + } m_epp.perAA = 0; SendAlternateAdvancementStats(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 97747d6425..566510cf9d 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3458,6 +3458,12 @@ void Lua_Client::ShowZoneShardMenu() self->ShowZoneShardMenu(); } +void Lua_Client::SetAAEXPPercentage(uint8 percentage) +{ + Lua_Safe_Call_Void(); + self->SetAAEXPPercentage(percentage); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3890,6 +3896,7 @@ luabind::scope lua_register_client() { .def("SetAAEXPModifier", (void(Lua_Client::*)(float))&Lua_Client::SetAAEXPModifier) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float))&Lua_Client::SetAAEXPModifier) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float,int16))&Lua_Client::SetAAEXPModifier) + .def("SetAAEXPPercentage", (void(Lua_Client::*)(uint8))&Lua_Client::SetAAEXPPercentage) .def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints) .def("SetAATitle", (void(Lua_Client::*)(std::string))&Lua_Client::SetAATitle) .def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle) diff --git a/zone/lua_client.h b/zone/lua_client.h index 4374fe199b..d86705f2e7 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -510,6 +510,7 @@ class Lua_Client : public Lua_Mob void AreaTaunt(float range); void AreaTaunt(float range, int bonus_hate); luabind::object GetInventorySlots(lua_State* L); + void SetAAEXPPercentage(uint8 percentage); void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index fdeece8f80..d86c70bf40 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3229,6 +3229,11 @@ perl::array Perl_Client_GetInventorySlots(Client* self) return result; } +void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage) +{ + self->SetAAEXPPercentage(percentage); +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3657,6 +3662,7 @@ void perl_register_client() package.add("SetAAEXPModifier", (void(*)(Client*, float))&Perl_Client_SetAAEXPModifier); package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float))&Perl_Client_SetAAEXPModifier); package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float, int16))&Perl_Client_SetAAEXPModifier); + package.add("SetAAEXPPercentage", &Perl_Client_SetAAEXPPercentage); package.add("SetAAPoints", &Perl_Client_SetAAPoints); package.add("SetAATitle", (void(*)(Client*, std::string))&Perl_Client_SetAATitle); package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle); From d47bf687d0ea39ccd2f86bb2ec23022fc0a34330 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 19 Jan 2025 18:37:09 -0400 Subject: [PATCH 54/73] [Fix] Update trader add/remove packets to limits for RoF2 (#4595) * Update trader add/remove to limit for RoF2 Update trader add/remove to limit for RoF2 * Update for feedback --------- Co-authored-by: Mitch Freeman --- zone/client.h | 5 +++++ zone/trading.cpp | 2 ++ zone/worldserver.cpp | 27 ++++++++++++++++++--------- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/zone/client.h b/zone/client.h index b050c34fa7..75c48a4941 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1130,6 +1130,10 @@ class Client : public Mob void SetTrader(bool status) { trader = status; } uint16 GetTraderID() { return trader_id; } void SetTraderID(uint16 id) { trader_id = id; } + void SetTraderCount(uint32 no) { m_trader_count = no; } + uint32 GetTraderCount() { return m_trader_count; } + void IncrementTraderCount() { m_trader_count += 1; } + void DecrementTraderCount() { m_trader_count > 0 ? m_trader_count -= 1 : m_trader_count = 0; } eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; } @@ -1948,6 +1952,7 @@ class Client : public Mob uint8 firstlogon; uint32 mercid; // current merc uint8 mercSlot; // selected merc slot + uint32 m_trader_count{}; uint32 m_buyer_id; uint32 m_barter_time; int32 m_parcel_platinum; diff --git a/zone/trading.cpp b/zone/trading.cpp index 785fd2aec6..a64dc33e9d 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -3225,6 +3225,8 @@ void Client::SendBulkBazaarTraders() EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit ); + SetTraderCount(results.count); + auto p_size = 4 + 12 * results.count + results.name_length; auto buffer = std::make_unique(p_size); memset(buffer.get(), 0, p_size); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 768fe4a4fc..8112d422cd 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3912,29 +3912,38 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) auto in = (TraderMessaging_Struct *) pack->pBuffer; for (auto const &c: entity_list.GetClientList()) { if (c.second->ClientVersion() >= EQ::versions::ClientVersion::RoF2) { - auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); - auto out = (BecomeTrader_Struct *) outapp->pBuffer; + auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); + auto out = (BecomeTrader_Struct *) outapp->pBuffer; + + out->entity_id = in->entity_id; + out->zone_id = in->zone_id; + out->zone_instance_id = in->instance_id; + out->trader_id = in->trader_id; + strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); + switch (in->action) { case TraderOn: { out->action = AddTraderToBazaarWindow; + if (c.second->GetTraderCount() < + EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + c.second->IncrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + break; } case TraderOff: { out->action = RemoveTraderFromBazaarWindow; + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); break; } default: { out->action = 0; + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); } } - out->entity_id = in->entity_id; - out->zone_id = in->zone_id; - out->zone_instance_id = in->instance_id; - out->trader_id = in->trader_id; - strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); - - c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); safe_delete(outapp); } if (zone && zone->GetZoneID() == Zones::BAZAAR && in->instance_id == zone->GetInstanceID()) { From f21cc170df7284e38227ac0d789ab7425d18e947 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 19 Jan 2025 20:10:19 -0400 Subject: [PATCH 55/73] [Feature] Evolving Item Support for RoF2 (#4496) * basic evolving items framework created * Implement evolving tab in the inventory window * Implement experience and number of kills * Move zone evolving map to a evolvingitemsmanager class * rework gm commands * rework GetInventory * wip * wip loot testing * Fix Duplicate Message * reworked evolving item looting, swapping, etc * reworked const functions for evolving methods * Functioning Player Trade of evolving items test item_id is 89550 * First pass of Final Result link working * First pass of item upgrading when reaching 100% * Add strings and logic for displaying the evolving item xp transfer window in Corathus * Prototype of xp transfer window sending items * WIP for evolve xp transfer * WIP for evolve xp transfer. First tests passed * XP Transfer Cleanup * XP Transfer Cleanup * Add Rule for evolving items equip timer/ default is 30 secs * Add logging and player events Add logging and player events * Formatting * Database updates * Updates for linux build * Perl/Cleanup * Command cleanup * Lua * Added a crash condition check if final item id is blank or not found. * Review Changes Updates to resolve review comments and a rebase. * migrate to content_db for items_evolving_details migrate to content_db for items_evolving_details * Simplify, don't hit database unless evolving * Update 2025_01_19_items_evolving_details.sql * Update client.cpp * Update manifest with items_evolving_details * character_id vs char_id * Remove _Struct from structs * Remove license header in evolving.cpp * Move evolving constants from eq_constants.h to evolving.h since it is more specific * Update database_schema.h * General cleanup * Be more specific with `evolving_items` vs `evolving` --------- Co-authored-by: Kinglykrab Co-authored-by: Akkadius --- client_files/export/main.cpp | 14 +- client_files/import/main.cpp | 14 +- common/CMakeLists.txt | 6 + common/database/database_update_manifest.cpp | 484 ++++++++++++++++++ common/database_schema.h | 3 + common/emu_oplist.h | 1 + common/eq_constants.h | 1 + common/eq_packet_structs.h | 64 +++ common/eqemu_logsys.h | 4 +- common/eqemu_logsys_log_aliases.h | 10 + common/events/player_event_logs.cpp | 1 + common/events/player_events.h | 27 +- common/evolving_items.cpp | 303 +++++++++++ common/evolving_items.h | 67 +++ common/inventory_profile.cpp | 194 +++++++ common/inventory_profile.h | 9 + common/item_instance.cpp | 105 ++-- common/item_instance.h | 104 ++-- common/patches/rof2.cpp | 78 ++- common/patches/rof2_ops.h | 1 + common/patches/rof2_structs.h | 41 +- ...base_character_evolving_items_repository.h | 475 +++++++++++++++++ .../base_items_evolving_details_repository.h | 451 ++++++++++++++++ .../character_evolving_items_repository.h | 66 +++ .../items_evolving_details_repository.h | 14 + common/ruletypes.h | 7 + common/shareddb.cpp | 80 ++- common/shareddb.h | 4 +- common/version.h | 2 +- shared_memory/main.cpp | 14 +- utils/patches/patch_RoF2.conf | 3 + world/main.cpp | 2 + zone/CMakeLists.txt | 1 + zone/attack.cpp | 2 +- zone/client.cpp | 3 +- zone/client.h | 19 +- zone/client_evolving_items.cpp | 451 ++++++++++++++++ zone/client_packet.cpp | 45 +- zone/client_packet.h | 1 + zone/command.cpp | 2 + zone/command.h | 2 + zone/doors.cpp | 5 + zone/doors.h | 2 + zone/exp.cpp | 11 +- zone/gm_commands/evolving_items.cpp | 301 +++++++++++ zone/inventory.cpp | 26 +- zone/lua_iteminst.cpp | 111 +++- zone/lua_iteminst.h | 16 +- zone/main.cpp | 7 + zone/perl_questitem.cpp | 96 +++- zone/string_ids.h | 5 + zone/trading.cpp | 3 + zone/zonedb.cpp | 2 +- 53 files changed, 3573 insertions(+), 187 deletions(-) create mode 100644 common/evolving_items.cpp create mode 100644 common/evolving_items.h create mode 100644 common/repositories/base/base_character_evolving_items_repository.h create mode 100644 common/repositories/base/base_items_evolving_details_repository.h create mode 100644 common/repositories/character_evolving_items_repository.h create mode 100644 common/repositories/items_evolving_details_repository.h create mode 100644 zone/client_evolving_items.cpp create mode 100644 zone/gm_commands/evolving_items.cpp diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index e90e6c76c2..a930d54046 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -36,12 +36,14 @@ #include "../../common/file.h" #include "../../common/events/player_event_logs.h" #include "../../common/skill_caps.h" - -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +#include "../../common/evolving_items.h" + +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void ExportSpells(SharedDatabase *db); void ExportSkillCaps(SharedDatabase *db); diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 0d8010221a..74f447a231 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -30,12 +30,14 @@ #include "../../common/repositories/base_data_repository.h" #include "../../common/file.h" #include "../../common/events/player_event_logs.h" - -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +#include "../../common/evolving_items.h" + +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void ImportSpells(SharedDatabase *db); void ImportSkillCaps(SharedDatabase *db); diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 0a1766fc8c..ff130adb0a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -39,6 +39,7 @@ SET(common_sources event_sub.cpp events/player_event_logs.cpp events/player_event_discord_formatter.cpp + evolving_items.cpp expedition_lockout_timer.cpp extprofile.cpp discord/discord_manager.cpp @@ -172,6 +173,7 @@ SET(repositories repositories/base/base_character_currency_repository.h repositories/base/base_character_data_repository.h repositories/base/base_character_disciplines_repository.h + repositories/base/base_character_evolving_items_repository.h repositories/base/base_character_expedition_lockouts_repository.h repositories/base/base_character_exp_modifiers_repository.h repositories/base/base_character_inspect_messages_repository.h @@ -240,6 +242,7 @@ SET(repositories repositories/base/base_inventory_snapshots_repository.h repositories/base/base_ip_exemptions_repository.h repositories/base/base_items_repository.h + repositories/base/base_items_evolving_details_repository.h repositories/base/base_ldon_trap_entries_repository.h repositories/base/base_ldon_trap_templates_repository.h repositories/base/base_level_exp_mods_repository.h @@ -355,6 +358,7 @@ SET(repositories repositories/character_currency_repository.h repositories/character_data_repository.h repositories/character_disciplines_repository.h + repositories/character_evolving_items_repository.h repositories/character_expedition_lockouts_repository.h repositories/character_exp_modifiers_repository.h repositories/character_inspect_messages_repository.h @@ -423,6 +427,7 @@ SET(repositories repositories/inventory_snapshots_repository.h repositories/ip_exemptions_repository.h repositories/items_repository.h + repositories/items_evolving_details_repository.h repositories/ldon_trap_entries_repository.h repositories/ldon_trap_templates_repository.h repositories/level_exp_mods_repository.h @@ -555,6 +560,7 @@ SET(common_headers events/player_event_discord_formatter.h events/player_events.h event_sub.h + evolving_items.h expedition_lockout_timer.h extprofile.h faction.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index cd484122b9..8e17fb9519 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5803,6 +5803,490 @@ ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0; .sql = R"( ALTER TABLE `zone` ADD COLUMN `shard_at_player_count` int(11) NULL DEFAULT 0 AFTER `seconds_before_idle`; +)", + .content_schema_update = true + }, + ManifestEntry{ + .version = 9289, + .description = "2025_01_19_evolving_items__character_evolving_items", + .check = "SHOW TABLES LIKE 'character_evolving_items'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `character_evolving_items` ( + `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, + `character_id` INT(10) UNSIGNED NULL DEFAULT '0', + `item_id` INT(10) UNSIGNED NULL DEFAULT '0', + `activated` TINYINT(1) UNSIGNED NULL DEFAULT '0', + `equipped` TINYINT(3) UNSIGNED NULL DEFAULT '0', + `current_amount` BIGINT(20) NULL DEFAULT '0', + `progression` DOUBLE(22,0) NULL DEFAULT '0', + `final_item_id` INT(10) UNSIGNED NULL DEFAULT '0', + `deleted_at` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +AUTO_INCREMENT=1 +; +)" + }, + ManifestEntry{ + .version = 9290, + .description = "2025_01_19_evolving_items__items_evolving_details", + .check = "SHOW TABLES LIKE 'items_evolving_details'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `items_evolving_details` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `item_evo_id` int(10) unsigned DEFAULT 0 COMMENT 'items.evoid', + `item_evolve_level` int(10) unsigned DEFAULT 0 COMMENT 'items.evolvinglevel', + `item_id` int(10) unsigned DEFAULT 0 COMMENT 'items.id', + `type` int(10) unsigned DEFAULT 0, + `sub_type` int(10) unsigned DEFAULT 0, + `required_amount` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +INSERT INTO `items_evolving_details` VALUES +(1,1000,1,86900,99,1,0), +(2,1000,2,86901,99,1,0), +(3,1000,3,86902,99,1,0), +(4,1000,4,86903,99,1,0), +(5,1000,5,86904,99,1,0), +(6,1001,1,86910,99,1,0), +(7,1001,2,86911,99,1,0), +(8,1001,3,86912,99,1,0), +(9,1001,4,86913,99,1,0), +(10,1001,5,86914,99,1,0), +(11,1001,6,86915,99,1,0), +(12,1001,7,86916,99,1,0), +(13,1002,1,86920,99,1,0), +(14,1002,2,86921,99,1,0), +(15,1002,3,86922,99,1,0), +(16,1002,4,86923,99,1,0), +(17,1002,5,86924,99,1,0), +(18,1002,6,86925,99,1,0), +(19,1002,7,86926,99,1,0), +(20,1003,1,86930,99,1,0), +(21,1003,2,86931,99,1,0), +(22,1003,3,86932,99,1,0), +(23,1003,4,86933,99,1,0), +(24,1003,5,86934,99,1,0), +(25,1003,6,86935,99,1,0), +(26,1004,1,86940,99,1,0), +(27,1004,2,86941,99,1,0), +(28,1004,3,86942,99,1,0), +(29,1004,4,86943,99,1,0), +(30,1004,5,86944,99,1,0), +(31,1004,6,86945,99,1,0), +(32,1005,1,86950,99,1,0), +(33,1005,2,86951,99,1,0), +(34,1005,3,86952,99,1,0), +(35,1005,4,86953,99,1,0), +(36,1005,5,86954,99,1,0), +(37,1005,6,86955,99,1,0), +(38,1005,7,86956,99,1,0), +(39,1006,1,86960,99,1,0), +(40,1006,2,86961,99,1,0), +(41,1006,3,86962,99,1,0), +(42,1006,4,86963,99,1,0), +(43,1006,5,86964,99,1,0), +(44,1006,6,86965,99,1,0), +(45,1006,7,86966,99,1,0), +(46,1007,1,86970,99,1,0), +(47,1007,2,86971,99,1,0), +(48,1007,3,86972,99,1,0), +(49,1007,4,86973,99,1,0), +(50,1007,5,86974,99,1,0), +(51,1007,6,86975,99,1,0), +(52,1007,7,86976,99,1,0), +(53,1008,1,86980,99,1,0), +(54,1008,2,86981,99,1,0), +(55,1008,3,86982,99,1,0), +(56,1008,4,86983,99,1,0), +(57,1008,5,86984,99,1,0), +(58,1009,1,86990,99,1,0), +(59,1009,2,86991,99,1,0), +(60,1009,3,86992,99,1,0), +(61,1009,4,86993,99,1,0), +(62,1009,5,86994,99,1,0), +(63,1009,6,86995,99,1,0), +(64,1009,7,86996,99,1,0), +(65,1009,8,86997,99,1,0), +(66,1009,9,86998,99,1,0), +(67,1009,10,86999,99,1,0), +(68,1010,1,90001,99,1,0), +(69,1010,2,90002,99,1,0), +(70,1010,3,90003,99,1,0), +(71,1010,6,90006,99,1,0), +(72,1010,7,90007,99,1,0), +(73,1010,8,90008,99,1,0), +(74,1010,10,90010,99,1,0), +(75,1050,1,89270,99,1,0), +(76,1050,2,89271,99,1,0), +(77,1050,3,89272,99,1,0), +(78,1050,4,89273,99,1,0), +(79,1050,5,89274,99,1,0), +(80,1050,6,89275,99,1,0), +(81,1051,1,89280,99,1,0), +(82,1051,2,89281,99,1,0), +(83,1051,3,89282,99,1,0), +(84,1051,4,89283,99,1,0), +(85,1051,5,89284,99,1,0), +(86,1052,1,89290,99,1,0), +(87,1052,2,89291,99,1,0), +(88,1052,3,89292,99,1,0), +(89,1052,4,89293,99,1,0), +(90,1052,5,89294,99,1,0), +(91,1052,6,89295,99,1,0), +(92,1052,7,89296,99,1,0), +(93,1053,1,89300,99,1,0), +(94,1053,2,89301,99,1,0), +(95,1053,3,89302,99,1,0), +(96,1053,4,89303,99,1,0), +(97,1053,5,89304,99,1,0), +(98,1053,6,89305,99,1,0), +(99,1053,7,89306,99,1,0), +(100,1053,8,89307,99,1,0), +(101,1053,9,89308,99,1,0), +(102,1054,1,89310,99,1,0), +(103,1054,2,89311,99,1,0), +(104,1054,3,89312,99,1,0), +(105,1054,4,89313,99,1,0), +(106,1054,5,89314,99,1,0), +(107,1054,6,89315,99,1,0), +(108,1055,1,89320,99,1,0), +(109,1055,2,89321,99,1,0), +(110,1055,3,89322,99,1,0), +(111,1055,4,89323,99,1,0), +(112,1055,5,89324,99,1,0), +(113,1055,6,89325,99,1,0), +(114,1055,7,89326,99,1,0), +(115,1056,1,89330,99,1,0), +(116,1056,2,89331,99,1,0), +(117,1056,3,89332,99,1,0), +(118,1056,4,89333,99,1,0), +(119,1056,5,89334,99,1,0), +(120,1057,1,89340,99,1,0), +(121,1057,2,89341,99,1,0), +(122,1057,3,89342,99,1,0), +(123,1057,4,89343,99,1,0), +(124,1057,5,89344,99,1,0), +(125,1057,6,89345,99,1,0), +(126,1057,7,89346,99,1,0), +(127,1057,8,89347,99,1,0), +(128,1058,1,89350,99,1,0), +(129,1058,2,89351,99,1,0), +(130,1058,3,89352,99,1,0), +(131,1058,4,89353,99,1,0), +(132,1058,5,89354,99,1,0), +(133,1058,6,89355,99,1,0), +(134,1058,7,89356,99,1,0), +(135,1059,1,89360,99,1,0), +(136,1059,2,89361,99,1,0), +(137,1059,3,89362,99,1,0), +(138,1060,1,89490,99,1,0), +(139,1060,2,89491,99,1,0), +(140,1060,3,89492,99,1,0), +(141,1061,1,89500,99,1,0), +(142,1061,2,89501,99,1,0), +(143,1061,3,89502,99,1,0), +(144,1062,1,89510,99,1,0), +(145,1062,2,89511,99,1,0), +(146,1062,3,89512,99,1,0), +(147,1063,1,89520,99,1,0), +(148,1063,2,89521,99,1,0), +(149,1063,3,89522,99,1,0), +(150,1064,1,89530,99,1,0), +(151,1064,2,89531,99,1,0), +(152,1064,3,89532,99,1,0), +(153,1065,1,89540,99,1,0), +(154,1065,2,89541,99,1,0), +(155,1065,3,89542,99,1,0), +(156,1066,1,89550,3,274,500), +(157,1066,2,89551,3,274,1000), +(158,1066,3,89552,3,274,2000), +(159,1067,1,89560,99,1,0), +(160,1067,2,89561,99,1,0), +(161,1067,3,89562,99,1,0), +(162,1069,1,85571,99,1,0), +(163,1069,2,85572,99,1,0), +(164,1069,3,85573,99,1,0), +(165,1200,1,95001,99,1,0), +(166,1200,2,95002,99,1,0), +(167,1200,3,95003,99,1,0), +(168,1200,4,95004,99,1,0), +(169,1200,5,95005,99,1,0), +(170,1200,6,95006,99,1,0), +(171,1200,7,95007,99,1,0), +(172,1201,1,95008,99,1,0), +(173,1201,2,95009,99,1,0), +(174,1201,3,95010,99,1,0), +(175,1201,4,95011,99,1,0), +(176,1201,5,95012,99,1,0), +(177,1201,6,95013,99,1,0), +(178,1201,7,95014,99,1,0), +(179,1202,1,95015,99,1,0), +(180,1202,2,95016,99,1,0), +(181,1202,3,95017,99,1,0), +(182,1202,4,95018,99,1,0), +(183,1202,5,95019,99,1,0), +(184,1202,6,95020,99,1,0), +(185,1202,7,95021,99,1,0), +(186,1203,1,95022,99,1,0), +(187,1203,2,95023,99,1,0), +(188,1203,3,95024,99,1,0), +(189,1203,4,95025,99,1,0), +(190,1203,5,95026,99,1,0), +(191,1203,6,95027,99,1,0), +(192,1203,7,95028,99,1,0), +(193,1204,1,95029,99,1,0), +(194,1204,2,95030,99,1,0), +(195,1204,3,95031,99,1,0), +(196,1204,4,95032,99,1,0), +(197,1204,5,95033,99,1,0), +(198,1204,6,95034,99,1,0), +(199,1204,7,95035,99,1,0), +(200,1205,1,95036,99,1,0), +(201,1205,2,95037,99,1,0), +(202,1205,3,95038,99,1,0), +(203,1205,4,95039,99,1,0), +(204,1205,5,95040,99,1,0), +(205,1205,6,95041,99,1,0), +(206,1205,7,95042,99,1,0), +(207,1206,1,95043,99,1,0), +(208,1206,2,95044,99,1,0), +(209,1206,3,95045,99,1,0), +(210,1206,4,95046,99,1,0), +(211,1206,5,95047,99,1,0), +(212,1206,6,95048,99,1,0), +(213,1206,7,95049,99,1,0), +(214,1207,1,95050,99,1,0), +(215,1207,2,95051,99,1,0), +(216,1207,3,95052,99,1,0), +(217,1207,4,95053,99,1,0), +(218,1207,5,95054,99,1,0), +(219,1207,6,95055,99,1,0), +(220,1207,7,95056,99,1,0), +(221,1208,1,95057,99,1,0), +(222,1208,2,95058,99,1,0), +(223,1208,3,95059,99,1,0), +(224,1208,4,95060,99,1,0), +(225,1208,5,95061,99,1,0), +(226,1208,6,95062,99,1,0), +(227,1208,7,95063,99,1,0), +(228,1209,1,95064,99,1,0), +(229,1209,2,95065,99,1,0), +(230,1209,3,95066,99,1,0), +(231,1209,4,95067,99,1,0), +(232,1209,5,95068,99,1,0), +(233,1209,6,95069,99,1,0), +(234,1209,7,95070,99,1,0), +(235,1210,1,95071,99,1,0), +(236,1210,2,95072,99,1,0), +(237,1210,3,95073,99,1,0), +(238,1210,4,95074,99,1,0), +(239,1210,5,95075,99,1,0), +(240,1210,6,95076,99,1,0), +(241,1210,7,95077,99,1,0), +(242,1211,1,85612,1,1,100000), +(243,1211,2,85613,1,1,200000), +(244,1211,3,85614,1,1,300000), +(245,1214,1,80035,99,1,0), +(246,1301,1,102700,99,1,0), +(247,1301,4,102703,99,1,0), +(248,1302,1,102704,99,1,0), +(249,1302,2,102705,99,1,0), +(250,1303,1,102706,99,1,0), +(251,1303,2,102707,99,1,0), +(252,1303,3,102708,99,1,0), +(253,1304,1,102709,99,1,0), +(254,1304,5,102713,99,1,0), +(255,1305,1,102714,99,1,0), +(256,1306,1,102716,99,1,0), +(257,1306,5,102720,99,1,0), +(258,1307,1,102721,99,1,0), +(259,1307,3,102723,99,1,0), +(260,1308,1,102724,99,1,0), +(261,1309,1,102727,99,1,0), +(262,1309,2,102728,99,1,0), +(263,1310,1,102729,99,1,0), +(264,1310,3,102731,99,1,0), +(265,1311,1,102732,99,1,0), +(266,1311,4,102735,99,1,0), +(267,1312,1,102736,99,1,0), +(268,1312,3,102738,99,1,0), +(269,1313,1,102739,99,1,0), +(270,1314,1,102743,99,1,0), +(271,1314,2,102744,99,1,0), +(272,1314,3,102745,99,1,0), +(273,1315,1,102746,99,1,0), +(274,1315,2,102747,99,1,0), +(275,1316,1,102748,99,1,0), +(276,1316,5,102752,99,1,0), +(277,1317,1,102753,99,1,0), +(278,1318,1,102756,99,1,0), +(279,1319,1,102759,99,1,0), +(280,1319,3,102761,99,1,0), +(281,1320,1,102762,99,1,0), +(282,1321,1,102765,99,1,0), +(283,1321,2,102766,99,1,0), +(284,1321,3,102767,99,1,0), +(285,1322,1,102768,99,1,0), +(286,1322,2,102769,99,1,0), +(287,1322,3,102770,99,1,0), +(288,1323,1,102771,99,1,0), +(289,1324,1,102774,99,1,0), +(290,1400,1,102800,99,1,0), +(291,1401,1,102807,99,1,0), +(292,1401,7,102813,99,1,0), +(293,1402,1,102814,99,1,0), +(294,1402,7,102820,99,1,0), +(295,1403,1,102821,99,1,0), +(296,1404,1,102828,99,1,0), +(297,1405,1,102835,99,1,0), +(298,1406,1,102842,99,1,0), +(299,1408,1,109310,99,1,0), +(300,1408,5,109314,99,1,0), +(301,1409,1,109315,99,1,0), +(302,1409,5,109319,99,1,0), +(303,1410,1,109320,99,1,0), +(304,1410,5,109324,99,1,0), +(305,1411,1,109325,99,1,0), +(306,1411,5,109329,99,1,0), +(307,1412,1,109330,99,1,0), +(308,1412,5,109334,99,1,0), +(309,1413,1,109335,99,1,0), +(310,1413,5,109339,99,1,0), +(311,1414,1,109340,99,1,0), +(312,1414,5,109344,99,1,0), +(313,1415,1,109345,99,1,0), +(314,1415,5,109349,99,1,0), +(315,1416,1,109350,99,1,0), +(316,1416,2,109351,99,1,0), +(317,1416,5,109354,99,1,0), +(318,1417,1,109355,99,1,0), +(319,1417,5,109359,99,1,0), +(320,1418,1,109360,99,1,0), +(321,1418,5,109364,99,1,0), +(322,1419,1,109365,99,1,0), +(323,1419,3,109367,99,1,0), +(324,1419,5,109369,99,1,0), +(325,1420,1,109370,99,1,0), +(326,1420,5,109374,99,1,0), +(327,1421,1,109375,99,1,0), +(328,1421,5,109379,99,1,0), +(329,1422,1,109380,99,1,0), +(330,1422,5,109384,99,1,0), +(331,1423,1,109385,99,1,0), +(332,1423,2,109386,99,1,0), +(333,1423,5,109389,99,1,0), +(334,1436,1,120378,99,1,0), +(335,1436,2,120379,99,1,0), +(336,1436,3,120380,99,1,0), +(337,1436,4,120381,99,1,0), +(338,1436,5,120382,99,1,0), +(339,1436,6,120383,99,1,0), +(340,1436,7,120384,99,1,0), +(341,1436,8,120385,99,1,0), +(342,1436,9,120386,99,1,0), +(343,1436,10,120387,99,1,0), +(344,1436,11,120388,99,1,0), +(345,1436,12,120389,99,1,0), +(346,1436,13,120390,99,1,0), +(347,1436,14,120391,99,1,0), +(348,1436,15,120392,99,1,0), +(349,1436,16,120393,99,1,0), +(350,1436,17,120394,99,1,0), +(351,1436,18,120395,99,1,0), +(352,1436,19,120396,99,1,0), +(353,1436,20,120397,99,1,0), +(354,1440,1,56992,99,1,0), +(355,1440,2,56993,99,1,0), +(356,1440,3,56994,99,1,0), +(357,1440,4,56995,99,1,0), +(358,1440,5,56996,99,1,0), +(359,1441,1,132787,99,1,0), +(360,1441,2,132788,99,1,0), +(361,1441,3,132789,99,1,0), +(362,1441,4,132790,99,1,0), +(363,1441,5,132791,99,1,0), +(364,1441,6,132792,99,1,0), +(365,1441,7,132793,99,1,0), +(366,1441,8,132794,99,1,0), +(367,1441,9,132795,99,1,0), +(368,1441,10,132796,99,1,0), +(369,1441,11,132797,99,1,0), +(370,1441,13,132799,99,1,0), +(371,1441,14,132800,99,1,0), +(372,1441,15,132801,99,1,0), +(373,1441,16,132802,99,1,0), +(374,1441,17,132803,99,1,0), +(375,1441,18,132804,99,1,0), +(376,1441,19,132805,99,1,0), +(377,1441,20,132806,99,1,0), +(378,1442,1,133137,99,1,0), +(379,1442,2,133138,99,1,0), +(380,1442,3,133139,99,1,0), +(381,1442,4,133140,99,1,0), +(382,1442,10,133146,99,1,0), +(383,1442,11,133147,99,1,0), +(384,1442,12,133148,99,1,0), +(385,1442,13,133149,99,1,0), +(386,1442,14,133150,99,1,0), +(387,1442,15,133151,99,1,0), +(388,1442,16,133152,99,1,0), +(389,1442,17,133153,99,1,0), +(390,1442,18,133154,99,1,0), +(391,1442,19,133155,99,1,0), +(392,1442,20,133156,99,1,0), +(393,1443,1,133406,99,1,0), +(394,1443,2,133407,99,1,0), +(395,1443,3,133408,99,1,0), +(396,1443,4,133409,99,1,0), +(397,1443,5,133410,99,1,0), +(398,1443,6,133411,99,1,0), +(399,1443,7,133412,99,1,0), +(400,1443,8,133413,99,1,0), +(401,1443,9,133414,99,1,0), +(402,1443,10,133415,99,1,0), +(403,1443,11,133416,99,1,0), +(404,1443,12,133417,99,1,0), +(405,1443,13,133418,99,1,0), +(406,1443,14,133419,99,1,0), +(407,1443,15,133420,99,1,0), +(408,1443,16,133421,99,1,0), +(409,1443,17,133422,99,1,0), +(410,1443,18,133423,99,1,0), +(411,1443,19,133424,99,1,0), +(412,1443,20,133425,99,1,0), +(413,1444,1,94938,99,1,0), +(414,1444,2,94939,99,1,0), +(415,1444,3,94940,99,1,0), +(416,1444,4,94941,99,1,0), +(417,1444,5,94942,99,1,0), +(418,1444,6,94943,99,1,0), +(419,1444,7,94944,99,1,0), +(420,1444,8,94945,99,1,0), +(421,1444,9,94946,99,1,0), +(422,1444,10,94947,99,1,0), +(423,1444,11,94948,99,1,0), +(424,1444,12,94949,99,1,0), +(425,1444,13,94950,99,1,0), +(426,1444,14,94951,99,1,0), +(427,1444,15,94952,99,1,0), +(428,1444,16,94953,99,1,0), +(429,1444,17,94954,99,1,0), +(430,1444,18,94955,99,1,0), +(431,1444,19,94956,99,1,0), +(432,1444,20,94957,99,1,0), +(433,1445,1,98858,99,1,0), +(434,1445,2,98859,99,1,0), +(435,1445,3,98860,99,1,0), +(436,1445,4,98861,99,1,0), +(437,1445,5,98862,99,1,0); + )", .content_schema_update = true } diff --git a/common/database_schema.h b/common/database_schema.h index e0b87b94d3..b86e16f448 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -51,6 +51,7 @@ namespace DatabaseSchema { {"character_enabledtasks", "charid"}, {"character_expedition_lockouts", "character_id"}, {"character_exp_modifiers", "character_id"}, + {"character_evolving_items", "character_id"}, {"character_inspect_messages", "id"}, {"character_instance_safereturns", "character_id"}, {"character_item_recast", "id"}, @@ -124,6 +125,7 @@ namespace DatabaseSchema { "character_enabledtasks", "character_expedition_lockouts", "character_exp_modifiers", + "character_evolving_items", "character_inspect_messages", "character_instance_safereturns", "character_item_recast", @@ -212,6 +214,7 @@ namespace DatabaseSchema { "ground_spawns", "horses", "items", + "items_evolving_details", "ldon_trap_entries", "ldon_trap_templates", "lootdrop", diff --git a/common/emu_oplist.h b/common/emu_oplist.h index e41e4994b0..fcfd63c633 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -162,6 +162,7 @@ N(OP_EnduranceUpdate), N(OP_EnterChat), N(OP_EnterWorld), N(OP_EnvDamage), +N(OP_EvolveItem), N(OP_ExpansionInfo), N(OP_ExpUpdate), N(OP_FaceChange), diff --git a/common/eq_constants.h b/common/eq_constants.h index fd9cd346a6..e048a9321e 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -1114,4 +1114,5 @@ enum ExpSource namespace DoorType { constexpr uint32 BuyerStall = 155; } + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e4e3c28f81..bfe5abf442 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -6460,6 +6460,70 @@ struct PickZone_Struct { int32 selection_id; }; +struct EvolveItemToggle { + uint32 action; + uint32 unknown_004; + uint64 unique_id; + uint32 percentage; + uint32 activated; +}; + +struct EvolveXPWindowReceive { + uint32 action; + uint32 unknown_004; + uint64 item1_unique_id; + uint64 item2_unique_id; +}; + +struct EvolveItemMessaging { + uint32 action; + char serialized_data[]; +}; + +struct EvolveXPWindowSend { + /*000*/ uint32 action; + /*004*/ uint64 item1_unique_id; + /*012*/ uint64 item2_unique_id; + /*020*/ uint32 compatibility; + /*024*/ uint32 max_transfer_level; + /*028*/ uint8 item1_present; + /*029*/ uint8 item2_present; + /*030*/ std::string serialize_item_1; + /*034*/ std::string serialize_item_2; + + template + void serialize(Archive &archive) + { + archive( + CEREAL_NVP(action), + CEREAL_NVP(item1_unique_id), + CEREAL_NVP(item2_unique_id), + CEREAL_NVP(compatibility), + CEREAL_NVP(max_transfer_level), + CEREAL_NVP(item1_present), + CEREAL_NVP(item2_present), + CEREAL_NVP(serialize_item_1), + CEREAL_NVP(serialize_item_2) + ); + } +}; + +struct EvolveTransfer { + uint32 item_from_id; + uint32 item_from_current_amount; + uint32 item_to_id; + uint32 item_to_current_amount; + uint32 compatibility; + uint32 max_transfer_level; +}; + +struct EvolveGetNextItem { + uint32 new_item_id; + uint64 new_current_amount; + uint64 from_current_amount; + uint32 max_transfer_level; +}; + // Restore structure packing to default #pragma pack() diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 8bf474f349..4d41885542 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -142,6 +142,7 @@ namespace Logs { EqTime, Corpses, XTargets, + EvolveItem, MaxCategoryID /* Don't Remove this */ }; @@ -242,7 +243,8 @@ namespace Logs { "Zoning", "EqTime", "Corpses", - "XTargets" + "XTargets", + "EvolveItem" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 10c9cd98a2..638657f64b 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -131,6 +131,16 @@ OutF(LogSys, Logs::Detail, Logs::Error, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogEvolveItem(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::EvolveItem))\ + OutF(LogSys, Logs::General, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogEvolveItemDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::EvolveItem))\ + OutF(LogSys, Logs::Detail, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define LogGuilds(message, ...) do {\ if (LogSys.IsLogEnabled(Logs::General, Logs::Guilds))\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/events/player_event_logs.cpp b/common/events/player_event_logs.cpp index 5baa3e5485..5603fb7b80 100644 --- a/common/events/player_event_logs.cpp +++ b/common/events/player_event_logs.cpp @@ -707,6 +707,7 @@ void PlayerEventLogs::SetSettingsDefaults() m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1; m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1; m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1; + m_settings[PlayerEvent::EVOLVE_ITEM].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 6384f8e0e4..6a01a5cf6b 100644 --- a/common/events/player_events.h +++ b/common/events/player_events.h @@ -62,6 +62,7 @@ namespace PlayerEvent { PARCEL_RETRIEVE, PARCEL_DELETE, BARTER_TRANSACTION, + EVOLVE_ITEM, MAX // dont remove }; @@ -124,7 +125,8 @@ namespace PlayerEvent { "Parcel Item Sent", "Parcel Item Retrieved", "Parcel Prune Routine", - "Barter Transaction" + "Barter Transaction", + "Evolve Item Update" }; // Generic struct used by all events @@ -1115,6 +1117,29 @@ namespace PlayerEvent { ); } }; + + struct EvolveItem { + std::string status; + uint32 item_id; + uint64 unique_id; + std::string item_name; + uint32 level; + double progression; + + // cereal + template + void serialize(Archive &ar) + { + ar( + CEREAL_NVP(status), + CEREAL_NVP(item_id), + CEREAL_NVP(unique_id), + CEREAL_NVP(item_name), + CEREAL_NVP(level), + CEREAL_NVP(progression) + ); + } + }; } #endif //EQEMU_PLAYER_EVENTS_H diff --git a/common/evolving_items.cpp b/common/evolving_items.cpp new file mode 100644 index 0000000000..2b7eb38c6f --- /dev/null +++ b/common/evolving_items.cpp @@ -0,0 +1,303 @@ +#include "evolving_items.h" +#include "item_instance.h" +#include "events/player_event_logs.h" +#include "repositories/character_evolving_items_repository.h" + +EvolvingItemsManager::EvolvingItemsManager() +{ + m_db = nullptr; + m_content_db = nullptr; +} + +void EvolvingItemsManager::LoadEvolvingItems() const +{ + auto const &results = ItemsEvolvingDetailsRepository::All(*m_content_db); + + if (results.empty()) { + return; + } + + std::ranges::transform( + results.begin(), + results.end(), + std::inserter( + evolving_items_manager.GetEvolvingItemsCache(), + evolving_items_manager.GetEvolvingItemsCache().end() + ), + [](const ItemsEvolvingDetailsRepository::ItemsEvolvingDetails &x) { + return std::make_pair(x.item_id, x); + } + ); +} + +void EvolvingItemsManager::SetDatabase(Database *db) +{ + m_db = db; +} + +void EvolvingItemsManager::SetContentDatabase(Database *db) +{ + m_content_db = db; +} + +double EvolvingItemsManager::CalculateProgression(const uint64 current_amount, const uint32 item_id) +{ + if (!evolving_items_manager.GetEvolvingItemsCache().contains(item_id)) { + return 0; + } + + return evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount > 0 + ? static_cast(current_amount) + / static_cast(evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount) * 100 + : 0; +} + +void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_id, const EQ::ItemInstance &inst) const +{ + inst.SetEvolveEquipped(false); + if (inst.IsEvolving() && slot_id <= EQ::invslot::EQUIPMENT_END && slot_id >= EQ::invslot::EQUIPMENT_BEGIN) { + inst.SetEvolveEquipped(true); + } + + if (!inst.IsEvolving()) { + return; + } + + if (!inst.GetEvolveUniqueID()) { + auto e = CharacterEvolvingItemsRepository::NewEntity(); + + e.character_id = char_id; + e.item_id = inst.GetID(); + e.equipped = inst.GetEvolveEquipped(); + e.final_item_id = evolving_items_manager.GetFinalItemID(inst); + + auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e); + e.id = r.id; + + inst.SetEvolveUniqueID(e.id); + inst.SetEvolveCharID(e.character_id); + inst.SetEvolveItemID(e.item_id); + inst.SetEvolveFinalItemID(e.final_item_id); + + return; + } + + CharacterEvolvingItemsRepository::SetEquipped(*m_db, inst.GetEvolveUniqueID(), inst.GetEvolveEquipped()); +} + +uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const +{ + const auto start_iterator = std::ranges::find_if( + evolving_items_manager.GetEvolvingItemsCache().cbegin(), + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&](const std::pair &a) { + return a.second.item_evo_id == inst.GetEvolveLoreID(); + } + ); + + if (start_iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) { + return 0; + } + + const auto final_id = std::ranges::max_element( + start_iterator, + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&]( + const std::pair &a, + const std::pair &b + ) { + return a.second.item_evo_id == b.second.item_evo_id && + a.second.item_evolve_level < b.second.item_evolve_level; + } + ); + + return final_id->first; +} + +uint32 EvolvingItemsManager::GetNextEvolveItemID(const EQ::ItemInstance &inst) const +{ + int8 const current_level = inst.GetEvolveLvl(); + + const auto iterator = std::ranges::find_if( + evolving_items_manager.GetEvolvingItemsCache().cbegin(), + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&](const std::pair &a) { + return a.second.item_evo_id == inst.GetEvolveLoreID() && + a.second.item_evolve_level == current_level + 1; + } + ); + + if (iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) { + return 0; + } + + return iterator->first; +} + +ItemsEvolvingDetailsRepository::ItemsEvolvingDetails EvolvingItemsManager::GetEvolveItemDetails(const uint64 unique_id) +{ + if (GetEvolvingItemsCache().contains(unique_id)) { + return GetEvolvingItemsCache().at(unique_id); + } + + return ItemsEvolvingDetailsRepository::NewEntity(); +} + +std::vector EvolvingItemsManager::GetEvolveIDItems( + const uint32 evolve_id +) +{ + std::vector e{}; + + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_evo_id == evolve_id) { + e.push_back(value); + } + } + + std::ranges::sort( + e.begin(), + e.end(), + [&]( + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &a, + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &b + ) { + return a.item_evolve_level < b.item_evolve_level; + } + ); + + return e; +} + +uint64 EvolvingItemsManager::GetTotalEarnedXP(const EQ::ItemInstance &inst) +{ + if (!inst) { + return 0; + } + + uint64 xp = inst.GetEvolveCurrentAmount(); + auto evolve_id_item_cache = GetEvolveIDItems(inst.GetEvolveLoreID()); + auto current_level = inst.GetEvolveLvl(); + + for (auto const &i: evolve_id_item_cache) { + if (i.item_evolve_level < current_level) { + xp += i.required_amount; + } + } + + return xp; +} + +EvolveGetNextItem EvolvingItemsManager::GetNextItemByXP(const EQ::ItemInstance &inst_in, const int64 in_xp) +{ + EvolveGetNextItem ets{}; + const auto evolve_items = GetEvolveIDItems(inst_in.GetEvolveLoreID()); + uint32 max_transfer_level = 0; + int64 xp = in_xp; + + for (auto const &e: evolve_items) { + if (e.item_evolve_level < inst_in.GetEvolveLvl()) { + continue; + } + + int64 have = 0; + if (e.item_evolve_level == inst_in.GetEvolveLvl()) { + have = inst_in.GetEvolveCurrentAmount(); + } + + const auto required = e.required_amount; + const int64 need = required - have; + const int64 balance = xp - need; + + if (balance <= 0) { + ets.new_current_amount = have + xp; + ets.new_item_id = e.item_id; + ets.from_current_amount = 0; + ets.max_transfer_level = max_transfer_level; + return ets; + } + + xp = balance; + max_transfer_level += 1; + + ets.new_current_amount = required; + ets.new_item_id = e.item_id; + ets.from_current_amount = balance - required; + ets.max_transfer_level = max_transfer_level; + } + + return ets; +} + +EvolveTransfer EvolvingItemsManager::DetermineTransferResults( + const EQ::ItemInstance &inst_from, + const EQ::ItemInstance &inst_to +) +{ + EvolveTransfer ets{}; + + auto evolving_details_inst_from = evolving_items_manager.GetEvolveItemDetails(inst_from.GetID()); + auto evolving_details_inst_to = evolving_items_manager.GetEvolveItemDetails(inst_to.GetID()); + + if (!evolving_details_inst_from.id || !evolving_details_inst_to.id) { + return ets; + } + + if (evolving_details_inst_from.type == evolving_details_inst_to.type) { + uint32 compatibility = 0; + uint64 xp = 0; + if (evolving_details_inst_from.sub_type == evolving_details_inst_to.sub_type) { + compatibility = 100; + } + else { + compatibility = 30; + } + + xp = evolving_items_manager.GetTotalEarnedXP(inst_from) * compatibility / 100; + auto results = evolving_items_manager.GetNextItemByXP(inst_to, xp); + + ets.item_from_id = evolving_items_manager.GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID()); + ets.item_from_current_amount = results.from_current_amount; + ets.item_to_id = results.new_item_id; + ets.item_to_current_amount = results.new_current_amount; + ets.compatibility = compatibility; + ets.max_transfer_level = results.max_transfer_level; + } + + return ets; +} + +uint32 EvolvingItemsManager::GetFirstItemInLoreGroup(const uint32 lore_id) +{ + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_evo_id == lore_id && value.item_evolve_level == 1) { + return key; + } + } + + return 0; +} + +uint32 EvolvingItemsManager::GetFirstItemInLoreGroupByItemID(const uint32 item_id) +{ + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_id == item_id) { + for (auto const &[key2, value2]: GetEvolvingItemsCache()) { + if (value2.item_evo_id == value.item_evo_id && value2.item_evolve_level == 1) { + return key; + } + } + } + } + + return 0; +} + +void EvolvingItemsManager::LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e) +{ + e.item_id = inst.GetID(); + e.item_name = inst.GetItem() ? inst.GetItem()->Name : std::string(); + e.level = inst.GetEvolveLvl(); + e.progression = inst.GetEvolveProgression(); + e.unique_id = inst.GetEvolveUniqueID(); +} diff --git a/common/evolving_items.h b/common/evolving_items.h new file mode 100644 index 0000000000..463b584937 --- /dev/null +++ b/common/evolving_items.h @@ -0,0 +1,67 @@ +#ifndef EVOLVING_H +#define EVOLVING_H + +#include "shareddb.h" +#include "events/player_events.h" +#include "repositories/items_evolving_details_repository.h" + +namespace EQ { + class ItemInstance; +} + +namespace EvolvingItems { + namespace Actions { + constexpr int8 UPDATE_ITEMS = 0; + constexpr int8 TRANSFER_WINDOW_OPEN = 1; + constexpr int8 TRANSFER_WINDOW_DETAILS = 2; + constexpr int8 TRANSFER_XP = 3; + constexpr int8 FINAL_RESULT = 4; + } + + namespace Types { + constexpr int8 AMOUNT_OF_EXP = 1; + constexpr int8 NUMBER_OF_KILLS = 2; + constexpr int8 SPECIFIC_MOB_RACE = 3; + constexpr int8 SPECIFIC_ZONE_ID = 4; + } + + namespace SubTypes { + constexpr int8 ALL_EXP = 0; + constexpr int8 SOLO_EXP = 1; + constexpr int8 GROUP_EXP = 2; + constexpr int8 RAID_EXP = 3; + } +} + +class EvolvingItemsManager +{ +public: + EvolvingItemsManager(); + void SetDatabase(Database *db); + void SetContentDatabase(Database *db); + + void LoadEvolvingItems() const; + void DoLootChecks(uint32 char_id, uint16 slot_id, const EQ::ItemInstance &inst) const; + uint32 GetFinalItemID(const EQ::ItemInstance &inst) const; + uint32 GetNextEvolveItemID(const EQ::ItemInstance &inst) const; + uint32 GetFirstItemInLoreGroup(uint32 lore_id); + uint32 GetFirstItemInLoreGroupByItemID(uint32 item_id); + uint64 GetTotalEarnedXP(const EQ::ItemInstance &inst); + static double CalculateProgression(uint64 current_amount, uint32 item_id); + static void LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e); + + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails GetEvolveItemDetails(uint64 id); + EvolveTransfer DetermineTransferResults(const EQ::ItemInstance& inst_from, const EQ::ItemInstance& inst_to); + EvolveGetNextItem GetNextItemByXP(const EQ::ItemInstance &inst_in, int64 in_xp); + std::map& GetEvolvingItemsCache() { return evolving_items_cache; } + std::vector GetEvolveIDItems(uint32 evolve_id); + +private: + std::map evolving_items_cache; + Database * m_db; + Database * m_content_db; +}; + +extern EvolvingItemsManager evolving_items_manager; + +#endif //EVOLVING_H diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 595b9f0258..d83c2ab167 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -344,6 +344,8 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } + + source_item_instance->SetEvolveEquipped(false); if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { auto source_item = source_item_instance->GetItem(); if (!source_item) { @@ -362,6 +364,9 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapLevel; return false; } + if (source_item_instance->IsEvolving() > 0) { + source_item_instance->SetEvolveEquipped(true); + } } } @@ -370,6 +375,8 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } + + destination_item_instance->SetEvolveEquipped(false); if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { auto destination_item = destination_item_instance->GetItem(); if (!destination_item) { @@ -388,6 +395,9 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapLevel; return false; } + if (destination_item_instance->IsEvolving()) { + destination_item_instance->SetEvolveEquipped(true); + } } } @@ -1402,6 +1412,8 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) int16 result = INVALID_INDEX; int16 parentSlot = INVALID_INDEX; + inst->SetEvolveEquipped(false); + if (slot_id == invslot::slotCursor) { // Replace current item on cursor, if exists m_cursor.pop(); // no memory delete, clients of this function know what they are doing @@ -1410,6 +1422,9 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) } else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + if (inst->IsEvolving()) { + inst->SetEvolveEquipped(true); + } m_worn[slot_id] = inst; result = slot_id; } @@ -1808,3 +1823,182 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it } return 0; } + +//This function has the same flaw as noted above +// Helper functions for evolving items +int16 EQ::InventoryProfile::HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where) +{ + int16 slot_id = INVALID_INDEX; + + // Altered by Father Nitwit to support a specification of + // where to search, with a default value to maintain compatibility + + // Check each inventory bucket + if (where & invWhereWorn) { + slot_id = _HasEvolvingItem(m_worn, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWherePersonal) { + slot_id = _HasEvolvingItem(m_inv, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereBank) { + slot_id = _HasEvolvingItem(m_bank, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereSharedBank) { + slot_id = _HasEvolvingItem(m_shbank, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereTrading) { + slot_id = _HasEvolvingItem(m_trade, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + // Behavioral change - Limbo is no longer checked due to improper handling of return value + if (where & invWhereCursor) { + // Check cursor queue + slot_id = _HasEvolvingItem(m_cursor, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + return slot_id; +} + +// Internal Method: Checks an inventory bucket for a particular evolving item unique id +int16 EQ::InventoryProfile::_HasEvolvingItem( + std::map &bucket, uint64 evolve_unique_id, uint8 quantity) +{ + uint32 quantity_found = 0; + + for (auto const &[key, value]: bucket) { + if (!value) { + continue; + } + + if (key <= EQ::invslot::POSSESSIONS_END && key >= EQ::invslot::POSSESSIONS_BEGIN) { + if (((uint64) 1 << key & m_lookup->PossessionsBitmask) == 0) { + continue; + } + } + else if (key <= EQ::invslot::BANK_END && key >= EQ::invslot::BANK_BEGIN) { + if (key - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { + continue; + } + } + + + if (value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += value->GetCharges() <= 0 ? 1 : value->GetCharges(); + if (quantity_found >= quantity) { + return key; + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + + if (!value->IsClassBag()) { + continue; + } + + for (auto const &[bag_key, bag_value]: *value->GetContents()) { + if (!bag_value) { + continue; + } + + if (bag_value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges(); + if (quantity_found >= quantity) { + return CalcSlotId(key, bag_key); + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + } + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 EQ::InventoryProfile::_HasEvolvingItem(ItemInstQueue &iqueue, uint64 evolve_unique_id, uint8 quantity) +{ + // The downfall of this (these) queue procedure is that callers presume that when an item is + // found, it is presented as being available on the cursor. In cases of a parity check, this + // is sufficient. However, in cases where referential criteria is considered, this can lead + // to unintended results. Funtionality should be observed when referencing the return value + // of this query + + uint32 quantity_found = 0; + + for (auto const &inst: iqueue) { + if (!inst) { + continue; + } + + if (inst->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += inst->GetCharges() <= 0 ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) { + return invslot::slotCursor; + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (inst->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + + if (!inst->IsClassBag()) { + continue; + } + + for (auto const &[bag_key, bag_value]: *inst->GetContents()) { + if (!bag_value) { + continue; + } + + if (bag_value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges(); + if (quantity_found >= quantity) { + return CalcSlotId(invslot::slotCursor, bag_key); + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; + } + + return INVALID_INDEX; +} diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 4761e1b978..b1d1e4850e 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -57,6 +57,8 @@ class ItemInstQueue inline std::list::const_iterator cbegin() { return m_list.cbegin(); } inline std::list::const_iterator cend() { return m_list.cend(); } + inline std::list::iterator begin() { return m_list.begin(); } + inline std::list::iterator end() { return m_list.end(); } inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t inline bool empty() { return m_list.empty(); } @@ -211,6 +213,11 @@ namespace EQ void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, bool value); std::string GetCustomItemData(int16 slot_id, const std::string& identifier); static const int GetItemStatValue(uint32 item_id, const std::string& identifier); + + std::map& GetWorn() { return m_worn; } + std::map& GetPersonal() { return m_inv; } + int16 HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where); + protected: /////////////////////////////// // Protected Methods @@ -233,6 +240,8 @@ namespace EQ int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity); int16 _HasItemByLoreGroup(std::map& bucket, uint32 loregroup); int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup); + int16 _HasEvolvingItem(std::map& bucket, uint64 evolve_unique_id, uint8 quantity); + int16 _HasEvolvingItem(ItemInstQueue& iqueue, uint64 evolve_unique_id, uint8 quantity); // Player inventory diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 6aeb32223b..1e1e550b50 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -25,6 +25,7 @@ #include "rulesys.h" #include "shareddb.h" #include "strings.h" +#include "evolving_items.h" //#include "../common/light_source.h" @@ -76,6 +77,10 @@ EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) { m_color = m_item->Color; } + if (IsEvolving()) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + } + m_SerialNumber = GetNextItemInstSerialNumber(); } @@ -95,6 +100,10 @@ EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges m_color = 0; } + if (IsEvolving()) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + } + m_SerialNumber = GetNextItemInstSerialNumber(); } @@ -146,7 +155,6 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_exp = copy.m_exp; m_evolveLvl = copy.m_evolveLvl; - m_activated = copy.m_activated; if (copy.m_scaledItem) { m_scaledItem = new ItemData(*copy.m_scaledItem); @@ -154,12 +162,7 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_scaledItem = nullptr; } - if (copy.m_evolveInfo) { - m_evolveInfo = new EvolveInfo(*copy.m_evolveInfo); - } else { - m_evolveInfo = nullptr; - } - + m_evolving_details = copy.m_evolving_details; m_scaling = copy.m_scaling; m_ornamenticon = copy.m_ornamenticon; m_ornamentidfile = copy.m_ornamentidfile; @@ -174,7 +177,6 @@ EQ::ItemInstance::~ItemInstance() Clear(); safe_delete(m_item); safe_delete(m_scaledItem); - safe_delete(m_evolveInfo); } // Query item type @@ -1030,29 +1032,6 @@ void EQ::ItemInstance::ScaleItem() { m_scaledItem->CharmFileID = 0; // this stops the client from trying to scale the item itself. } -bool EQ::ItemInstance::EvolveOnAllKills() const { - return (m_evolveInfo && m_evolveInfo->AllKills); -} - -int8 EQ::ItemInstance::GetMaxEvolveLvl() const { - if (m_evolveInfo) - return m_evolveInfo->MaxLvl; - else - return 0; -} - -uint32 EQ::ItemInstance::GetKillsNeeded(uint8 currentlevel) { - uint32 kills = -1; // default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero. - if (m_evolveInfo) - if (currentlevel != m_evolveInfo->MaxLvl) - kills = m_evolveInfo->LvlKills[currentlevel - 1]; - - if (kills == 0) - kills = -1; - - return kills; -} - void EQ::ItemInstance::SetTimer(std::string name, uint32 time) { Timer t(time); t.Start(time, false); @@ -1951,28 +1930,54 @@ void EQ::ItemInstance::ClearGUIDMap() { guids.clear(); } -// -// class EvolveInfo -// -EvolveInfo::EvolveInfo() { - // nothing here yet + +bool EQ::ItemInstance::TransferOwnership(Database &db, const uint32 to_char_id) const +{ + if (!to_char_id || !IsEvolving()) { + return false; + } + + SetEvolveCharID(to_char_id); + CharacterEvolvingItemsRepository::UpdateCharID(db, GetEvolveUniqueID(), to_char_id); + return true; +} + +uint32 EQ::ItemInstance::GetAugmentEvolveUniqueID(uint8 augment_index) const +{ + if (!m_item || !m_item->IsClassCommon()) { + return 0; + } + + const auto item = GetItem(augment_index); + if (item) { + return item->GetEvolveUniqueID(); + } + + return 0; } -EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) { - FirstItem = first; - MaxLvl = max; - AllKills = allkills; - LvlKills[0] = L2; - LvlKills[1] = L3; - LvlKills[2] = L4; - LvlKills[3] = L5; - LvlKills[4] = L6; - LvlKills[5] = L7; - LvlKills[6] = L8; - LvlKills[7] = L9; - LvlKills[8] = L10; +void EQ::ItemInstance::SetTimer(std::string name, uint32 time) const{ + Timer t(time); + t.Start(time, false); + m_timers[name] = t; } -EvolveInfo::~EvolveInfo() { +void EQ::ItemInstance::SetEvolveEquipped(const bool in) const +{ + if (!IsEvolving()) { + return; + } + + m_evolving_details.equipped = in; + if (in && !GetTimers().contains("evolve")) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + return; + } + + if (in) { + GetTimers().at("evolve").SetTimer(RuleI(EvolvingItems, DelayUponEquipping)); + return; + } + GetTimers().at("evolve").Disable(); } diff --git a/common/item_instance.h b/common/item_instance.h index 928a6aabc3..4005a70c2c 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -23,6 +23,7 @@ #ifndef COMMON_ITEM_INSTANCE_H #define COMMON_ITEM_INSTANCE_H +#include "evolving_items.h" class ItemParse; // Parses item packets @@ -34,6 +35,7 @@ class EvolveInfo; // Stores information about an evolving item family #include "../common/bodytypes.h" #include "../common/deity.h" #include "../common/memory_buffer.h" +#include "../common/repositories/character_evolving_items_repository.h" #include @@ -205,13 +207,9 @@ namespace EQ bool IsDroppable(bool recurse = true) const; bool IsScaling() const { return m_scaling; } - bool IsEvolving() const { return (m_evolveLvl >= 1); } uint32 GetExp() const { return m_exp; } void SetExp(uint32 exp) { m_exp = exp; } void AddExp(uint32 exp) { m_exp += exp; } - bool IsActivated() { return m_activated; } - void SetActivated(bool activated) { m_activated = activated; } - int8 GetEvolveLvl() const { return m_evolveLvl; } void SetScaling(bool v) { m_scaling = v; } uint32 GetOrnamentationIcon() const { return m_ornamenticon; } void SetOrnamentIcon(uint32 ornament_icon) { m_ornamenticon = ornament_icon; } @@ -226,9 +224,6 @@ namespace EQ void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); - bool EvolveOnAllKills() const; - int8 GetMaxEvolveLvl() const; - uint32 GetKillsNeeded(uint8 currentlevel); std::string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id = slot_id; s.inst = (const void*)this; std::string ser; ser.assign((char*)&s, sizeof(InternalSerializedItem_Struct)); return ser; } void Serialize(OutBuffer& ob, int16 slot_id) const { InternalSerializedItem_Struct isi; isi.slot_id = slot_id; isi.inst = (const void*)this; ob.write((const char*)&isi, sizeof(isi)); } @@ -236,8 +231,9 @@ namespace EQ inline int32 GetSerialNumber() const { return m_SerialNumber; } inline void SetSerialNumber(int32 id) { m_SerialNumber = id; } - std::map& GetTimers() { return m_timers; } + std::map& GetTimers() const { return m_timers; } void SetTimer(std::string name, uint32 time); + void SetTimer(std::string name, uint32 time) const; void StopTimer(std::string name); void ClearTimers(); @@ -312,6 +308,34 @@ namespace EQ static void AddGUIDToMap(uint64 existing_serial_number); static void ClearGUIDMap(); + // evolving items stuff + CharacterEvolvingItemsRepository::CharacterEvolvingItems &GetEvolvingDetails() const { return m_evolving_details; } + + int8 GetEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingLevel; } return false; } + bool IsEvolving() const { if (GetItem()) { return GetItem()->EvolvingItem; } return false; } + int8 GetMaxEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingMax; } return false; } + bool GetEvolveActivated() const { return m_evolving_details.activated ? true : false; } + bool GetEvolveEquipped() const { return m_evolving_details.equipped ? true : false; } + double GetEvolveProgression() const { return m_evolving_details.progression; } + uint64 GetEvolveUniqueID() const { return m_evolving_details.id; } + uint32 GetEvolveCharID() const { return m_evolving_details.character_id; } + uint32 GetEvolveItemID() const { return m_evolving_details.item_id; } + uint32 GetEvolveLoreID() const { if (GetItem()) { return GetItem()->EvolvingID; } return false; } + uint64 GetEvolveCurrentAmount() const { return m_evolving_details.current_amount; } + uint32 GetEvolveFinalItemID() const { return m_evolving_details.final_item_id; } + uint32 GetAugmentEvolveUniqueID(uint8 augment_index) const; + void SetEvolveEquipped(const bool in) const; + void SetEvolveActivated(const bool in) const { m_evolving_details.activated = in; } + void SetEvolveProgression(const double in) const { m_evolving_details.progression = in; } + void SetEvolveUniqueID(const uint64 in) const { m_evolving_details.id = in; } + void SetEvolveCharID(const uint32 in) const { m_evolving_details.character_id = in; } + void SetEvolveItemID(const uint32 in) const { m_evolving_details.item_id = in; } + void SetEvolveCurrentAmount(const uint64 in) const { m_evolving_details.current_amount = in; } + void SetEvolveAddToCurrentAmount(const uint64 in) const { m_evolving_details.current_amount += in; } + void SetEvolveFinalItemID(const uint32 in) const { m_evolving_details.final_item_id = in; } + bool TransferOwnership(Database& db, const uint32 to_char_id) const; + void CalculateEvolveProgression() const { m_evolving_details.progression = evolving_items_manager.CalculateProgression(GetEvolveCurrentAmount(), GetID()); } + protected: ////////////////////////// // Protected Members @@ -323,48 +347,32 @@ namespace EQ void _PutItem(uint8 index, ItemInstance* inst) { m_contents[index] = inst; } - ItemInstTypes m_use_type {ItemInstNormal}; // Usage type for item - const ItemData* m_item {nullptr}; // Ptr to item data - int16 m_charges {0}; // # of charges for chargeable items - uint32 m_price {0}; // Bazaar /trader price - uint32 m_color {0}; - uint32 m_merchantslot {0}; - int16 m_currentslot {0}; - bool m_attuned {false}; - int32 m_merchantcount {1}; //number avaliable on the merchant, -1=unlimited - int32 m_SerialNumber {0}; // Unique identifier for this instance of an item. Needed for Bazaar. - uint32 m_exp {0}; - int8 m_evolveLvl {0}; - bool m_activated {false}; - ItemData* m_scaledItem {nullptr}; - ::EvolveInfo* m_evolveInfo {nullptr}; - bool m_scaling {false}; - uint32 m_ornamenticon {0}; - uint32 m_ornamentidfile {0}; - uint32 m_new_id_file {0}; - uint32 m_ornament_hero_model {0}; - uint32 m_recast_timestamp {0}; - int m_task_delivered_count {0}; + ItemInstTypes m_use_type{ItemInstNormal};// Usage type for item + const ItemData * m_item{nullptr}; // Ptr to item data + int16 m_charges{0}; // # of charges for chargeable items + uint32 m_price{0}; // Bazaar /trader price + uint32 m_color{0}; + uint32 m_merchantslot{0}; + int16 m_currentslot{0}; + bool m_attuned{false}; + int32 m_merchantcount{1};//number avaliable on the merchant, -1=unlimited + int32 m_SerialNumber{0}; // Unique identifier for this instance of an item. Needed for Bazaar. + uint32 m_exp{0}; + int8 m_evolveLvl{0}; + ItemData * m_scaledItem{nullptr}; + bool m_scaling{false}; + uint32 m_ornamenticon{0}; + uint32 m_ornamentidfile{0}; + uint32 m_new_id_file{0}; + uint32 m_ornament_hero_model{0}; + uint32 m_recast_timestamp{0}; + int m_task_delivered_count{0}; + mutable CharacterEvolvingItemsRepository::CharacterEvolvingItems m_evolving_details{}; // Items inside of this item (augs or contents) {}; - std::map m_contents {}; // Zero-based index: min=0, max=9 - std::map m_custom_data {}; - std::map m_timers {}; + std::map m_contents {}; // Zero-based index: min=0, max=9 + std::map m_custom_data {}; + mutable std::map m_timers {}; }; } - -class EvolveInfo { -public: - friend class EQ::ItemInstance; - //temporary - uint16 LvlKills[9]; - uint32 FirstItem; - uint8 MaxLvl; - bool AllKills; - - EvolveInfo(); - EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10); - ~EvolveInfo(); -}; - #endif /*COMMON_ITEM_INSTANCE_H*/ diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 783a9d77fa..15f3be60eb 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1350,6 +1350,58 @@ namespace RoF2 dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_EvolveItem) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + auto action = *reinterpret_cast(in->pBuffer); + + switch (action) { + case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: { + auto emu = reinterpret_cast(in->pBuffer); + + EvolveXPWindowSend e{}; + EQ::Util::MemoryStreamReader ss(emu->serialized_data, in->size - sizeof(emu->action)); + cereal::BinaryInputArchive ar(ss); + ar(e); + + auto item_1 = static_cast(reinterpret_cast(e.serialize_item_1.data())->inst); + auto item_2 = static_cast(reinterpret_cast(e.serialize_item_2.data())->inst); + + EQ::OutBuffer ob; + + SerializeItem(ob, item_1, 0, 0, ItemPacketMerchant); + SerializeItem(ob, item_2, 0, 0, ItemPacketMerchant); + + auto out = std::make_unique( + OP_EvolveItem, + sizeof(EvolveXPWindowSendDetails_Struct) + ob.size() + ); + auto data = reinterpret_cast(out->pBuffer); + + data->action = e.action; + data->compatibility = e.compatibility; + data->max_transfer_level = e.max_transfer_level; + data->item1_unique_id = e.item1_unique_id; + data->item2_unique_id = e.item2_unique_id; + data->item1_present = e.item1_present; + data->item2_present = e.item2_present; + + memcpy(data->serialize_data, ob.str().data(), ob.size()); + dest->QueuePacket(out.get()); + safe_delete(in); + break; + } + default: { + dest->FastQueuePacket(&in); + break; + } + } + } + ENCODE(OP_ExpansionInfo) { ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); @@ -6405,6 +6457,11 @@ namespace RoF2 hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0); hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber()); hdr.parcel_item_id = packet_type == ItemPacketParcel ? inst->GetID() : 0; + if (item->EvolvingItem) { + hdr.instance_id = inst->GetEvolveUniqueID() & 0xFFFFFFFF; //lower dword + hdr.parcel_item_id = inst->GetEvolveUniqueID() >> 32; //upper dword + } + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (inst->IsStackable() ? (item->MaxCharges ? 1 : 0) : ((inst->GetCharges() > 254) ? 0xFFFFFFFF @@ -6418,18 +6475,15 @@ namespace RoF2 ob.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader)); if (item->EvolvingItem > 0) { - RoF2::structs::EvolvingItem evotop; - - evotop.unknown001 = 0; - evotop.unknown002 = 0; - evotop.unknown003 = 0; - evotop.unknown004 = 0; - evotop.evoLevel = item->EvolvingLevel; - evotop.progress = 0; - evotop.Activated = 1; - evotop.evomaxlevel = item->EvolvingMax; - - ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem)); + RoF2::structs::EvolvingItem_Struct evotop; + + evotop.final_item_id = inst->GetEvolveFinalItemID(); + evotop.evolve_level = item->EvolvingLevel; + evotop.progress = inst->GetEvolveProgression(); + evotop.activated = inst->GetEvolveActivated(); + evotop.evolve_max_level = item->EvolvingMax; + + ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem_Struct)); } /** diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 08d8b99857..0330287e41 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -70,6 +70,7 @@ E(OP_DzMemberListName) E(OP_DzMemberListStatus) E(OP_DzSetLeaderName) E(OP_Emote) +E(OP_EvolveItem) E(OP_ExpansionInfo) E(OP_FormattedMessage) E(OP_GMLastName) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 1bbf7c26c2..4a8d277ebe 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4746,16 +4746,13 @@ struct ItemSerializationHeader uint8 isEvolving; }; -struct EvolvingItem { - uint8 unknown001; - uint8 unknown002; - uint8 unknown003; - uint8 unknown004; - int32 evoLevel; +struct EvolvingItem_Struct { + uint32 final_item_id; + int32 evolve_level; double progress; - uint8 Activated; - int32 evomaxlevel; - uint8 unknown005[4]; + uint8 activated; + int32 evolve_max_level; + uint8 unknown005[4]; }; struct ItemSerializationHeaderFinish @@ -5428,6 +5425,32 @@ struct Parcel_Struct }; }; /*structs*/ +struct EvolveItemToggle_Struct { + uint32 action; + uint32 unknown_004; + uint64 unique_id; + uint32 percentage; + uint32 activated; +}; + +struct EvolveXPWindowReceive_Struct { + uint32 action; + uint32 unknown_004; + uint64 item1_unique_id; + uint64 item2_unique_id; +}; + +struct EvolveXPWindowSendDetails_Struct { + /*000*/ uint32 action; + /*004*/ uint64 item1_unique_id; + /*012*/ uint64 item2_unique_id; + /*020*/ uint32 compatibility; + /*024*/ uint32 max_transfer_level; + /*028*/ uint8 item1_present; + /*029*/ uint8 item2_present; + /*030*/ char serialize_data[]; +}; + }; /*RoF2*/ #endif /*COMMON_ROF2_STRUCTS_H*/ diff --git a/common/repositories/base/base_character_evolving_items_repository.h b/common/repositories/base/base_character_evolving_items_repository.h new file mode 100644 index 0000000000..9aa6c1773a --- /dev/null +++ b/common/repositories/base/base_character_evolving_items_repository.h @@ -0,0 +1,475 @@ +/** + * 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_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseCharacterEvolvingItemsRepository { +public: + struct CharacterEvolvingItems { + uint64_t id; + uint32_t character_id; + uint32_t item_id; + uint8_t activated; + uint8_t equipped; + int64_t current_amount; + double progression; + uint32_t final_item_id; + time_t deleted_at; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "character_id", + "item_id", + "activated", + "equipped", + "current_amount", + "progression", + "final_item_id", + "deleted_at", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "character_id", + "item_id", + "activated", + "equipped", + "current_amount", + "progression", + "final_item_id", + "UNIX_TIMESTAMP(deleted_at)", + }; + } + + 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("character_evolving_items"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterEvolvingItems NewEntity() + { + CharacterEvolvingItems e{}; + + e.id = 0; + e.character_id = 0; + e.item_id = 0; + e.activated = 0; + e.equipped = 0; + e.current_amount = 0; + e.progression = 0; + e.final_item_id = 0; + e.deleted_at = 0; + + return e; + } + + static CharacterEvolvingItems GetCharacterEvolvingItems( + const std::vector &character_evolving_itemss, + int character_evolving_items_id + ) + { + for (auto &character_evolving_items : character_evolving_itemss) { + if (character_evolving_items.id == character_evolving_items_id) { + return character_evolving_items; + } + } + + return NewEntity(); + } + + static CharacterEvolvingItems FindOne( + Database& db, + int character_evolving_items_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + character_evolving_items_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10); + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_evolving_items_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_evolving_items_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const CharacterEvolvingItems &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.character_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); + v.push_back(columns[3] + " = " + std::to_string(e.activated)); + v.push_back(columns[4] + " = " + std::to_string(e.equipped)); + v.push_back(columns[5] + " = " + std::to_string(e.current_amount)); + v.push_back(columns[6] + " = " + std::to_string(e.progression)); + v.push_back(columns[7] + " = " + std::to_string(e.final_item_id)); + v.push_back(columns[8] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterEvolvingItems InsertOne( + Database& db, + CharacterEvolvingItems e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + 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.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + 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) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", 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) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", 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 CharacterEvolvingItems &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + 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.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + 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_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H diff --git a/common/repositories/base/base_items_evolving_details_repository.h b/common/repositories/base/base_items_evolving_details_repository.h new file mode 100644 index 0000000000..0f932bfb8b --- /dev/null +++ b/common/repositories/base/base_items_evolving_details_repository.h @@ -0,0 +1,451 @@ +/** + * 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_ITEMS_EVOLVING_DETAILS_REPOSITORY_H +#define EQEMU_BASE_ITEMS_EVOLVING_DETAILS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseItemsEvolvingDetailsRepository { +public: + struct ItemsEvolvingDetails { + uint32_t id; + uint32_t item_evo_id; + uint32_t item_evolve_level; + uint32_t item_id; + uint32_t type; + uint32_t sub_type; + int64_t required_amount; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "item_evo_id", + "item_evolve_level", + "item_id", + "type", + "sub_type", + "required_amount", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "item_evo_id", + "item_evolve_level", + "item_id", + "type", + "sub_type", + "required_amount", + }; + } + + 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("items_evolving_details"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static ItemsEvolvingDetails NewEntity() + { + ItemsEvolvingDetails e{}; + + e.id = 0; + e.item_evo_id = 0; + e.item_evolve_level = 0; + e.item_id = 0; + e.type = 0; + e.sub_type = 0; + e.required_amount = 0; + + return e; + } + + static ItemsEvolvingDetails GetItemsEvolvingDetails( + const std::vector &items_evolving_detailss, + int items_evolving_details_id + ) + { + for (auto &items_evolving_details : items_evolving_detailss) { + if (items_evolving_details.id == items_evolving_details_id) { + return items_evolving_details; + } + } + + return NewEntity(); + } + + static ItemsEvolvingDetails FindOne( + Database& db, + int items_evolving_details_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + items_evolving_details_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int items_evolving_details_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + items_evolving_details_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const ItemsEvolvingDetails &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.item_evo_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level)); + v.push_back(columns[3] + " = " + std::to_string(e.item_id)); + v.push_back(columns[4] + " = " + std::to_string(e.type)); + v.push_back(columns[5] + " = " + std::to_string(e.sub_type)); + v.push_back(columns[6] + " = " + std::to_string(e.required_amount)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static ItemsEvolvingDetails InsertOne( + Database& db, + ItemsEvolvingDetails e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; + + 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) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; + + 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 ItemsEvolvingDetails &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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_ITEMS_EVOLVING_DETAILS_REPOSITORY_H diff --git a/common/repositories/character_evolving_items_repository.h b/common/repositories/character_evolving_items_repository.h new file mode 100644 index 0000000000..8bc379505d --- /dev/null +++ b/common/repositories/character_evolving_items_repository.h @@ -0,0 +1,66 @@ +#ifndef EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H +#define EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_character_evolving_items_repository.h" + +#include + +class CharacterEvolvingItemsRepository: public BaseCharacterEvolvingItemsRepository { +public: + // Custom extended repository methods here + + static CharacterEvolvingItems SetCurrentAmountAndProgression(Database& db, const uint64 id, const uint64 amount, const double progression) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.current_amount = amount; + e.progression = progression; + e.deleted_at = 0; + UpdateOne(db, e); + return e; + } + + static CharacterEvolvingItems SetEquipped(Database& db, const uint64 id, const bool equipped) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.equipped = equipped; + e.deleted_at = 0; + UpdateOne(db, e); + return e; + } + + static CharacterEvolvingItems SoftDelete(Database& db, const uint64 id) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.deleted_at = time(nullptr); + UpdateOne(db, e); + return e; + } + + static bool UpdateCharID(Database &db, const uint64 id, const uint32 to_char_id) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return false; + } + + e.character_id = to_char_id; + e.deleted_at = 0; + return UpdateOne(db, e); + } +}; + +#endif //EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H diff --git a/common/repositories/items_evolving_details_repository.h b/common/repositories/items_evolving_details_repository.h new file mode 100644 index 0000000000..906bb60951 --- /dev/null +++ b/common/repositories/items_evolving_details_repository.h @@ -0,0 +1,14 @@ +#ifndef EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H +#define EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_items_evolving_details_repository.h" + +class ItemsEvolvingDetailsRepository: public BaseItemsEvolvingDetailsRepository { +public: + // Custom extended repository methods here + +}; + +#endif //EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index c0682979b4..e27ed3f3cf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -1038,6 +1038,13 @@ RULE_INT(Parcel, ParcelMaxItems, 50, "The maximum number of parcels a player is RULE_INT(Parcel, ParcelPruneDelay, 30, "The number of days after which a parcel is deleted. Items are lost!") RULE_CATEGORY_END() +RULE_CATEGORY(EvolvingItems) +RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.") +RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.") +RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.") +RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/shareddb.cpp b/common/shareddb.cpp index ac7a3a173a..c51b79885e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -37,11 +37,13 @@ #include "strings.h" #include "eqemu_config.h" #include "data_verification.h" +#include "evolving_items.h" #include "repositories/criteria/content_filter_criteria.h" #include "repositories/account_repository.h" #include "repositories/faction_association_repository.h" #include "repositories/starting_items_repository.h" #include "path_manager.h" +#include "../zone/client.h" #include "repositories/loottable_repository.h" #include "repositories/character_item_recast_repository.h" #include "repositories/character_corpses_repository.h" @@ -645,15 +647,23 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is return true; } - // Overloaded: Retrieve character inventory based on character id (zone entry) -bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) +//bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) +bool SharedDatabase::GetInventory(Client *c) { - if (!char_id || !inv) + if (!c) { return false; + } + + uint32 char_id = c->CharacterID(); + EQ::InventoryProfile &inv = c->GetInv(); // Retrieve character inventory - auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id)); + auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`", char_id)); + auto e_results = CharacterEvolvingItemsRepository::GetWhere( + *this, fmt::format("`char_id` = '{}' AND `deleted_at` IS NULL", char_id) + ); + if (results.empty()) { LogError("Error loading inventory for char_id {} from the database.", char_id); return false; @@ -667,8 +677,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) const auto timestamps = GetItemRecastTimestamps(char_id); auto cv_conflict = false; - const auto pmask = inv->GetLookup()->PossessionsBitmask; - const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; + const auto pmask = inv.GetLookup()->PossessionsBitmask; + const auto bank_size = inv.GetLookup()->InventoryTypeSize.Bank; std::vector queue{}; for (auto &row: results) { @@ -785,9 +795,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) } } + if (item->EvolvingItem) { + if (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END) { + inst->SetEvolveEquipped(true); + } + + auto t = std::ranges::find_if( + e_results.cbegin(), + e_results.cend(), + [&](const CharacterEvolvingItemsRepository::CharacterEvolvingItems &x) { + return x.item_id == item_id; + } + ); + + if (t == std::end(e_results)) { + auto e = CharacterEvolvingItemsRepository::NewEntity(); + + e.character_id = char_id; + e.item_id = item_id; + e.equipped = inst->GetEvolveEquipped(); + e.final_item_id = evolving_items_manager.GetFinalItemID(*inst); + + auto r = CharacterEvolvingItemsRepository::InsertOne(*this, e); + e.id = r.id; + e_results.push_back(e); + + inst->SetEvolveUniqueID(e.id); + inst->SetEvolveCharID(e.character_id); + inst->SetEvolveItemID(e.item_id); + inst->SetEvolveActivated(e.activated); + inst->SetEvolveEquipped(e.equipped); + inst->SetEvolveCurrentAmount(e.current_amount); + inst->CalculateEvolveProgression(); + inst->SetEvolveFinalItemID(e.final_item_id); + } + else { + inst->SetEvolveUniqueID(t->id); + inst->SetEvolveCharID(t->character_id); + inst->SetEvolveItemID(t->item_id); + inst->SetEvolveActivated(t->activated); + inst->SetEvolveEquipped(t->equipped); + inst->SetEvolveCurrentAmount(t->current_amount); + inst->CalculateEvolveProgression(); + inst->SetEvolveFinalItemID(t->final_item_id); + } + } + int16 put_slot_id; if (slot_id >= 8000 && slot_id <= 8999) { - put_slot_id = inv->PushCursor(*inst); + put_slot_id = inv.PushCursor(*inst); } else if (slot_id >= 3111 && slot_id <= 3179) { // Admins: please report any occurrences of this error @@ -797,10 +853,10 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) item_id, slot_id ); - put_slot_id = inv->PushCursor(*inst); + put_slot_id = inv.PushCursor(*inst); } else { - put_slot_id = inv->PutItem(slot_id, *inst); + put_slot_id = inv.PutItem(slot_id, *inst); } row.guid = inst->GetSerialNumber(); @@ -825,8 +881,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) "ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", char_name, char_id, - EQ::versions::MobVersionName(inv->InventoryVersion()), - (inv->GMInventory() ? "true" : "false") + EQ::versions::MobVersionName(inv.InventoryVersion()), + (inv.GMInventory() ? "true" : "false") ); } @@ -837,7 +893,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) EQ::ItemInstance::ClearGUIDMap(); // Retrieve shared inventory - return GetSharedBank(char_id, inv, true); + return GetSharedBank(char_id, &inv, true); } // Overloaded: Retrieve character inventory based on account_id and character name (char select) diff --git a/common/shareddb.h b/common/shareddb.h index 67c684206b..d36132fd31 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -28,6 +28,8 @@ #include "fixed_memory_variable_hash_set.h" #include "say_link.h" #include "repositories/command_subsettings_repository.h" +#include "repositories/items_evolving_details_repository.h" +#include "../common/repositories/character_evolving_items_repository.h" #include #include @@ -103,7 +105,7 @@ class SharedDatabase : public Database { bool GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid); int32 GetSharedPlatinum(uint32 account_id); bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); - bool GetInventory(uint32 char_id, EQ::InventoryProfile *inv); + bool GetInventory(Client* c); bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated std::map GetItemRecastTimestamps(uint32 char_id); uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type); diff --git a/common/version.h b/common/version.h index 9ca78821e1..ba2bb092ba 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9288 +#define CURRENT_BINARY_DATABASE_VERSION 9290 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index c598b39f5c..f395867ce8 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -33,12 +33,14 @@ #include "../common/zone_store.h" #include "../common/path_manager.h" #include "../common/events/player_event_logs.h" - -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +#include "../common/evolving_items.h" + +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; #ifdef _WINDOWS #include diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index a63916911d..3866cfe899 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -731,3 +731,6 @@ OP_RemoveTrap=0x71da OP_PickZoneWindow=0x72d8 OP_PickZone=0xaaba + +#evolve item related +OP_EvolveItem=0x7cfb diff --git a/world/main.cpp b/world/main.cpp index aff459fb9a..71109bb959 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -46,6 +46,7 @@ #include "client.h" #include "worlddb.h" #include "wguild_mgr.h" +#include "../common/evolving_items.h" #ifdef _WINDOWS #include @@ -111,6 +112,7 @@ WorldContentService content_service; WebInterfaceList web_interface; PathManager path; PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void CatchSignal(int sig_num); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 1ead62e1b0..798114399c 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -17,6 +17,7 @@ SET(zone_sources botspellsai.cpp cheat_manager.cpp client.cpp + client_evolving_items.cpp client_bot.cpp client_mods.cpp client_packet.cpp diff --git a/zone/attack.cpp b/zone/attack.cpp index e2d7cdf176..3de17a13ce 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2815,7 +2815,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy if (con_level != ConsiderColor::Gray) { if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) { - give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level); + give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level, false, this); if ( killer_mob && diff --git a/zone/client.cpp b/zone/client.cpp index 6b8fb31444..f6286f47c3 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13113,7 +13113,8 @@ void Client::SetAAEXPPercentage(uint8 percentage) if (before_percentage > 0 && percentage == 0) { MessageString(Chat::White, AA_OFF); - } else if (before_percentage == 0 && percentage > 0) { + } + else if (before_percentage == 0 && percentage > 0) { MessageString(Chat::White, AA_ON); } diff --git a/zone/client.h b/zone/client.h index 75c48a4941..adba50bacc 100644 --- a/zone/client.h +++ b/zone/client.h @@ -72,6 +72,7 @@ namespace EQ #include "../common/repositories/trader_repository.h" #include "../common/guild_base.h" #include "../common/repositories/buyer_buy_lines_repository.h" +#include "../common/repositories/character_evolving_items_repository.h" #ifdef _WINDOWS // since windows defines these within windef.h (which windows.h include) @@ -710,13 +711,13 @@ class Client : public Mob void SendCrystalCounts(); uint64 GetExperienceForKill(Mob *against); - void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); + void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false, NPC* npc = nullptr); uint64 CalcEXP(uint8 conlevel = 0xFF, bool ignore_mods = false); void CalculateNormalizedAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp); void CalculateStandardAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp); void CalculateLeadershipExp(uint64 &add_exp, uint8 conlevel); void CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp, uint8 conlevel, bool resexp); - void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false); + void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false, NPC* npc = nullptr); void AddLevelBasedExp(ExpSource exp_source, uint8 exp_percentage, uint8 max_level = 0, bool ignore_mods = false); void SetLeadershipEXP(uint64 group_exp, uint64 raid_exp); void AddLeadershipEXP(uint64 group_exp, uint64 raid_exp); @@ -1816,6 +1817,18 @@ class Client : public Mob void RecordKilledNPCEvent(NPC *n); uint32 GetEXPForLevel(uint16 check_level); + + // Evolving Item Info + void ProcessEvolvingItem(const uint64 exp, const Mob* mob); + void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item); + void DoEvolveItemToggle(const EQApplicationPacket* app); + void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app); + bool DoEvolveCheckProgression(const EQ::ItemInstance &inst); + void SendEvolveXPWindowDetails(const EQApplicationPacket* app); + void DoEvolveTransferXP(const EQApplicationPacket* app); + void SendEvolveXPTransferWindow(); + void SendEvolveTransferResults(const EQ::ItemInstance &inst_from, const EQ::ItemInstance &inst_to, const EQ::ItemInstance &inst_from_new, const EQ::ItemInstance &inst_to_new, const uint32 compatibility, const uint32 max_transfer_level); + protected: friend class Mob; void CalcEdibleBonuses(StatBonuses* newbon); @@ -1975,6 +1988,8 @@ class Client : public Mob uint16 m_door_tool_entity_id; uint16 m_object_tool_entity_id; + + public: uint16 GetDoorToolEntityId() const; void SetDoorToolEntityId(uint16 door_tool_entity_id); diff --git a/zone/client_evolving_items.cpp b/zone/client_evolving_items.cpp new file mode 100644 index 0000000000..4fb2238b02 --- /dev/null +++ b/zone/client_evolving_items.cpp @@ -0,0 +1,451 @@ +#include "../common/evolving_items.h" + +#include "../common/events/player_event_logs.h" +#include "../common/global_define.h" + +#include "client.h" +#include "string_ids.h" +#include "worldserver.h" + +extern WorldServer worldserver; + +void Client::DoEvolveItemToggle(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + auto item = CharacterEvolvingItemsRepository::FindOne(database, in->unique_id); + + LogEvolveItemDetail( + "Character ID [{}] requested to set evolve item with unique id [{}] to status [{}]", + CharacterID(), + in->unique_id, + in->activated + ); + + if (!item.id) { + LogEvolveItemDetail( + "Character ID [{}] toggle evolve item unique id [{}] failed", CharacterID(), in->unique_id); + return; + } + + item.activated = in->activated; + const auto inst = GetInv().GetItem(GetInv().HasItem(item.item_id)); + inst->SetEvolveActivated(item.activated ? true : false); + + CharacterEvolvingItemsRepository::ReplaceOne(database, item); + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, item); +} + +void Client::SendEvolvingPacket(const int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item) +{ + auto out = std::make_unique(OP_EvolveItem, sizeof(EvolveItemToggle)); + const auto data = reinterpret_cast(out->pBuffer); + + LogEvolveItemDetail( + "Character ID [{}] requested info for evolving item with unique id [{}] status [{}] " + "percentage [{}]", + CharacterID(), + item.id, + item.activated, + item.progression + ); + + data->action = action; + data->unique_id = item.id; + data->percentage = item.progression; + data->activated = item.activated; + + QueuePacket(out.get()); + + LogEvolveItem( + "Sent evolve item with unique id [{}] status [{}] percentage [{}] to Character ID " + "[{}]", + data->unique_id, + data->activated, + data->percentage, + CharacterID() + ); +} + +void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) +{ + std::vector queue{}; + + for (auto &[key, inst]: GetInv().GetWorn()) { + LogEvolveItemDetail( + "CharacterID [{}] found equipped item ID [{}]", CharacterID(), inst->GetID()); + if (!inst->IsEvolving() || !inst->GetEvolveActivated()) { + LogEvolveItemDetail( + "CharacterID [{}], item ID [{}] not an evolving item.", CharacterID(), inst->GetID()); + continue; + } + + if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) { + LogEvolveItemDetail( + "CharacterID [{}], item ID [{}] timer not yet expired. [{}] secs remaining.", + CharacterID(), + inst->GetID(), + inst->GetTimers().at("evolve").GetRemainingTime() / 1000); + continue; + } + + auto const type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).type; + auto const sub_type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).sub_type; + + LogEvolveItemDetail( + "CharacterID [{}] item id [{}] type {} sub_type {} is Evolving. Continue processing...", + CharacterID(), + inst->GetID(), + type, + sub_type); + + switch (type) { + case EvolvingItems::Types::AMOUNT_OF_EXP: { + LogEvolveItemDetail("Type [{}] Processing sub_type", type); + if (sub_type == EvolvingItems::SubTypes::ALL_EXP || + (sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100); + } + else if ( + sub_type == EvolvingItems::SubTypes::ALL_EXP || + (sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100); + } + else if ( + sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100); + } + + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 1 Amount of EXP - SubType [{0}] - " + "Assigned [{2}] of exp to [{1}]", + sub_type, + inst->GetID(), + exp * 0.001 + ); + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + case EvolvingItems::Types::SPECIFIC_MOB_RACE: { + LogEvolveItemDetail("Type [{}] Processing sub type", type); + if (mob && mob->GetRace() == sub_type) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(1); + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, + inst->GetEvolveUniqueID(), + inst->GetEvolveCurrentAmount(), + inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 3 Specific Mob Race - SubType [{0}] " + "- Increased count by 1 for [{1}]", + sub_type, + inst->GetID() + ); + } + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + case EvolvingItems::Types::SPECIFIC_ZONE_ID: { + LogEvolveItemDetail("Type [{}] Processing sub type", type); + if (mob && mob->GetZoneID() == sub_type) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(1); + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, + inst->GetEvolveUniqueID(), + inst->GetEvolveCurrentAmount(), + inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 4 Specific Zone ID - SubType [{0}] " + "- Increased count by 1 for [{1}]", + sub_type, + inst->GetID() + ); + } + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + default: { + } + } + } + + if (!queue.empty()) { + for (auto const &i: queue) { + DoEvolveCheckProgression(*i); + } + } +} + +void Client::DoEvolveItemDisplayFinalResult(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const uint32 item_id = static_cast(in->unique_id & 0xFFFFFFFF); + if (item_id == 0) { + LogEvolveItem("Error - Item ID of final evolve item is blank."); + return; + } + + std::unique_ptr const inst(database.CreateItem(item_id)); + + LogEvolveItemDetail( + "Character ID [{}] requested to view final evolve item id [{}] for evolve item id [{}]", + CharacterID(), + item_id, + evolving_items_manager.GetFirstItemInLoreGroupByItemID(item_id)); + + inst->SetEvolveProgression(100); + + if (inst) { + LogEvolveItemDetail( + "Sending final result for item id [{}] to Character ID [{}]", item_id, CharacterID()); + SendItemPacket(0, inst.get(), ItemPacketViewLink); + } +} + +bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst) +{ + if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) { + return false; + } + + const auto new_item_id = evolving_items_manager.GetNextEvolveItemID(inst); + if (!new_item_id) { + return false; + } + + std::unique_ptr const new_inst(database.CreateItem(new_item_id)); + + PlayerEvent::EvolveItem e{}; + + RemoveItemBySerialNumber(inst.GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(inst, e); + e.status = "Evolved Item due to obtaining progression - Old Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*new_inst, true); + evolving_items_manager.LoadPlayerEvent(*new_inst, e); + e.status = "Evolved Item due to obtaining progression - New Evolve Item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + MessageString(Chat::Yellow, EVOLVE_ITEM_EVOLVED, inst.GetItem()->Name); + + LogEvolveItem( + "Evolved item id [{}] into item id [{}] for Character ID [{}]", + inst.GetID(), + new_inst->GetID(), + CharacterID()); + + return true; +} + +void Client::SendEvolveXPTransferWindow() +{ + auto out = std::make_unique(OP_EvolveItem, sizeof(EvolveItemToggle)); + const auto data = reinterpret_cast(out->pBuffer); + + data->action = 1; + + QueuePacket(out.get()); +} + +void Client::SendEvolveXPWindowDetails(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const auto item_1_slot = + GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + const auto item_2_slot = + GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + + if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) { + return; + } + + const auto inst_from = GetInv().GetItem(item_1_slot); + const auto inst_to = GetInv().GetItem(item_2_slot); + + if (!inst_from || !inst_to) { + return; + } + + const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to); + + if (!results.item_from_id || !results.item_to_id) { + SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0); + return; + } + + std::unique_ptr const inst_from_new(database.CreateItem(results.item_from_id)); + std::unique_ptr const inst_to_new(database.CreateItem(results.item_to_id)); + if (!inst_from_new || !inst_to_new) { + SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0); + return; + } + + inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount); + inst_from_new->CalculateEvolveProgression(); + inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount); + inst_to_new->CalculateEvolveProgression(); + + SendEvolveTransferResults( + *inst_from, *inst_to, *inst_from_new, *inst_to_new, results.compatibility, results.max_transfer_level); +} + +void Client::DoEvolveTransferXP(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const auto item_1_slot = + GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + const auto item_2_slot = + GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + + if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) { + return; + } + + const auto inst_from = GetInv().GetItem(item_1_slot); + const auto inst_to = GetInv().GetItem(item_2_slot); + + if (!inst_from || !inst_to) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to); + + if (!results.item_from_id || !results.item_to_id) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + std::unique_ptr const inst_from_new(database.CreateItem(results.item_from_id)); + std::unique_ptr const inst_to_new(database.CreateItem(results.item_to_id)); + + if (!inst_from_new || !inst_to_new) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount); + inst_from_new->CalculateEvolveProgression(); + inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount); + inst_to_new->CalculateEvolveProgression(); + + PlayerEvent::EvolveItem e{}; + + RemoveItemBySerialNumber(inst_from->GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(*inst_from, e); + e.status = "Transfer XP - Original FROM Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*inst_from_new, true); + evolving_items_manager.LoadPlayerEvent(*inst_from_new, e); + e.status = "Transfer XP - Updated FROM item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + RemoveItemBySerialNumber(inst_to->GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(*inst_to, e); + e.status = "Transfer XP - Original TO Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*inst_to_new, true); + evolving_items_manager.LoadPlayerEvent(*inst_to_new, e); + e.status = "Transfer XP - Updated TO Evolve item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + LogEvolveItem( + "Evolve Transfer XP resulted in evolved item id [{}] into item id [{}] for Character ID " + "[{}]", + inst_to->GetID(), + inst_to_new->GetID(), + CharacterID() + ); +} + +void Client::SendEvolveTransferResults( + const EQ::ItemInstance &inst_from, + const EQ::ItemInstance &inst_to, + const EQ::ItemInstance &inst_from_new, + const EQ::ItemInstance &inst_to_new, + const uint32 compatibility, + const uint32 max_transfer_level) +{ + std::stringstream ss; + cereal::BinaryOutputArchive ar(ss); + + EvolveXPWindowSend e{}; + e.action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS; + e.compatibility = compatibility; + e.item1_unique_id = inst_from.GetEvolveUniqueID(); + e.item2_unique_id = inst_to.GetEvolveUniqueID(); + e.max_transfer_level = max_transfer_level; + e.item1_present = 1; + e.item2_present = 1; + e.serialize_item_1 = inst_from_new.Serialize(0); + e.serialize_item_2 = inst_to_new.Serialize(0); + + { + ar(e); + } + + uint32 packet_size = sizeof(EvolveItemMessaging) + ss.str().length(); + + std::unique_ptr out(new EQApplicationPacket(OP_EvolveItem, packet_size)); + const auto data = reinterpret_cast(out->pBuffer); + + data->action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS; + memcpy(data->serialized_data, ss.str().data(), ss.str().length()); + + QueuePacket(out.get()); + + ss.str(""); + ss.clear(); +} diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ef671ef124..26da7ebce9 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -80,7 +80,6 @@ extern PetitionList petition_list; extern EntityList entity_list; typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); - //Use a map for connecting opcodes since it dosent get used a lot and is sparse std::map ConnectingOpcodes; //Use a static array for connected, for speed @@ -208,6 +207,7 @@ void MapOpcodes() ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; + ConnectedOpcodes[OP_EvolveItem] = &Client::Handle_OP_EvolveItem; ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; @@ -1309,7 +1309,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) // set to full support in case they're a gm with items in disabled expansion slots...but, have their gm flag off... // item loss will occur when they use the 'empty' slots, if this is not done m_inv.SetGMInventory(true); - loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ + loaditems = database.GetInventory(this); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ @@ -10748,7 +10748,12 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) InterrogateInventory(this, true, false, true, error); } - return; + for (int slot : {mi->to_slot, mi->from_slot}) { + auto item = GetInv().GetItem(slot); + if (item && item->IsEvolving()) { + CharacterEvolvingItemsRepository::UpdateOne(database, item->GetEvolvingDetails()); + } + } } void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app) @@ -17165,3 +17170,37 @@ void Client::Handle_OP_ShopRetrieveParcel(const EQApplicationPacket *app) auto parcel_in = (ParcelRetrieve_Struct *)app->pBuffer; DoParcelRetrieve(*parcel_in); } + +void Client::Handle_OP_EvolveItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(EvolveItemToggle)) { + LogError( + "Received Handle_OP_EvolveItem packet. Expected size {}, received size {}.", + sizeof(EvolveItemToggle), + app->size + ); + return; + } + + auto in = reinterpret_cast(app->pBuffer); + + switch (in->action) { + case EvolvingItems::Actions::UPDATE_ITEMS: { + DoEvolveItemToggle(app); + break; + } + case EvolvingItems::Actions::FINAL_RESULT: { + DoEvolveItemDisplayFinalResult(app); + break; + } + case EvolvingItems::Actions::TRANSFER_XP: { + DoEvolveTransferXP(app); + break; + } + case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: { + SendEvolveXPWindowDetails(app); + } + default: { + } + } +} diff --git a/zone/client_packet.h b/zone/client_packet.h index 4ef62bdf9d..2970f91dc6 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -114,6 +114,7 @@ void Handle_OP_Emote(const EQApplicationPacket *app); void Handle_OP_EndLootRequest(const EQApplicationPacket *app); void Handle_OP_EnvDamage(const EQApplicationPacket *app); + void Handle_OP_EvolveItem(const EQApplicationPacket *app); void Handle_OP_FaceChange(const EQApplicationPacket *app); void Handle_OP_FeignDeath(const EQApplicationPacket *app); void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); diff --git a/zone/command.cpp b/zone/command.cpp index 1007e09d32..35b920e53e 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -126,6 +126,7 @@ int command_init(void) command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) || command_add("entityvariable", "[clear|delete|set|view] - Modify entity variables for yourself or your target", AccountStatus::GMAdmin, command_entityvariable) || command_add("exptoggle", "[Toggle] - Toggle your or your target's experience gain.", AccountStatus::QuestTroupe, command_exptoggle) || + command_add("evolve", "Evolving Item manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_evolvingitems) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || command_add("factionassociation", "[factionid] [amount] - triggers a faction hits via association", AccountStatus::GMLeadAdmin, command_faction_association) || command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || @@ -826,6 +827,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/entityvariable.cpp" #include "gm_commands/exptoggle.cpp" #include "gm_commands/faction.cpp" +#include "gm_commands/evolving_items.cpp" #include "gm_commands/feature.cpp" #include "gm_commands/find.cpp" #include "gm_commands/fish.cpp" diff --git a/zone/command.h b/zone/command.h index 1899095169..09164e6bf3 100644 --- a/zone/command.h +++ b/zone/command.h @@ -38,6 +38,7 @@ void SendShowInventorySubCommands(Client *c); void SendFixMobSubCommands(Client *c); void SendDataBucketsSubCommands(Client *c); void SendParcelsSubCommands(Client *c); +void SendEvolvingItemsSubCommands(Client *c); // Commands void command_acceptrules(Client *c, const Seperator *sep); @@ -80,6 +81,7 @@ void command_emptyinventory(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep); void command_entityvariable(Client *c, const Seperator *sep); void command_exptoggle(Client *c, const Seperator *sep); +void command_evolvingitems(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); void command_faction_association(Client *c, const Seperator *sep); void command_feature(Client *c, const Seperator *sep); diff --git a/zone/doors.cpp b/zone/doors.cpp index 3ec9c86af4..036f389284 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -30,6 +30,7 @@ #include "string_ids.h" #include "worldserver.h" #include "zonedb.h" +#include "../common/evolving_items.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include @@ -610,6 +611,10 @@ void Doors::HandleClick(Client *sender, uint8 trigger) } } } + + if (GetOpenType() == 40 && GetZone(GetDoorZone(),0)) { + sender->SendEvolveXPTransferWindow(); + } } void Doors::Open(Mob *sender, bool alt_mode) diff --git a/zone/doors.h b/zone/doors.h index 3c1ec37042..b93aed6570 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -77,6 +77,8 @@ class Doors : public Entity bool IsDoorBlacklisted(); + const char* GetDoorZone() const { return m_zone_name; } + private: bool GetIsDoorBlacklisted(); diff --git a/zone/exp.cpp b/zone/exp.cpp index be6b8af2b8..a1f8c8adf6 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -497,7 +497,7 @@ void Client::CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp, add_exp = GetEXP() + add_exp; } -void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp) { +void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp, NPC* npc) { if (!IsEXPEnabled()) { return; } @@ -574,10 +574,10 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo } // Now update our character's normal and AA xp - SetEXP(exp_source, exp, aaexp, resexp); + SetEXP(exp_source, exp, aaexp, resexp, npc); } -void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp) { +void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp, NPC* npc) { uint64 current_exp = GetEXP(); uint64 current_aa_exp = GetAAXP(); uint64 total_current_exp = current_exp + current_aa_exp; @@ -677,6 +677,7 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool } } } + ProcessEvolvingItem(exp_gained, npc); } else if(total_add_exp < total_current_exp) { //only loss message if you lose exp, no message if you gained/lost nothing. uint64 exp_lost = current_exp - set_exp; float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100; @@ -1205,7 +1206,7 @@ void Group::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) { if (diff >= max_diff) { const uint64 tmp = (m->GetLevel() + 3) * (m->GetLevel() + 3) * 75 * 35 / 10; const uint64 tmp2 = group_experience / member_count; - m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level); + m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC()); } } } @@ -1256,7 +1257,7 @@ void Raid::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) { if (diff >= max_diff) { const uint64 tmp = (m.member->GetLevel() + 3) * (m.member->GetLevel() + 3) * 75 * 35 / 10; const uint64 tmp2 = (raid_experience / member_modifier) + 1; - m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level); + m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC()); } } } diff --git a/zone/gm_commands/evolving_items.cpp b/zone/gm_commands/evolving_items.cpp new file mode 100644 index 0000000000..3971c00213 --- /dev/null +++ b/zone/gm_commands/evolving_items.cpp @@ -0,0 +1,301 @@ +#include "../client.h" +#include "../command.h" +#include "../../common/evolving_items.h" + +void command_evolvingitems(Client *c, const Seperator *sep) +{ + const uint16 arguments = sep->argnum; + if (!arguments) { + SendEvolvingItemsSubCommands(c); + return; + } + + Client* t = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + const bool is_item = !strcasecmp(sep->arg[1], "item"); + const bool is_target = !strcasecmp(sep->arg[1], "target"); + + if (!is_item && !is_target) { + SendEvolvingItemsSubCommands(c); + return; + } + + if (is_target) { + if (arguments > 1) { + c->Message(Chat::White, "Usage: #evolve target"); + } else { + c->Message(Chat::Red, "Worn Items"); + for (auto const &[key, value]: t->GetInv().GetWorn()) { + if (!value->IsEvolving()) { + continue; + } + + auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID()); + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + value->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + value->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Final Item ID | {:0d}", + value->GetEvolveFinalItemID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + value->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + value->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Current Amount | {:0d}", + value->GetEvolveCurrentAmount() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Required Amount | {:0d}", + item.required_amount + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + value->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Type | {}", + item.type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Subtype | {}", + item.sub_type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + value->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + value->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } + + c->Message(Chat::Red, "Personal Items"); + for (auto const &[key, value]: t->GetInv().GetPersonal()) { + if (!value->IsEvolving()) { + continue; + } + + auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID()); + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + value->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + value->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Final Item ID | {:0d}", + value->GetEvolveFinalItemID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + value->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + value->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Current Amount | {:0d}", + value->GetEvolveCurrentAmount() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Required Amount | {:0d}", + item.required_amount + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + value->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Type | {}", + item.type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Subtype | {}", + item.sub_type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + value->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + value->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } + } + } else if (is_item) { + if (arguments > 2) { + c->Message(Chat::White, "Usage: #evolve item item_id"); + } else if (sep->IsNumber(2)) { + auto item_id = Strings::ToUnsignedInt(sep->arg[2]); + auto item = c->GetInv().GetItem(c->GetInv().HasItem(item_id)); + if (item) { + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + item->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Slot | {:0d}", + c->GetInv().HasItem(item_id) + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + item->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + item->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + item->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + item->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + item->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + item->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } else { + c->Message( + Chat::Red, + fmt::format( + "Item {} could not be found in your inventory.", + item_id + ).c_str() + ); + } + } else { + SendEvolvingItemsSubCommands(c); + } + } +} + +void SendEvolvingItemsSubCommands(Client *c) +{ + c->Message(Chat::White, "#evolve item item_id (Shows evolve values within the iteminstance)"); + c->Message( + Chat::White, + "#evolve target (Shows evolve values within the target's cache - Must have a player target selected.)" + ); +} diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 30f4c31daf..f89c1005c1 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -25,6 +25,7 @@ #include "zonedb.h" #include "../common/events/player_event_logs.h" #include "bot.h" +#include "../common/evolving_items.h" #include "../common/repositories/character_corpse_items_repository.h" extern WorldServer worldserver; @@ -1091,7 +1092,11 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up } // end QS code - bool isDeleted = m_inv.DeleteItem(slot_id, quantity); + uint64 evolve_id = m_inv[slot_id]->GetEvolveUniqueID(); + bool isDeleted = m_inv.DeleteItem(slot_id, quantity); + if (isDeleted && evolve_id && (slot_id > EQ::invslot::TRADE_END || slot_id < EQ::invslot::TRADE_BEGIN)) { + CharacterEvolvingItemsRepository::SoftDelete(database, evolve_id); + } const EQ::ItemInstance* inst = nullptr; if (slot_id == EQ::invslot::slotCursor) { @@ -1143,6 +1148,8 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up bool Client::PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update) { LogInventory("Putting item [{}] ([{}]) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID); + + evolving_items_manager.DoLootChecks(CharacterID(), EQ::invslot::slotCursor, inst); m_inv.PushCursor(inst); if (client_update) { @@ -1163,9 +1170,9 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo if (slot_id == EQ::invslot::slotCursor) { // don't trust macros before conditional statements... return PushItemOnCursor(inst, client_update); } - else { - m_inv.PutItem(slot_id, inst); - } + + evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst); + m_inv.PutItem(slot_id, inst); if (client_update) { @@ -1173,15 +1180,16 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo //SendWearChange(EQ::InventoryProfile::CalcMaterialFromSlot(slot_id)); } + CalcBonuses(); + if (slot_id == EQ::invslot::slotCursor) { auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(CharacterID(), s, e); } - else { - return database.SaveInventory(CharacterID(), &inst, slot_id); - } - CalcBonuses(); + return database.SaveInventory(CharacterID(), &inst, slot_id); + + //CalcBonuses(); // this never fires?? // a lot of wasted checks and calls coded above... } @@ -1191,6 +1199,8 @@ void Client::PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, Loo bool cursor_empty = m_inv.CursorEmpty(); + evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst); + if (slot_id == EQ::invslot::slotCursor) { m_inv.PushCursor(inst); auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); diff --git a/zone/lua_iteminst.cpp b/zone/lua_iteminst.cpp index ad9ed14054..f81cfc88b1 100644 --- a/zone/lua_iteminst.cpp +++ b/zone/lua_iteminst.cpp @@ -255,11 +255,6 @@ int8 Lua_ItemInst::GetMaxEvolveLvl() { return self->GetMaxEvolveLvl(); } -uint32 Lua_ItemInst::GetKillsNeeded(uint8 current_level) { - Lua_Safe_Call_Int(); - return self->GetKillsNeeded(current_level); -} - Lua_ItemInst Lua_ItemInst::Clone() { Lua_Safe_Call_Class(Lua_ItemInst); return Lua_ItemInst(self->Clone(), true); @@ -348,6 +343,96 @@ std::string Lua_ItemInst::GetItemLink() return linker.GenerateLink(); } +void Lua_ItemInst::AddEvolveAmount(uint64 amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveAddToCurrentAmount(amount); +} + +uint32 Lua_ItemInst::GetAugmentEvolveUniqueID(uint8 slot_id) +{ + Lua_Safe_Call_Int(); + return self->GetAugmentEvolveUniqueID(slot_id); +} + +bool Lua_ItemInst::GetEvolveActivated() +{ + Lua_Safe_Call_Bool(); + return self->GetEvolveActivated(); +} + +uint64 Lua_ItemInst::GetEvolveAmount() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveCurrentAmount(); +} + +uint32 Lua_ItemInst::GetEvolveCharacterID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveCharID(); +} + +bool Lua_ItemInst::GetEvolveEquipped() +{ + Lua_Safe_Call_Bool(); + return self->GetEvolveEquipped(); +} + +uint32 Lua_ItemInst::GetEvolveFinalItemID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveFinalItemID(); +} + +uint32 Lua_ItemInst::GetEvolveItemID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveItemID(); +} + +int8 Lua_ItemInst::GetEvolveLevel() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveLvl(); +} + +uint32 Lua_ItemInst::GetEvolveLoreID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveLoreID(); +} + +float Lua_ItemInst::GetEvolveProgression() +{ + Lua_Safe_Call_Real(); + return self->GetEvolveProgression(); +} + +uint64 Lua_ItemInst::GetEvolveUniqueID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveUniqueID(); +} + +bool Lua_ItemInst::IsEvolving() +{ + Lua_Safe_Call_Bool(); + return self->IsEvolving(); +} + +void Lua_ItemInst::SetEvolveAmount(uint64 amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveCurrentAmount(amount); +} + +void Lua_ItemInst::SetEvolveProgression(float amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveProgression(amount); +} + luabind::scope lua_register_iteminst() { return luabind::class_("ItemInst") .def(luabind::constructor<>()) @@ -356,12 +441,14 @@ luabind::scope lua_register_iteminst() { .property("null", &Lua_ItemInst::Null) .property("valid", &Lua_ItemInst::Valid) .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) + .def("AddEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::AddEvolveAmount) .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers) .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) .def("ContainsAugmentByID", (bool(Lua_ItemInst::*)(uint32))&Lua_ItemInst::ContainsAugmentByID) .def("CountAugmentByID", (int(Lua_ItemInst::*)(uint32))&Lua_ItemInst::CountAugmentByID) .def("DeleteCustomData", (void(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::DeleteCustomData) .def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment) + .def("GetAugmentEvolveUniqueID", (uint32(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetAugmentEvolveUniqueID) .def("GetAugmentIDs", (luabind::object(Lua_ItemInst::*)(lua_State*))&Lua_ItemInst::GetAugmentIDs) .def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID) .def("GetAugmentType", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetAugmentType) @@ -370,13 +457,22 @@ luabind::scope lua_register_iteminst() { .def("GetCustomData", (std::string(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::GetCustomData) .def("GetCustomDataString", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCustomDataString) .def("GetExp", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetExp) + .def("GetEvolveActivated", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveActivated) + .def("GetEvolveAmount", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveAmount) + .def("GetEvolveCharacterID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveCharacterID) + .def("GetEvolveEquipped", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveEquipped) + .def("GetEvolveFinalItemID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveFinalItemID) + .def("GetEvolveItemID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveItemID) + .def("GetEvolveLevel", (int8(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLevel) + .def("GetEvolveLoreID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLoreID) + .def("GetEvolveProgression", (float(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveProgression) + .def("GetEvolveUniqueID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveUniqueID) .def("GetID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetID) .def("GetItem", (Lua_Item(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItem) .def("GetItem", (Lua_ItemInst(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetItem) .def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID) .def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink) .def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID) - .def("GetKillsNeeded", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetKillsNeeded) .def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl) .def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName) .def("GetPrice", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetPrice) @@ -389,6 +485,7 @@ luabind::scope lua_register_iteminst() { .def("IsAugmented", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmented) .def("IsEquipable", (bool(Lua_ItemInst::*)(int16))&Lua_ItemInst::IsEquipable) .def("IsEquipable", (bool(Lua_ItemInst::*)(uint16,uint16))&Lua_ItemInst::IsEquipable) + .def("IsEvolving", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsEvolving) .def("IsExpendable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsExpendable) .def("IsInstNoDrop", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsInstNoDrop) .def("IsStackable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsStackable) @@ -404,6 +501,8 @@ luabind::scope lua_register_iteminst() { .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,float))&Lua_ItemInst::SetCustomData) .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,int))&Lua_ItemInst::SetCustomData) .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,const std::string&))&Lua_ItemInst::SetCustomData) + .def("SetEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::SetEvolveAmount) + .def("SetEvolveProgression", (void(Lua_ItemInst::*)(float))&Lua_ItemInst::SetEvolveProgression) .def("SetExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetExp) .def("SetInstNoDrop", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetInstNoDrop) .def("SetPrice", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetPrice) diff --git a/zone/lua_iteminst.h b/zone/lua_iteminst.h index 2ff99165c2..a4c7529e40 100644 --- a/zone/lua_iteminst.h +++ b/zone/lua_iteminst.h @@ -77,7 +77,6 @@ class Lua_ItemInst : public Lua_Ptr void SetExp(uint32 exp); void AddExp(uint32 exp); int8 GetMaxEvolveLvl(); - uint32 GetKillsNeeded(uint8 current_level); Lua_ItemInst Clone(); void SetTimer(std::string name, uint32 time); void StopTimer(std::string name); @@ -91,6 +90,21 @@ class Lua_ItemInst : public Lua_Ptr void ItemSay(const char* text, uint8 language_id); luabind::object GetAugmentIDs(lua_State* L); std::string GetItemLink(); + void AddEvolveAmount(uint64 amount); + uint32 GetAugmentEvolveUniqueID(uint8 slot_id); + bool GetEvolveActivated(); + uint64 GetEvolveAmount(); + uint32 GetEvolveCharacterID(); + bool GetEvolveEquipped(); + uint32 GetEvolveFinalItemID(); + uint32 GetEvolveItemID(); + int8 GetEvolveLevel(); + uint32 GetEvolveLoreID(); + float GetEvolveProgression(); + uint64 GetEvolveUniqueID(); + bool IsEvolving(); + void SetEvolveAmount(uint64 amount); + void SetEvolveProgression(float amount); private: bool cloned_; diff --git a/zone/main.cpp b/zone/main.cpp index a05af774a4..5d463fa9b1 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -52,6 +52,7 @@ #include "task_manager.h" #include "quest_parser_collection.h" #include "embparser.h" +#include "../common/evolving_items.h" #include "lua_parser.h" #include "questmgr.h" #include "npc_scale_manager.h" @@ -110,6 +111,7 @@ PathManager path; PlayerEventLogs player_event_logs; DatabaseUpdate database_update; SkillCaps skill_caps; +EvolvingItemsManager evolving_items_manager; const SPDat_Spell_Struct* spells; int32 SPDAT_RECORDS = -1; @@ -387,6 +389,11 @@ int main(int argc, char **argv) title_manager.LoadTitles(); content_db.LoadTributes(); + // Load evolving item data + evolving_items_manager.SetDatabase(&database); + evolving_items_manager.SetContentDatabase(&content_db); + evolving_items_manager.LoadEvolvingItems(); + database.GetDecayTimes(npcCorpseDecayTimes); if (!EQ::ProfanityManager::LoadProfanityList(&database)) { diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp index fea1bf0616..53e906de7e 100644 --- a/zone/perl_questitem.cpp +++ b/zone/perl_questitem.cpp @@ -152,11 +152,6 @@ uint32 Perl_QuestItem_GetItemScriptID(EQ::ItemInstance* self) return self->GetItemScriptID(); } -uint32 Perl_QuestItem_GetKillsNeeded(EQ::ItemInstance* self, uint8 current_level) -{ - return self->GetKillsNeeded(current_level); -} - int8 Perl_QuestItem_GetMaxEvolveLevel(EQ::ItemInstance* self) { return self->GetMaxEvolveLvl(); @@ -297,12 +292,88 @@ std::string Perl_QuestItem_GetItemLink(EQ::ItemInstance* self) return linker.GenerateLink(); } +void Perl_QuestItem_AddEvolveAmount(EQ::ItemInstance* self, uint64 amount) +{ + self->SetEvolveAddToCurrentAmount(amount); +} + +uint32 Perl_QuestItem_GetAugmentEvolveUniqueID(EQ::ItemInstance* self, uint8 slot_id) +{ + return self->GetAugmentEvolveUniqueID(slot_id); +} + +bool Perl_QuestItem_GetEvolveActivated(EQ::ItemInstance* self) +{ + return self->GetEvolveActivated(); +} + +uint64 Perl_QuestItem_GetEvolveAmount(EQ::ItemInstance* self) +{ + return self->GetEvolveCurrentAmount(); +} + +uint32 Perl_QuestItem_GetEvolveCharacterID(EQ::ItemInstance* self) +{ + return self->GetEvolveCharID(); +} + +bool Perl_QuestItem_GetEvolveEquipped(EQ::ItemInstance* self) +{ + return self->GetEvolveEquipped(); +} + +uint32 Perl_QuestItem_GetEvolveItemID(EQ::ItemInstance* self) +{ + return self->GetEvolveItemID(); +} + +uint32 Perl_QuestItem_GetEvolveFinalItemID(EQ::ItemInstance* self) +{ + return self->GetEvolveFinalItemID(); +} + +int8 Perl_QuestItem_GetEvolveLevel(EQ::ItemInstance* self) +{ + return self->GetEvolveLvl(); +} + +uint32 Perl_QuestItem_GetEvolveLoreID(EQ::ItemInstance* self) +{ + return self->GetEvolveLoreID(); +} + +double Perl_QuestItem_GetEvolveProgression(EQ::ItemInstance* self) +{ + return self->GetEvolveProgression(); +} + +uint64 Perl_QuestItem_GetEvolveUniqueID(EQ::ItemInstance* self) +{ + return self->GetEvolveUniqueID(); +} + +bool Perl_QuestItem_IsEvolving(EQ::ItemInstance* self) +{ + return self->IsEvolving(); +} + +void Perl_QuestItem_SetEvolveProgression(EQ::ItemInstance* self, float amount) +{ + self->SetEvolveProgression(amount); +} + +void Perl_QuestItem_SetEvolveAmount(EQ::ItemInstance* self, uint64 amount) +{ + self->SetEvolveCurrentAmount(amount); +} + void perl_register_questitem() { perl::interpreter perl(PERL_GET_THX); auto package = perl.new_class("QuestItem"); + package.add("AddEvolveAmount", &Perl_QuestItem_AddEvolveAmount); package.add("AddEXP", &Perl_QuestItem_AddEXP); package.add("ClearTimers", &Perl_QuestItem_ClearTimers); package.add("Clone", &Perl_QuestItem_Clone); @@ -310,6 +381,7 @@ void perl_register_questitem() package.add("CountAugmentByID", &Perl_QuestItem_CountAugmentByID); package.add("DeleteCustomData", &Perl_QuestItem_DeleteCustomData); package.add("GetAugment", &Perl_QuestItem_GetAugment); + package.add("GetAugmentEvolveUniqueID", &Perl_QuestItem_GetAugmentEvolveUniqueID); package.add("GetAugmentIDs", &Perl_QuestItem_GetAugmentIDs); package.add("GetAugmentItemID", &Perl_QuestItem_GetAugmentItemID); package.add("GetAugmentType", &Perl_QuestItem_GetAugmentType); @@ -317,6 +389,16 @@ void perl_register_questitem() package.add("GetColor", &Perl_QuestItem_GetColor); package.add("GetCustomData", &Perl_QuestItem_GetCustomData); package.add("GetCustomDataString", &Perl_QuestItem_GetCustomDataString); + package.add("GetEvolveActivated", &Perl_QuestItem_GetEvolveActivated); + package.add("GetEvolveAmount", &Perl_QuestItem_GetEvolveAmount); + package.add("GetEvolveCharacterID", &Perl_QuestItem_GetEvolveCharacterID); + package.add("GetEvolveEquipped", &Perl_QuestItem_GetEvolveEquipped); + package.add("GetEvolveFinalItemID", &Perl_QuestItem_GetEvolveFinalItemID); + package.add("GetEvolveItemID", &Perl_QuestItem_GetEvolveItemID); + package.add("GetEvolveLevel", &Perl_QuestItem_GetEvolveLevel); + package.add("GetEvolveLoreID", &Perl_QuestItem_GetEvolveLoreID); + package.add("GetEvolveProgression", &Perl_QuestItem_GetEvolveProgression); + package.add("GetEvolveUniqueID", &Perl_QuestItem_GetEvolveUniqueID); package.add("GetEXP", &Perl_QuestItem_GetEXP); package.add("GetID", &Perl_QuestItem_GetID); package.add("GetItem", (EQ::ItemData*(*)(EQ::ItemInstance*))&Perl_QuestItem_GetItem); @@ -324,7 +406,6 @@ void perl_register_questitem() package.add("GetItemID", &Perl_QuestItem_GetItemID); package.add("GetItemLink", &Perl_QuestItem_GetItemLink); package.add("GetItemScriptID", &Perl_QuestItem_GetItemScriptID); - package.add("GetKillsNeeded", &Perl_QuestItem_GetKillsNeeded); package.add("GetMaxEvolveLevel", &Perl_QuestItem_GetMaxEvolveLevel); package.add("GetName", &Perl_QuestItem_GetName); package.add("GetPrice", &Perl_QuestItem_GetPrice); @@ -337,6 +418,7 @@ void perl_register_questitem() package.add("IsAugmented", &Perl_QuestItem_IsAugmented); package.add("IsEquipable", (bool(*)(EQ::ItemInstance*, int16))&Perl_QuestItem_IsEquipable); package.add("IsEquipable", (bool(*)(EQ::ItemInstance*, uint16, uint16))&Perl_QuestItem_IsEquipable); + package.add("IsEvolving", &Perl_QuestItem_IsEvolving); package.add("IsExpendable", &Perl_QuestItem_IsExpendable); package.add("IsInstanceNoDrop", &Perl_QuestItem_IsInstanceNoDrop); package.add("IsStackable", &Perl_QuestItem_IsStackable); @@ -352,6 +434,8 @@ void perl_register_questitem() package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, float))&Perl_QuestItem_SetCustomData); package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, int))&Perl_QuestItem_SetCustomData); package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, std::string))&Perl_QuestItem_SetCustomData); + package.add("SetEvolveAmount", &Perl_QuestItem_SetEvolveAmount); + package.add("SetEvolveProgression", &Perl_QuestItem_SetEvolveProgression); package.add("SetEXP", &Perl_QuestItem_SetEXP); package.add("SetInstanceNoDrop", &Perl_QuestItem_SetInstanceNoDrop); package.add("SetPrice", &Perl_QuestItem_SetPrice); diff --git a/zone/string_ids.h b/zone/string_ids.h index b778170b62..0da8fc03e3 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -419,6 +419,11 @@ #define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank. #define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits. #define GUILD_BANK_EMPTY_HANDS 6108 // You must empty your hands to withdraw from the Guild Bank. +#define EVOLVE_ITEM_EVOLVED 6145 //Your %1 has evolved! +#define EVOLVE_DETAILS 6146 //Evolving: Level %1/%2 %3%% %4 +#define EVOLVE_LEVEL_LIMIT 6147 //Your %1 can not evolve until you reach level %2. +#define EVOLVE_XP_TXFR_CONFIRM 6148 //Are you sure you want to transfer experience between these two items? +#define EVOLVE_XP_TXFRD 6149 //Your item's experience has been transferred! #define TRADESKILL_COMBINE_LORE 6199 // Combine would result in a LORE item (%1) you already possess. #define TRANSFORM_FAILED 6326 //This mold cannot be applied to your %1. #define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1. diff --git a/zone/trading.cpp b/zone/trading.cpp index a64dc33e9d..d9fb3499b2 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -371,6 +371,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { + inst->TransferOwnership(database, other->CharacterID()); LogTrading("Container [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; @@ -482,6 +483,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (other->PutItemInInventory(partial_slot, *partial_inst, true)) { LogTrading("Partial stack [{}] ([{}]) successfully transferred, deleting [{}] charges from trade slot", inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges())); + inst->TransferOwnership(database, other->CharacterID()); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; @@ -589,6 +591,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { + inst->TransferOwnership(database, other->CharacterID()); LogTrading("Item [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index f346fd57cd..4000397a38 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -52,7 +52,7 @@ #include "../common/repositories/zone_repository.h" #include "../common/repositories/trader_repository.h" - +#include "../common/repositories/character_evolving_items_repository.h" #include #include From 37a7b7fc419ec5012a4c718aaa1570bcc1fee6a2 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:02:53 -0400 Subject: [PATCH 56/73] [Feature] Add Alternate Bazaar Search Approach (#4600) * Add Alternate Bazaar Search This adds an alternate bazaar search allowing multinstance bazaar searching and traders above 600. Allows searches based on Bazaar Shard * Update worldserver.cpp --------- Co-authored-by: Mitch Freeman Co-authored-by: Akkadius --- common/bazaar.cpp | 25 +++++++++- common/repositories/trader_repository.h | 42 ++++++++++++++++- common/ruletypes.h | 1 + zone/client.h | 2 +- zone/client_packet.cpp | 20 ++++++++ zone/trading.cpp | 63 ++++++++++++++++++++++--- zone/worldserver.cpp | 26 ++++++++-- 7 files changed, 166 insertions(+), 13 deletions(-) diff --git a/common/bazaar.cpp b/common/bazaar.cpp index b24fcb1d19..cce3fcdd3b 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -31,6 +31,7 @@ Bazaar::GetSearchResults( char_zone_instance_id ); + bool convert = false; std::string search_criteria_trader("TRUE "); if (search.search_scope == NonRoFBazaarSearchScope) { @@ -51,8 +52,24 @@ Bazaar::GetSearchResults( ); } else if (search.trader_id > 0) { - search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (search.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + convert = true; + search_criteria_trader.append(fmt::format( + " AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}", + Zones::BAZAAR, + search.trader_id - TraderRepository::TRADER_CONVERT_ID) + ); + } + else { + search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + } + } + else { + search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + } } + if (search.min_cost != 0) { search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000)); } @@ -355,6 +372,12 @@ Bazaar::GetSearchResults( } LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (convert || (r.trader_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)) { + r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id; + } + } + all_entries.push_back(r); } diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 2999b738a0..a8e3c13c17 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -12,6 +12,7 @@ class TraderRepository : public BaseTraderRepository { public: + static constexpr uint32 TRADER_CONVERT_ID = 4000000000; struct DistinctTraders_Struct { uint32 trader_id; @@ -40,7 +41,11 @@ class TraderRepository : public BaseTraderRepository { int32 char_zone_instance_id ); - static BulkTraders_Struct GetDistinctTraders(Database &db, uint32 char_zone_instance_id, uint32 max_results) + static BulkTraders_Struct GetDistinctTraders( + Database &db, + uint32 char_zone_instance_id, + uint32 max_results = std::numeric_limits::max() + ) { BulkTraders_Struct all_entries{}; std::vector distinct_traders; @@ -49,7 +54,9 @@ class TraderRepository : public BaseTraderRepository { "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " "JOIN character_data AS c ON t.char_id = c.id " - "ORDER BY t.char_zone_instance_id = {} DESC LIMIT {};", + "WHERE t.char_zone_instance_id = {} " + "ORDER BY t.char_zone_instance_id ASC " + "LIMIT {}", char_zone_instance_id, max_results) ); @@ -227,6 +234,37 @@ class TraderRepository : public BaseTraderRepository { return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids))); } + + static DistinctTraders_Struct GetTraderByInstanceAndSerialnumber( + Database &db, + uint32 instance_id, + const char *serial_number + ) + { + DistinctTraders_Struct trader{}; + + auto query = fmt::format( + "SELECT t.id, t.char_id, c.name " + "FROM trader AS t " + "JOIN character_data AS c ON c.id = t.char_id " + "WHERE t.char_zone_id = 151 AND t.char_zone_instance_id = {} AND t.item_sn = {} LIMIT 1", + instance_id, + serial_number + ); + + auto results = db.QueryDatabase(query); + + if (results.RowCount() == 0) { + return trader; + } + + auto row = results.begin(); + std::string name = row[2]; + trader.trader_id = Strings::ToUnsignedInt(row[1]); + trader.trader_name = row[2] ? row[2] : ""; + + return trader; + } }; #endif //EQEMU_TRADER_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index e27ed3f3cf..2b52e081d1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -830,6 +830,7 @@ RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a ba RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.") RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.") RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.") +RULE_BOOL(Bazaar, UseAlternateBazaarSearch, false, "Allows the bazaar search window to search across bazaar shards. Default is false.") RULE_CATEGORY_END() RULE_CATEGORY(Mail) diff --git a/zone/client.h b/zone/client.h index adba50bacc..e8a93bac50 100644 --- a/zone/client.h +++ b/zone/client.h @@ -295,7 +295,7 @@ class Client : public Mob void SendBazaarDone(uint32 trader_id); void SendBulkBazaarTraders(); void SendBulkBazaarBuyers(); - void DoBazaarInspect(const BazaarInspect_Struct &in); + void DoBazaarInspect(BazaarInspect_Struct &in); void SendBazaarDeliveryCosts(); static std::string DetermineMoneyString(uint64 copper); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 26da7ebce9..92ef6a3d90 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -15535,6 +15535,26 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) // Client has elected to buy an item from a Trader // auto in = (TraderBuy_Struct *) app->pBuffer; + + if (RuleB(Bazaar, UseAlternateBazaarSearch) && in->trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + return; + } + + in->trader_id = trader.trader_id; + strn0cpy(in->seller_name, trader.trader_name.c_str(), sizeof(in->seller_name)); + } + auto trader = entity_list.GetClientByID(in->trader_id); switch (in->method) { diff --git a/zone/trading.cpp b/zone/trading.cpp index d9fb3499b2..bdbb6326c9 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -3222,11 +3222,42 @@ void Client::SendBulkBazaarTraders() return; } - auto results = TraderRepository::GetDistinctTraders( - database, - GetInstanceID(), - EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit - ); + TraderRepository::BulkTraders_Struct results{}; + + if (RuleB(Bazaar, UseAlternateBazaarSearch)) + { + if (GetZoneID() == Zones::BAZAAR) { + results = TraderRepository::GetDistinctTraders(database, GetInstanceID()); + } + + uint32 number = 1; + auto shards = CharacterDataRepository::GetInstanceZonePlayerCounts(database, Zones::BAZAAR); + for (auto const &shard: shards) { + if (GetZoneID() != Zones::BAZAAR || (GetZoneID() == Zones::BAZAAR && GetInstanceID() != shard.instance_id)) { + + TraderRepository::DistinctTraders_Struct t{}; + t.entity_id = 0; + t.trader_id = TraderRepository::TRADER_CONVERT_ID + shard.instance_id; + t.trader_name = fmt::format("Bazaar Shard {}", number); + t.zone_id = Zones::BAZAAR; + t.zone_instance_id = shard.instance_id; + results.count += 1; + results.name_length += t.trader_name.length() + 1; + results.traders.push_back(t); + } + + number++; + } + } + else { + results = TraderRepository::GetDistinctTraders( + database, + GetInstanceID(), + EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit + ); + } + + SetTraderCount(results.count); SetTraderCount(results.count); @@ -3251,8 +3282,28 @@ void Client::SendBulkBazaarTraders() QueuePacket(outapp.get()); } -void Client::DoBazaarInspect(const BazaarInspect_Struct &in) +void Client::DoBazaarInspect(BazaarInspect_Struct &in) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (in.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + fmt::format("{}", in.serial_number).c_str() + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + in.serial_number + ); + return; + } + + in.trader_id = trader.trader_id; + } + } + auto items = TraderRepository::GetWhere( database, fmt::format("`char_id` = '{}' AND `item_sn` = '{}'", in.trader_id, in.serial_number) ); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 8112d422cd..995887f17f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3926,16 +3926,36 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) out->action = AddTraderToBazaarWindow; if (c.second->GetTraderCount() < EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->IncrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { c.second->IncrementTraderCount(); c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } } - break; } case TraderOff: { out->action = RemoveTraderFromBazaarWindow; - c.second->DecrementTraderCount(); - c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + if (c.second->GetTraderCount() <= + EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } break; } default: { From 75698a809f66b42024b6ff4a7d08c8db27dfb86f Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Jan 2025 20:18:17 -0500 Subject: [PATCH 57/73] [Feature] Add Support for Item Previews (#4599) * [Feature] Add Support for Item Previews * Update client_packet.cpp --- common/emu_oplist.h | 1 + common/patches/rof.cpp | 9 +- common/patches/rof2.cpp | 30 +- common/patches/rof2_ops.h | 3 +- utils/patches/patch_RoF2.conf | 1 + utils/scripts/opcode_scripts/opcodelist.txt | 2402 +++++++++---------- zone/client_packet.cpp | 25 + zone/client_packet.h | 1 + zone/lua_packet.cpp | 1 + 9 files changed, 1269 insertions(+), 1204 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index fcfd63c633..6b5ab12ab9 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -290,6 +290,7 @@ N(OP_ItemLinkText), N(OP_ItemName), N(OP_ItemPacket), N(OP_ItemPreview), +N(OP_ItemPreviewRequest), N(OP_ItemRecastDelay), N(OP_ItemVerifyReply), N(OP_ItemVerifyRequest), diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 7d16d95e4e..9a6facac73 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -5188,7 +5188,14 @@ namespace RoF //sprintf(hdr.unknown000, "06e0002Y1W00"); - snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID); + strn0cpy( + hdr.unknown000, + fmt::format( + "{:016}\0", + packet_type == ItemPacketInvalid ? 0 : inst->GetSerialNumber() + ).c_str(), + sizeof(hdr.unknown000) + ); hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 1000) ? 0xFFFFFFFF : inst->GetCharges()) : 1); hdr.unknown004 = 0; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 15f3be60eb..3e05972e65 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2105,6 +2105,33 @@ namespace RoF2 } } + ENCODE(OP_ItemPreviewRequest) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + uchar* in_buf = in->pBuffer; + + auto int_item = (EQ::InternalSerializedItem_Struct*) in_buf; + + EQ::OutBuffer buf; + EQ::OutBuffer::pos_type last_pos = buf.tellp(); + + SerializeItem(buf, (const EQ::ItemInstance*) int_item->inst, int_item->slot_id, 0, ItemPacketInvalid); + if (buf.tellp() == last_pos) { + LogNetcode("RoF2::ENCODE(OP_ItemPreviewRequest) Serialization failed"); + safe_delete_array(in_buf); + safe_delete(in); + return; + } + + in->size = buf.size(); + in->pBuffer = buf.detach(); + + safe_delete_array(in_buf); + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_ItemVerifyReply) { ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); @@ -6857,12 +6884,13 @@ namespace RoF2 iqbs.Heirloom = 0; iqbs.Placeable = 0; iqbs.unknown28 = -1; + iqbs.unknown29 = packet_type == ItemPacketInvalid ? 0xFF : 0; iqbs.unknown30 = -1; iqbs.NoZone = 0; iqbs.NoGround = 0; iqbs.unknown37a = 0; // (guessed position) New to RoF2 iqbs.unknown38 = 0; - iqbs.unknown39 = 1; + iqbs.unknown39 = packet_type == ItemPacketInvalid ? 0 : 1;; ob.write((const char*)&iqbs, sizeof(RoF2::structs::ItemQuaternaryBodyStruct)); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 0330287e41..754635e416 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify @@ -92,6 +92,7 @@ E(OP_InspectBuffs) E(OP_InspectRequest) E(OP_ItemLinkResponse) E(OP_ItemPacket) +E(OP_ItemPreviewRequest) E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 3866cfe899..93d6f04637 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -261,6 +261,7 @@ OP_ItemPacket=0x368e OP_ItemLinkResponse=0x70c0 OP_ItemLinkClick=0x4cef OP_ItemPreview=0x6b5c +OP_ItemPreviewRequest=0x7f80 OP_NewSpawn=0x6097 OP_Track=0x17e5 OP_TrackTarget=0x695e diff --git a/utils/scripts/opcode_scripts/opcodelist.txt b/utils/scripts/opcode_scripts/opcodelist.txt index 8a4ebf87d6..6e324c40d3 100644 --- a/utils/scripts/opcode_scripts/opcodelist.txt +++ b/utils/scripts/opcode_scripts/opcodelist.txt @@ -1,317 +1,317 @@ -RoF2 Built May 10 2013 23:30:08 -0x04d6 -0x5efd -0x0cc1 -0x2c3d -0x7b25 -0x4f44 -0x7592 -0x2b1c +RoF2 Built May 10 2013 23:30:08 +0x04d6 +0x5efd +0x0cc1 +0x2c3d +0x7b25 +0x4f44 +0x7592 +0x2b1c 0x1fd1 OP_Consent -0x15a5 -0x3c7e -0x6262 +0x15a5 +0x3c7e +0x6262 0x471d OP_AckPacket -0x64a8 -0x7c06 -0x2c5e -0x24ab -0x19b5 -0x72fa -0x0770 +0x64a8 +0x7c06 +0x2c5e +0x24ab +0x19b5 +0x72fa +0x0770 0x4cef OP_ItemLinkClick 0x08c1 OP_GMServers 0x318f OP_BeginCast 0x5ec8 OP_Disarm -0x6ae3 +0x6ae3 0x2b2d OP_ChannelMessage -0x1cb1 +0x1cb1 0x5070 OP_TimeOfDay 0x7dfc OP_ClientUpdate -0x35ea -0x3b9a -0x148d +0x35ea +0x3b9a +0x148d 0x345d OP_ClientReady 0x5ae2 OP_WorldObjectsSent -0x03a4 +0x03a4 0x69e2 OP_TradeAcceptClick 0x354c OP_CancelTrade 0x3993 OP_FinishTrade -0x165b -0x2b95 -0x6fe2 -0x42fd -0x25d7 -0x7436 +0x165b +0x2b95 +0x6fe2 +0x42fd +0x25d7 +0x7436 0x4206 OP_TradeCoins 0x14bf OP_TradeRequestAck 0x7349 OP_FinishWindow -0x71fd +0x71fd 0x0b9c OP_SetGuildRank -0x0f7d -0x1bd3 +0x0f7d +0x1bd3 0x5053 OP_GuildPublicNote -0x01f9 +0x01f9 0x2c84 OP_MobUpdate -0x279d -0x5a94 +0x279d +0x5a94 0x579a OP_TradeSkillCombine -0x56b1 -0x0f25 -0x3c14 -0x0c16 +0x56b1 +0x0f25 +0x3c14 +0x0c16 0x4a8f OP_GMFind -0x2ad6 -0x0160 -0x7852 -0x41bf -0x40dd -0x3ec6 -0x6af4 -0x6d9d -0x2538 -0x0017 -0x017 -0x691a -0x0520 -0x200f -0x65ab +0x2ad6 +0x0160 +0x7852 +0x41bf +0x40dd +0x3ec6 +0x6af4 +0x6d9d +0x2538 +0x0017 +0x017 +0x691a +0x0520 +0x200f +0x65ab 0x674b OP_WhoAllRequest -0x6a02 -0x0e48 +0x6a02 +0x0e48 0x12a6 OP_GuildMemberList -0x0eae -0x2921 -0x7056 -0x507a +0x0eae +0x2921 +0x7056 +0x507a 0x6279 OP_GuildList -0x5a26 -0x5b51 -0x0bad -0x59ad -0x2264 -0x004c +0x5a26 +0x5b51 +0x0bad +0x59ad +0x2264 +0x004c 0x04c OP_SkillUpdate 0x0ddd OP_ShopPlayerBuy -0x4101 -0x5f03 -0x3de3 +0x4101 +0x5f03 +0x3de3 0x1901 OP_Petition -0x791b -0x3a5d -0x7013 -0x47f1 +0x791b +0x3a5d +0x7013 +0x47f1 0x6506 OP_PlayerProfile 0x30a8 OP_ShopEnd 0x724f OP_ShopDelItem -0x5829 -0x1ce1 -0x47d6 -0x07cf -0x40c9 -0x38d4 -0x7eb9 -0x1e2c +0x5829 +0x1ce1 +0x47d6 +0x07cf +0x40c9 +0x38d4 +0x7eb9 +0x1e2c 0x3ae1 OP_GMBecomeNPC -0x4849 -0x376b +0x4849 +0x376b 0x1e2a OP_Fishing 0x1eec OP_LevelUpdate -0x4958 +0x4958 0x20ed OP_ExpUpdate -0x3ed4 +0x3ed4 0x701c OP_RezzAnswer 0x640e OP_InstillDoubt 0x0ecf OP_Mend -0x09d3 +0x09d3 0x70a3 OP_IncreaseStats -0x178e -0x27e8 -0x61f8 -0x4322 -0x3f84 +0x178e +0x27e8 +0x61f8 +0x4322 +0x3f84 0x40ef OP_FinishWindow2 -0x166c +0x166c 0x106b OP_RandomReply -0x5bfd -0x11c8 -0x1753 +0x5bfd +0x11c8 +0x1753 0x009f OP_SetRunMode -0x09f -0x4ae6 -0x3dbf -0x0399 -0x5c89 -0x012c -0x381c -0x4b36 -0x36d5 -0x5f21 -0x56f7 -0x46b1 +0x09f +0x4ae6 +0x3dbf +0x0399 +0x5c89 +0x012c +0x381c +0x4b36 +0x36d5 +0x5f21 +0x56f7 +0x46b1 0x357c OP_GMSearchCorpse -0x2422 -0x084e +0x2422 +0x084e 0x67e3 OP_GuildPeace -0x0f83 +0x0f83 0x4577 OP_TrackUnknown 0x5d55 OP_Sneak 0x67fe OP_Hide -0x12e5 -0x2c7a +0x12e5 +0x2c7a 0x600d OP_SaveOnZoneReq -0x16ce +0x16ce 0x46ce OP_GMLastName 0x1ffb OP_GuildWar -0x6718 -0x2f3e +0x6718 +0x2f3e 0x7e09 OP_GuildLeader -0x5ce4 -0x5bcc -0x551d -0x6dbc +0x5ce4 +0x5bcc +0x551d +0x6dbc 0x2219 OP_SafeFallSuccess -0x2686 -0x2022 -0x1c53 -0x33a2 -0x3541 -0x582e -0x3cba -0x190c -0x5067 -0x46d2 -0x49cf -0x1b0c - +0x2686 +0x2022 +0x1c53 +0x33a2 +0x3541 +0x582e +0x3cba +0x190c +0x5067 +0x46d2 +0x49cf +0x1b0c + 0x31e6 OP_ApplyPoison 0x4211 OP_BoardBoat 0x7617 OP_LeaveBoat -0x4466 +0x4466 0x1287 OP_CastSpell 0x5467 OP_ManaChange 0x43af OP_ColoredText -0x6a0d +0x6a0d 0x217c OP_MemorizeSpell 0x260a OP_SenseHeading -0x5886 -0x7e8c -0x1a37 -0x0b6f -0x62ca -0x34a4 -0x6c83 -0x5d10 -0x21a0 -0x3537 -0x2acd -0x7c6d -0x6618 -0x6c65 - - +0x5886 +0x7e8c +0x1a37 +0x0b6f +0x62ca +0x34a4 +0x6c83 +0x5d10 +0x21a0 +0x3537 +0x2acd +0x7c6d +0x6618 +0x6c65 + + 0x32c2 OP_GroupInvite2 0x2a50 OP_CancelInvite 0x2060 OP_GroupFollow2 0x31f4 OP_Jump -0x4420 -0x1256 -0x4d38 -0x50f5 - +0x4420 +0x1256 +0x4d38 +0x50f5 + 0x79c5 OP_GMHideMe -0x2fab -0x57a5 -0x32a4 +0x2fab +0x57a5 +0x32a4 0x590d OP_ExpansionInfo 0x6f15 OP_Damage -0x42ea -0x3091 -0x6b5a -0x3234 -0x30b4 +0x42ea +0x3091 +0x6b5a +0x3234 +0x30b4 0x51fd OP_EnvDamage -0x15f4 +0x15f4 0x1808 OP_DeleteCharacter 0x1795 OP_NewZone 0x7887 OP_ReqNewZone -0x2414 -0x2eb3 -0x3db3 -0x4008 - +0x2414 +0x2eb3 +0x3db3 +0x4008 + 0x373b OP_Emote 0x7280 OP_DeleteSpawn -0x4bc2 -0x15bb +0x4bc2 +0x15bb 0x4fed OP_ShopRequest -0x0c2f +0x0c2f 0x4aa1 OP_ClickObject 0x6fca OP_GroundSpawn -0x1045 -0x2a9e -0x2523 -0x6213 +0x1045 +0x2a9e +0x2523 +0x6213 0x4a39 OP_Save -0x0797 +0x0797 0x35fa OP_ReqClientSpawn 0x5f8e OP_SendExpZonein 0x4c10 OP_GroupDisband 0x747c OP_SomeItemPacketMaybe 0x744c OP_Action 0x00d2 OP_SendCharInfo -0x0d2 -0x11d4 +0x0d2 +0x11d4 0x6bbf OP_CharacterCreate 0x6517 OP_Death -0x19f6 -0x360f -0x1951 +0x19f6 +0x360f +0x1951 0x51d3 OP_GMKill 0x26a7 OP_GMKick 0x7d8e OP_GMGoto -0x475e -0x5253 -0x3c79 -0x5065 -0x0337 -0x2ded -0x777b -0x4434 -0x0e90 -0x57da -0x150e -0x1ba8 -0x70eb +0x475e +0x5253 +0x3c79 +0x5065 +0x0337 +0x2ded +0x777b +0x4434 +0x0e90 +0x57da +0x150e +0x1ba8 +0x70eb 0x0adf OP_LootRequest 0x30f7 OP_EndLootRequest 0x5f44 OP_MoneyOnCorpse -0x3d54 -0x482d -0x58b2 -0x7361 -0x604f -0x3d87 -0x6909 -0x4f73 -0x0a0f +0x3d54 +0x482d +0x58b2 +0x7361 +0x604f +0x3d87 +0x6909 +0x4f73 +0x0a0f 0x56a2 OP_ApproveName -0x69e6 +0x69e6 0x3a8f OP_ClickDoor 0x08e8 OP_MoveDoor -0x7dd1 -0x5c66 +0x7dd1 +0x5c66 0x312a OP_Illusion -0x2807 -0x4fe2 -0x2f2e -0x5824 -0x6661 -0x4742 -0x4d02 -0x2105 -0x19e9 -0x2268 +0x2807 +0x4fe2 +0x2f2e +0x5824 +0x6661 +0x4742 +0x4d02 +0x2105 +0x19e9 +0x2268 0x7994 OP_WearChange 0x0386 OP_Bind_Wound 0x5306 OP_Forage @@ -320,210 +320,210 @@ RoF2 Built May 10 2013 23:30:08 0x7053 OP_GuildInviteAccept 0x1444 OP_GuildRemove 0x3708 OP_GuildDelete -0x7b22 -0x610f -0x324a -0x2889 -0x6f2a -0x2998 +0x7b22 +0x610f +0x324a +0x2889 +0x6f2a +0x2998 0x700c OP_RemoveAllDoors 0x1966 OP_GMTraining 0x4d6b OP_GMEndTraining -0x6e8d -0x2b41 +0x6e8d +0x2b41 0x4dc9 OP_LootItem 0x7177 OP_Animation -0x650e +0x650e 0x2d18 OP_ZoneChange -0x707b -0x0803 -0x3fc3 +0x707b +0x0803 +0x3fc3 0x7326 OP_GuildStatus -0x0804 -0x3f86 -0x6d20 -0x04a3 -0x4714 +0x0804 +0x3f86 +0x6d20 +0x04a3 +0x4714 0x6703 OP_Begging 0x0ae7 OP_ControlBoat -0x27d7 +0x27d7 0x3c21 OP_RezzRequest -0x6f22 +0x6f22 0x32ee OP_MoveItem 0x0bcf OP_MoveCoin -0x2452 -0x0186 -0x0522 +0x2452 +0x0186 +0x0522 0x3a54 OP_Split 0x659c OP_Buff -0x225b - +0x225b + 0x12cc OP_World_Client_CRC1 0x661e OP_Weather 0x742b OP_Consider -0x102e +0x102e 0x0f13 OP_World_Client_CRC2 -0x6944 +0x6944 0x2703 OP_Taunt 0x5602 OP_Feedback 0x68c2 OP_TradeMoneyUpdate -0x07f7 -0x3fe7 -0x43a0 -0x314c +0x07f7 +0x3fe7 +0x43a0 +0x314c 0x4b70 OP_Consume 0x2a79 OP_Stamina -0x402e -0x750e -0x0927 +0x402e +0x750e +0x0927 0x36a4 OP_Stun -0x2b03 +0x2b03 0x68d3 OP_DuelResponse2 0x6a46 OP_DuelResponse 0x5237 OP_ZoneSpawns 0x3eba OP_CombatAbility 0x109d OP_AutoAttack 0x075d OP_TargetMouse -0x05d2 +0x05d2 0x2a85 OP_GMTrainSkill 0x43a3 OP_ConfirmDelete -0x6c57 -0x3906 +0x6c57 +0x3906 0x55c4 OP_LootComplete 0x3196 OP_ShopEndConfirm 0x18ad OP_DeleteItem 0x01b8 OP_DeleteCharge -0x01d7 -0x4d28 -0x4fd9 -0x02c6 -0x3e30 +0x01d7 +0x4d28 +0x4fd9 +0x02c6 +0x3e30 0x3fcf OP_RequestClientZoneChange -0x4e0e -0x1dee +0x4e0e +0x1dee 0x62ac OP_GMZoneRequest 0x4ac6 OP_Logout 0x3526 OP_AutoAttack2 0x7ceb OP_LogServer 0x0423 OP_Surname 0x3956 OP_FriendsWho -0x45dc -0x35e0 -0x173e -0x4853 -0x393b +0x45dc +0x35e0 +0x173e +0x4853 +0x393b 0x0efa OP_SwapSpell -0x3588 -0x26e9 +0x3588 +0x26e9 0x4e56 OP_YellForHelp -0x2551 -0x38c8 +0x2551 +0x38c8 0x7499 OP_ApproveWorld -0x603a +0x603a 0x7b10 OP_RandomReq -0x208f +0x208f 0x607e OP_GMDelCorpse 0x1821 OP_Sacrifice -0x30d6 +0x30d6 0x760d OP_RezzComplete -0x4dd5 -0x0fe8 -0x6e5c -0x2228 -0x21bc -0x78c3 -0x3e57 -0x69d0 -0x1e3a +0x4dd5 +0x0fe8 +0x6e5c +0x2228 +0x21bc +0x78c3 +0x3e57 +0x69d0 +0x1e3a 0x1a30 OP_Sound -0x61a0 +0x61a0 0x048c OP_InterruptCast -0x6e80 -0x3d29 +0x6e80 +0x3d29 0x5d92 OP_Charm 0x0159 OP_PetCommands -0x486e -0x579e -0x49b3 +0x486e +0x579e +0x49b3 0x6db5 OP_GMApproval -0x4759 +0x4759 0x0c22 OP_MOTD 0x2097 OP_GMToggle -0x72f7 +0x72f7 0x640c OP_MoneyUpdate -0x51e5 -0x1802 -0x5de1 -0x2e0c +0x51e5 +0x1802 +0x5de1 +0x2e0c 0x58e2 OP_TargetCommand 0x444d OP_SetServerFilter 0x4478 OP_Assist -0x2a21 -0x7e59 +0x2a21 +0x7e59 0x0b0b OP_SetGuildMOTD 0x3e13 OP_GuildMOTD 0x7a11 OP_ClearObject 0x6580 OP_Translocate 0x28ec OP_Camp 0x61b3 OP_BecomeTrader -0x31af +0x31af 0x0876 OP_TGB -0x4ee1 -0x7e92 +0x4ee1 +0x7e92 0x7d14 OP_AAExpUpdate - + 0x5578 OP_FaceChange -0x1a80 -0x42ff -0x7c2d +0x1a80 +0x42ff +0x7c2d 0x2c57 OP_MobRename -0x6dab -0x4568 -0x7776 -0x5029 -0x696c +0x6dab +0x4568 +0x7776 +0x5029 +0x696c 0x23b9 OP_Dye 0x5204 OP_ConsiderCorpse 0x213f OP_SimpleMessage 0x1024 OP_FormattedMessage -0x2ce5 -0x5cc9 -0x3358 +0x2ce5 +0x5cc9 +0x3358 0x52e5 OP_DeleteSpell 0x48c1 OP_Shielding -0x6f14 +0x6f14 0x1414 OP_Report -0x0e1c -0x1219 +0x0e1c +0x1219 0x6857 OP_KeyRing 0x55ac OP_RaidInvite 0x3973 OP_RaidUpdate -0x56fe -0x7f2d +0x56fe +0x7f2d 0x31df OP_TraderShop 0x4ef5 OP_Trader -0x735d -0x019b -0x5930 -0x2d73 +0x735d +0x019b +0x5930 +0x2d73 0x39d6 OP_BazaarSearch -0x6a96 +0x6a96 0x424e OP_AAAction 0x7a27 OP_RespondAA -0x5eca -0x70c6 -0x0dd4 -0x4598 -0x19b6 -0x728a -0x5232 -0x77bd - +0x5eca +0x70c6 +0x0dd4 +0x4598 +0x19b6 +0x728a +0x5232 +0x77bd + 0x70c0 OP_ItemLinkResponse -0x633c -0x63eb -0x4765 -0x6290 +0x633c +0x63eb +0x4765 +0x6290 0x1db6 OP_RecipesSearch 0x6e02 OP_RecipeReply 0x40d7 OP_RecipeDetails @@ -546,248 +546,248 @@ RoF2 Built May 10 2013 23:30:08 0x0272 OP_TargetHoTT 0x2003 OP_ClearNPCMarks 0x20d3 OP_ClearRaidNPCMarks -0x1c89 -0x4ee2 +0x1c89 +0x4ee2 0x2797 OP_LeadershipExpUpdate 0x6da5 OP_ClearLeadershipAbilities -0x0ca6 -0x7717 -0x509d -0x6bee -0x0b04 +0x0ca6 +0x7717 +0x509d +0x6bee +0x0b04 0x6097 OP_NewSpawn -0x758c -0x2b10 -0x4d09 +0x758c +0x2b10 +0x4d09 0x0083 OP_SpecialMesg 0x3714 OP_TaskDescription 0x08d3 OP_TaskActivity 0x39f0 OP_CancelTask -0x5f7a +0x5f7a 0x006a OP_ItemScriptAdjustment -0x7465 -0x31c0 -0x14ba -0x25e0 -0x62a0 -0x407a -0x7c88 -0x51b8 +0x7465 +0x31c0 +0x14ba +0x25e0 +0x62a0 +0x407a +0x7c88 +0x51b8 0x578c OP_WhoAllResponse -0x1197 -0x2dd3 -0x333a -0x2925 +0x1197 +0x2dd3 +0x333a +0x2925 0x37b1 OP_MobHealth -0x7314 +0x7314 0x2404 OP_MobManaUpdate -0x5f5e +0x5f5e 0x1c81 OP_MobEnduranceUpdate -0x4a78 -0x37a2 -0x5565 -0x2d09 -0x3141 -0x695e +0x4a78 +0x37a2 +0x5565 +0x2d09 +0x3141 +0x695e 0x0029 OP_TrackTarget -0x029 -0x1612 -0x3e69 -0x5c59 +0x029 +0x1612 +0x3e69 +0x5c59 0x7e1a OP_GMZoneRequest2 -0x24d9 +0x24d9 0x5089 OP_ZoneEntry -0x61db -0x542f -0x2006 +0x61db +0x542f +0x2006 0x52fa OP_FeignDeath 0x39e8 OP_PickPocket -0x7c2e -0x2cdd -0x5e3a -0x4214 +0x7c2e +0x2cdd +0x5e3a +0x4214 0x2828 OP_HPUpdate -0x14b8 +0x14b8 0x3791 OP_ManaUpdate -0x0698 +0x0698 0x5f42 OP_EnduranceUpdate -0x7eb5 +0x7eb5 0x73f4 OP_Bug 0x69a4 OP_SendZonepoints 0x57bc OP_InspectRequest 0x71ac OP_InspectAnswer 0x4229 OP_GroupMakeLeader -0x54f7 -0x322b -0x3264 -0x202c -0x29ae +0x54f7 +0x322b +0x3264 +0x202c +0x29ae 0x7a09 OP_SendLoginInfo -0x5206 -0x7fd2 +0x5206 +0x7fd2 0x6259 OP_PostEnterWorld -0x0f70 -0x00d1 -0x0d1 -0x1eac -0x1b54 -0x5e23 -0x7910 -0x152e -0x7555 -0x05ce +0x0f70 +0x00d1 +0x0d1 +0x1eac +0x1b54 +0x5e23 +0x7910 +0x152e +0x7555 +0x05ce 0x1649 OP_GroupFollow 0x6110 OP_GroupInvite -0x0641 +0x0641 0x578f OP_EnterWorld -0x6cb8 -0x6ef5 +0x6cb8 +0x6ef5 0x4c44 OP_ZoneServerInfo 0x4cb4 OP_ZoneUnavail -0x74fb -0xbe6b -0x5ac7 -0x6281 +0x74fb +0xbe6b +0x5ac7 +0x6281 0x1bc5 OP_SetChatServer -0x48c8 -0x677b -0x6820 -0x2623 +0x48c8 +0x677b +0x6820 +0x2623 0x69b9 OP_GuildMemberUpdate -0x2dcb -0x5e04 -0x1d3c +0x2dcb +0x5e04 +0x1d3c 0x6060 OP_LFGCommand 0x0340 OP_LFGGetMatchesRequest 0x49a9 OP_LFPCommand 0x4d7d OP_LFPGetMatchesRequest 0x5048 OP_LFGGetMatchesResponse 0x22c6 OP_LFPGetMatchesResponse -0x499e +0x499e 0x2d4e OP_GuildDemote -0x2009 -0x3b26 -0x69d4 -0x40b1 -0x587d -0x3747 -0x7a66 -0x2f03 -0x01ed +0x2009 +0x3b26 +0x69d4 +0x40b1 +0x587d +0x3747 +0x7a66 +0x2f03 +0x01ed 0x36e0 OP_GetGuildMOTD 0x4f1f OP_GetGuildMOTDReply -0x6087 -0x5f92 -0x439a -0x4334 -0x13c2 +0x6087 +0x5f92 +0x439a +0x4334 +0x13c2 0x7cb8 OP_TestBuff 0x17e5 OP_Track -0x5265 -0x1440 -0x7a4b -0x0648 -0x2529 +0x5265 +0x1440 +0x7a4b +0x0648 +0x2529 0x486f OP_GMSummon 0x458e OP_GMEmoteWorld -0x2549 +0x2549 0x1cfd OP_GMEmoteZone 0x5ca6 OP_CharInventory -0x2710 +0x2710 0x7291 OP_SpawnDoor -0x7afc -0x1f65 -0x3ddd -0x85fc +0x7afc +0x1f65 +0x3ddd +0x85fc 0x72df OP_ReadBook 0x3af1 OP_RequestDuel -0x52f7 +0x52f7 0x77b5 OP_TradeRequest -0x70f2 -0x207d +0x70f2 +0x207d 0x384a OP_ConsentResponse 0x5505 OP_TradeBusy 0x0c1e OP_ClickObjectAction -0x51eb -0x466b -0x41a3 -0x0682 -0x1242 +0x51eb +0x466b +0x41a3 +0x0682 +0x1242 0x261d OP_LoadSpellSet -0x32a6 +0x32a6 0x2c6c OP_AdventureRequest -0x39e5 +0x39e5 0x5648 OP_AdventureDetails 0x5327 OP_LDoNButton -0x4524 +0x4524 0x5954 OP_RandomNameGenerator -0x327d -0x2945 -0x33d7 -0x2651 -0x3b86 -0x0c6c -0x24d4 -0x4441 -0x4e42 -0x50d8 -0x1afb -0x6bd4 +0x327d +0x2945 +0x33d7 +0x2651 +0x3b86 +0x0c6c +0x24d4 +0x4441 +0x4e42 +0x50d8 +0x1afb +0x6bd4 0x3cb0 OP_AdventureInfoRequest 0x4c54 OP_AdventureInfo 0x7171 OP_AdventureData -0x35e8 -0x7d49 -0x1848 -0x2599 -0x0dab -0x6493 -0x34f2 -0x7d40 -0x0e00 -0x1cf3 -0x71f4 +0x35e8 +0x7d49 +0x1848 +0x2599 +0x0dab +0x6493 +0x34f2 +0x7d40 +0x0e00 +0x1cf3 +0x71f4 0x5d18 OP_LeaveAdventure -0x4e25 -0x50c2 +0x4e25 +0x50c2 0x400f OP_AdventureFinish -0x6368 -0x6192 -0x3aa4 +0x6368 +0x6192 +0x3aa4 0x502e OP_Marquee 0x1b01 OP_AdventureUpdate -0x0f24 -0x1015 -0x23a6 -0x5a74 -0x037a +0x0f24 +0x1015 +0x23a6 +0x5a74 +0x037a 0x7a45 OP_ConsentDeny -0x71bc +0x71bc 0x2382 OP_DenyResponse -0x02a0 +0x02a0 0x1126 OP_VetClaimRequest -0x0668 +0x0668 0x16d4 OP_VetClaimReply 0x5cea OP_FindPersonRequest -0x0cc3 +0x0cc3 0x7e58 OP_FindPersonReply 0x9be3 OP_PickLock -0x0438 +0x0438 0x3d5c OP_LDoNOpen -0x15e7 +0x15e7 0x368e OP_ItemPacket -0x7331 -0x4936 -0x2c4e +0x7331 +0x4936 +0x2c4e 0x78bf OP_DisarmTraps 0x02af OP_SenseTraps 0x65c3 OP_AdventurePointsUpdate -0x0dcb +0x0dcb 0x661b OP_AugmentItem -0x5f68 -0x5278 -0x6b53 -0x5dd1 +0x5f68 +0x5278 +0x6b53 +0x5dd1 0x559a OP_WeaponEquip2 0x2d25 OP_WeaponUnequip2 0x23c1 OP_WorldClientReady @@ -795,7 +795,7 @@ RoF2 Built May 10 2013 23:30:08 0x7093 OP_AdventureLeaderboardRequest 0x2370 OP_AdventureStatsReply 0x7f79 OP_AdventureLeaderboardReply -0x4eb3 +0x4eb3 0x3377 OP_BuffCreate 0x5882 OP_PetBuffWindow 0x4f4b OP_TargetBuffs @@ -806,31 +806,31 @@ RoF2 Built May 10 2013 23:30:08 0x4254 OP_TributeInfo 0x79fc OP_SelectTribute 0x073d OP_TributeTimer -0x08bf -0x7697 -0x4b65 -0x51ae -0x51a9 +0x08bf +0x7697 +0x4b65 +0x51ae +0x51a9 0x7666 OP_OpenTributeMaster -0x4290 +0x4290 0x759e OP_DisciplineUpdate -0x01c6 +0x01c6 0x6989 OP_DisciplineTimer 0x58fb OP_TributeMoney -0x7422 -0x0d25 -0x4de1 -0x46d0 -0x4f50 -0x2ddc -0x7441 -0x6a12 -0x29a8 -0x3f7f -0x150b -0x11e3 -0x1ad3 - +0x7422 +0x0d25 +0x4de1 +0x46d0 +0x4f50 +0x2ddc +0x7441 +0x6a12 +0x29a8 +0x3f7f +0x150b +0x11e3 +0x1ad3 + 0x59ca OP_DzAddPlayer 0x4701 OP_DzRemovePlayer 0x1abc OP_DzSwapPlayer @@ -838,96 +838,96 @@ RoF2 Built May 10 2013 23:30:08 0x543d OP_DzPlayerList 0x14c6 OP_DzJoinExpeditionConfirm 0x7f4b OP_DzJoinExpeditionReply -0x1950 -0x64b5 +0x1950 +0x64b5 0x0398 OP_DzListTimers -0x7b68 +0x7b68 0x4f7e OP_DzExpeditionInfo 0x9119 OP_DzExpeditionList 0x205f OP_DzQuit 0xb2e3 OP_DzMemberStatus 0x32f0 OP_DzLeaderStatus 0x3de9 OP_DzMemberList -0x5ae4 +0x5ae4 0x4d6e OP_OnLevelMessage -0x4fd0 -0x575b +0x4fd0 +0x575b 0x7e94 OP_DzExpeditionEndsWarning -0x5189 -0x383c +0x5189 +0x383c 0x791e OP_BankerChange -0x5c74 +0x5c74 0x71b1 OP_RecipesFavorite -0x20ab -0x025f -0x214a +0x20ab +0x025f +0x214a 0x08a6 OP_PopupResponse 0x15a9 OP_ItemRecastDelay 0x3707 OP_PVPLeaderBoardDetailsRequest 0x04aa OP_PVPLeaderBoardRequest 0x25b7 OP_PVPLeaderBoardDetailsReply 0x071f OP_PVPLeaderBoardReply -0x2dee -0x4e62 -0x0c91 -0x18d3 +0x2dee +0x4e62 +0x0c91 +0x18d3 0x6f4b OP_Weblink 0x4b15 OP_PVPStats -0x6755 -0x5c32 +0x6755 +0x5c32 0x5770 OP_PlayMP3 -0x7425 -0x5eed -0x574e -0x11b4 -0x4ed6 -0x0d9f -0x7d23 +0x7425 +0x5eed +0x574e +0x11b4 +0x4ed6 +0x0d9f +0x7d23 0x3fb0 OP_ClearSurname -0xc693 +0xc693 0x7b1e OP_RemoveNimbusEffect -0x20ae -0x0727 -0x3771 -0x7fe0 -0x4d5e +0x20ae +0x0727 +0x3771 +0x7fe0 +0x4d5e 0x1877 OP_SendGuildTributes -0x649f -0x5f3f -0x5bcb -0x43d2 -0x49ea +0x649f +0x5f3f +0x5bcb +0x43d2 +0x49ea 0x378d OP_OpenGuildTributeMaster -0x7f8e -0x02bc -0x7dd2 -0x7c1d -0x7a41 -0x7db5 +0x7f8e +0x02bc +0x7dd2 +0x7c1d +0x7a41 +0x7db5 0x7eec OP_SetChatServer2 0x0904 OP_CorpseDrag 0x7037 OP_CorpseDrop 0x5e19 OP_TaskActivityComplete -0x4e32 +0x4e32 0x241d OP_TributeToggle -0x756a -0x7745 -0x039d +0x756a +0x7745 +0x039d 0x0f50 OP_ClearAA 0x66b5 OP_SendAATable 0x0afb OP_AugmentInfo -0x10f6 -0x1013 - +0x10f6 +0x1013 + 0x6344 OP_RequestTitles 0x2d08 OP_SendTitleList -0x3719 -0x7a48 +0x3719 +0x7a48 0x0a23 OP_AcceptNewTask -0x705b +0x705b 0x3bc9 OP_LevelAppearance -0x60ef -0x1619 +0x60ef +0x1619 0x17fd OP_VoiceMacroIn 0x409a OP_VoiceMacroOut 0x4493 OP_WorldComplete @@ -936,437 +936,437 @@ RoF2 Built May 10 2013 23:30:08 0x127f OP_CameraEffect 0x0d32 OP_NewTitlesAvailable 0x34a7 OP_WeaponEquip1 -0x34cd +0x34cd 0x5936 OP_SpellEffect 0x241e OP_AutoFire 0x66f0 OP_UpdateAA 0x100e OP_CustomTitles -0x6b65 -0x12f5 +0x6b65 +0x12f5 0x7677 OP_Bandolier -0x688f -0x7adf -0x0ed4 +0x688f +0x7adf +0x0ed4 0x243a OP_Barter -0x1a6a -0x5623 +0x1a6a +0x5623 0x43c8 OP_SendAAStats -0x655e +0x655e 0x1a3e OP_PotionBelt 0x6326 OP_SetStartCity -0x7485 -0x5416 -0x3282 -0x3752 -0x425b -0x27c8 -0x2b19 -0x70ce -0x3165 -0x786b -0x0f26 -0x3500 -0x3d04 +0x7485 +0x5416 +0x3282 +0x3752 +0x425b +0x27c8 +0x2b19 +0x70ce +0x3165 +0x786b +0x0f26 +0x3500 +0x3d04 0x5134 OP_GuildBank -0x0521 -0x7850 -0x108b -0x5671 -0x6d2b -0x732f +0x0521 +0x7850 +0x108b +0x5671 +0x6d2b +0x732f 0x748f OP_GuildManageBanker -0x6858 -0x5e74 -0x3f35 -0x35e9 -0x2056 +0x6858 +0x5e74 +0x3f35 +0x35e9 +0x2056 0x6922 OP_AdventureMerchantRequest 0x5b72 OP_AdventureMerchantPurchase 0x2f9b OP_AdventureMerchantSell 0x3e47 OP_AdventureMerchantResponse 0x0b7d OP_DzChooseZone -0x2818 -0x35bd -0x51df -0x1ff7 -0x3926 -0x6265 -0x4ab0 -0x5e6c -0x1350 -0x6288 -0x7348 +0x2818 +0x35bd +0x51df +0x1ff7 +0x3926 +0x6265 +0x4ab0 +0x5e6c +0x1350 +0x6288 +0x7348 0x48a2 OP_OpenNewTasksWindow -0x3010 -0x45db +0x3010 +0x45db 0x36e8 OP_AvaliableTask -0x4865 -0x322e -0x7582 +0x4865 +0x322e +0x7582 0x5727 OP_TaskMemberList -0x6646 -0x37f2 -0x3444 -0x5ffc -0x5cb5 -0x0119 -0x35b5 -0x6cc6 -0x4926 -0x1b1d -0x7299 -0x0bf1 -0x08b4 -0x7f7a -0x3dab -0x1e7d -0x610a -0x04c8 -0x4811 -0x609e -0x65f0 +0x6646 +0x37f2 +0x3444 +0x5ffc +0x5cb5 +0x0119 +0x35b5 +0x6cc6 +0x4926 +0x1b1d +0x7299 +0x0bf1 +0x08b4 +0x7f7a +0x3dab +0x1e7d +0x610a +0x04c8 +0x4811 +0x609e +0x65f0 0x467f OP_CrystalCountUpdate 0x7aee OP_CrystalCreate 0x2439 OP_CrystalReclaim -0x39c1 -0x555e -0x7c9c -0x2c94 -0x2fc2 -0x2067 -0x059e -0x7f74 -0x68b2 -0x6f2b -0x01d6 -0x5182 -0x1da2 -0x5147 -0x51f8 -0x11f3 -0x0d07 -0x272f -0x413f -0x5968 +0x39c1 +0x555e +0x7c9c +0x2c94 +0x2fc2 +0x2067 +0x059e +0x7f74 +0x68b2 +0x6f2b +0x01d6 +0x5182 +0x1da2 +0x5147 +0x51f8 +0x11f3 +0x0d07 +0x272f +0x413f +0x5968 0x3e0e OP_DzCompass -0x282a +0x282a 0x035f OP_GMNameChange -0x4013 -0x0e2f -0x099e +0x4013 +0x0e2f +0x099e 0x4eba OP_CompletedTasks 0x5f1c OP_TaskHistoryRequest 0x3d05 OP_TaskHistoryReply -0x2b8e -0x66d6 -0x0143 -0x6a5d -0x6e61 -0x2b0f +0x2b8e +0x66d6 +0x0143 +0x6a5d +0x6e61 +0x2b0f 0x46c6 OP_FloatListThing -0x3a8d -0x15e5 -0x7d89 -0x4085 -0x1507 -0x5d93 +0x3a8d +0x15e5 +0x7d89 +0x4085 +0x1507 +0x5d93 0x1669 OP_ResetAA -0x4664 -0x312d -0x2215 +0x4664 +0x312d +0x2215 0x1745 OP_Rewind -0x0cf1 -0x6567 -0x4405 -0x72d8 -0xaaba +0x0cf1 +0x6567 +0x4405 +0x72d8 +0xaaba 0x27f8 OP_AssistGroup -0x7cfb -0x1a32 -0x14fd -0x77bb -0x36d1 -0x6193 -0x184c -0x3c47 -0x4e0b -0x7c15 +0x7cfb +0x1a32 +0x14fd +0x77bb +0x36d1 +0x6193 +0x184c +0x3c47 +0x4e0b +0x7c15 0x2ec3 OP_ShroudSelectionWindow 0x2b37 OP_ShroudRequestStats 0x6c04 OP_ShroudRespondStats 0x1cd0 OP_ShroudSelect 0x79da OP_ShroudSelectCancel -0x7806 +0x7806 0x541d OP_ShroudProgress 0x3823 OP_ShroudProgress2 0x6562 OP_Shroud -0x4b25 -0x2507 -0x3acc -0x006e -0x06e -0x0728 -0x66cd -0x3c54 -0x2e8d -0x4eea -0x5a67 -0x3d20 -0x649c -0x21a6 +0x4b25 +0x2507 +0x3acc +0x006e +0x06e +0x0728 +0x66cd +0x3c54 +0x2e8d +0x4eea +0x5a67 +0x3d20 +0x649c +0x21a6 0x28bc OP_RespawnWindow 0x0ecb OP_ZonePlayerToBind -0x08d8 -0x7b71 -0x1115 -0x18cb -0x34f4 -0x5f08 -0x5bd2 -0x5052 -0x5e69 -0x2ccc -0x655c +0x08d8 +0x7b71 +0x1115 +0x18cb +0x34f4 +0x5f08 +0x5bd2 +0x5052 +0x5e69 +0x2ccc +0x655c 0x6773 OP_CharacterCreateRequest 0x590e OP_VetRewardsAvaliable -0x6b0b -0x555a -0x7786 +0x6b0b +0x555a +0x7786 0x67e8 OP_ShroudRemove -0x6381 -0x5628 -0xb52f -0x6084 +0x6381 +0x5628 +0xb52f +0x6084 0x2958 OP_GuildUpdateURLAndChannel 0x2301 OP_WorldUnknown001 -0x610b -0x6f8b +0x610b +0x6f8b 0x4d25 OP_InspectMessageUpdate 0x3033 OP_BlockedBuffs 0x0de7 OP_RemoveBlockedBuffs -0x30e5 -0x618c -0x58e6 +0x30e5 +0x618c +0x58e6 0x1456 OP_ShroudUnknown1 0x053c OP_Untargetable -0x71da -0x333f -0x49bc +0x71da +0x333f +0x49bc 0x64f2 OP_BuffRemoveRequest -0x6b3d +0x6b3d 0x34cb OP_ClearBlockedBuffs -0x5646 -0x7d13 -0x15e0 -0x5710 -0x172b -0x1888 -0x2956 -0x6a68 -0x7631 -0x298e -0x003c -0x03c -0x0b0f -0x5a72 -0x0767 -0x2f1a -0x6c7a -0x1660 -0x344f -0x028b -0x6eea -0x7707 -0x3fb2 -0x289e +0x5646 +0x7d13 +0x15e0 +0x5710 +0x172b +0x1888 +0x2956 +0x6a68 +0x7631 +0x298e +0x003c +0x03c +0x0b0f +0x5a72 +0x0767 +0x2f1a +0x6c7a +0x1660 +0x344f +0x028b +0x6eea +0x7707 +0x3fb2 +0x289e 0x3342 OP_GroupMentor 0x5892 OP_NPCMoveUpdate 0x189c OP_ItemVerifyRequest 0x097b OP_ItemVerifyReply -0x2115 -0x6411 -0x6471 -0x134a -0x1304 +0x2115 +0x6411 +0x6471 +0x134a +0x1304 0x5a79 OP_ShieldGroup -0x2dde -0x7d50 -0x1d47 -0x10ec +0x2dde +0x7d50 +0x1d47 +0x10ec 0x000f OP_RestState -0x00f -0x0f +0x00f +0x0f 0x465b OP_ItemViewUnknown -0x2289 -0x023b -0x4223 -0x7261 -0x2af9 -0x19aa -0x66dd +0x2289 +0x023b +0x4223 +0x7261 +0x2af9 +0x19aa +0x66dd 0x4b64 OP_GMTrainSkillConfirm -0x319e -0x1af3 -0x449c -0x8582 +0x319e +0x1af3 +0x449c +0x8582 0x4b8d #OP_LoginUnknown1 0x298d #OP_LoginUnknown2 -0x06c8 -0x4f93 -0x412d -0x001f -0x01f -0x60f6 -0x1a9e -0x798e -0x17b7 -0x3042 -0x61bd -0x1f6e -0x65a6 -0x740d -0x48da -0x20d9 -0x5258 -0x1b5d -0x49f4 -0x7aa9 -0xb350 -0x6e2a -0x5d4e -0x6e4d -0x4ffc -0x1d15 -0x6f23 -0x2296 -0x765b -0x2e01 -0x26dd -0x72d3 -0x6981 -0x3b30 -0x14ac -0x0d92 -0x0001 -0x001 -0x01 -0x09bb -0x9e18 -0x0d9d -0x7f2b -0x3651 -0x645d -0x3af2 -0x4377 -0x39c9 -0x4924 -0x1e50 -0x4683 -0x0276 -0x6dec -0x56c9 -0x3ee6 -0x7121 -0x62ab -0x5d88 -0x05f0 +0x06c8 +0x4f93 +0x412d +0x001f +0x01f +0x60f6 +0x1a9e +0x798e +0x17b7 +0x3042 +0x61bd +0x1f6e +0x65a6 +0x740d +0x48da +0x20d9 +0x5258 +0x1b5d +0x49f4 +0x7aa9 +0xb350 +0x6e2a +0x5d4e +0x6e4d +0x4ffc +0x1d15 +0x6f23 +0x2296 +0x765b +0x2e01 +0x26dd +0x72d3 +0x6981 +0x3b30 +0x14ac +0x0d92 +0x0001 +0x001 +0x01 +0x09bb +0x9e18 +0x0d9d +0x7f2b +0x3651 +0x645d +0x3af2 +0x4377 +0x39c9 +0x4924 +0x1e50 +0x4683 +0x0276 +0x6dec +0x56c9 +0x3ee6 +0x7121 +0x62ab +0x5d88 +0x05f0 0x6b6d OP_AltCurrency -0x61cb -0x0165 +0x61cb +0x0165 0x74ec OP_MercenaryAssign 0x5409 OP_AltCurrencyMerchantRequest 0x3788 OP_AltCurrencyPurchase 0x40b6 OP_AltCurrencySell 0x27a2 OP_AltCurrencyMerchantReply 0x532a OP_AltCurrencySellSelection -0x3899 -0x7567 -0x4820 +0x3899 +0x7567 +0x4820 0x0339 OP_AltCurrencyReclaim -0x0c12 -0x074d -0x47ba -0x55c8 -0x4c89 -0x18b7 -0x2950 -0x44cb -0x4477 -0x6146 -0x40cf -0x2405 -0x49e2 -0x2aff -0x0bfa -0x26c0 -0x7d39 -0x259f -0x086b -0x6dc1 -0x6f80 -0x042e -0x2ad4 -0x317b -0x73ac -0x18d7 -0x5b8b -0x5c83 -0x1f51 -0x62f7 -0x1c46 -0x2a44 -0x3e7a -0x4374 -0x3b23 -0x1df4 -0x3ed8 -0x4b50 -0x36eb -0x65c7 -0x5620 -0x587e -0x3897 -0x7e62 -0x67ae -0x74f4 +0x0c12 +0x074d +0x47ba +0x55c8 +0x4c89 +0x18b7 +0x2950 +0x44cb +0x4477 +0x6146 +0x40cf +0x2405 +0x49e2 +0x2aff +0x0bfa +0x26c0 +0x7d39 +0x259f +0x086b +0x6dc1 +0x6f80 +0x042e +0x2ad4 +0x317b +0x73ac +0x18d7 +0x5b8b +0x5c83 +0x1f51 +0x62f7 +0x1c46 +0x2a44 +0x3e7a +0x4374 +0x3b23 +0x1df4 +0x3ed8 +0x4b50 +0x36eb +0x65c7 +0x5620 +0x587e +0x3897 +0x7e62 +0x67ae +0x74f4 0x4613 OP_SendFindableNPCs -0x2a92 -0x6d6e -0x2c01 -0x1243 -0x133e -0x67fc - +0x2a92 +0x6d6e +0x2c01 +0x1243 +0x133e +0x67fc + 0x49e1 OP_HideCorpse -0x0e44 -0x239a -0x1a0a -0x398f -0x1ff4 -0x88a1 +0x0e44 +0x239a +0x1a0a +0x398f +0x1ff4 +0x88a1 0x74da OP_GroupDisbandOther 0x21b4 OP_GroupLeaderChange -0x62b7 -0x4ced +0x62b7 +0x4ced 0x3abb OP_GroupUpdate 0x0f6c OP_GroupDelete -0x0cbc +0x0cbc 0x6194 OP_GroupUpdateB -0x7fe6 -0x04d0 +0x7fe6 +0x04d0 0x7323 OP_GroupAcknowledge 0x1ae5 OP_GroupDisbandYou -0x4d9f +0x4d9f 0x70e2 OP_GroupRoles -0x6875 -0x6298 +0x6875 +0x6298 0x02cf OP_GroupLeadershipAAUpdate 0x22b8 OP_MercenarySuspend2 -0x7bff -0x5512 -0x2b57 -0x1380 -0x2bcb +0x7bff +0x5512 +0x2b57 +0x1380 +0x2bcb 0x7b89 OP_MercenaryDataUpdateRequest 0x61a4 OP_MercenaryDataUpdate 0x11c1 OP_MercenaryDataRequest @@ -1375,297 +1375,297 @@ RoF2 Built May 10 2013 23:30:08 0x6e83 OP_MercenaryDismiss 0x31e4 OP_MercenaryTimerRequest 0x0763 OP_MercenaryTimer -0x78f6 -0x1b37 +0x78f6 +0x1b37 0x27f2 OP_MercenaryCommand -0x5df8 -0x4333 -0x69ca -0x6e9f +0x5df8 +0x4333 +0x69ca +0x6e9f 0x4407 OP_MercenarySuspendRequest 0x6f03 OP_MercenarySuspendResponse 0x27a0 OP_MercenaryUnsuspendResponse -0x5f88 -0x2749 -0x038f -0x0e52 -0x20b9 +0x5f88 +0x2749 +0x038f +0x0e52 +0x20b9 0x5d26 OP_MercenaryUnknown1 -0x69e7 -0x66b9 -0x0b72 -0x7e6f -0x29ec -0x6248 -0x702b -0x2b4f -0x6e6d -0x1e9f -0x5bd5 -0x4b7f -0x1490 -0x075e -0x4263 -0x1eba -0x4a74 -0x0a37 -0x3289 -0x3171 -0x0114 -0x5148 -0x76c3 -0x09bf -0x356f -0x77a7 -0x479a -0x209f -0x54e6 -0x6c3e -0xee80 -0x40e5 +0x69e7 +0x66b9 +0x0b72 +0x7e6f +0x29ec +0x6248 +0x702b +0x2b4f +0x6e6d +0x1e9f +0x5bd5 +0x4b7f +0x1490 +0x075e +0x4263 +0x1eba +0x4a74 +0x0a37 +0x3289 +0x3171 +0x0114 +0x5148 +0x76c3 +0x09bf +0x356f +0x77a7 +0x479a +0x209f +0x54e6 +0x6c3e +0xee80 +0x40e5 0x76d9 OP_GuildCreate -0x1dc8 -0x794a -0x35c5 -0x137d -0x004a -0x04a -0x29b4 -0x18f1 -0x17fc +0x1dc8 +0x794a +0x35c5 +0x137d +0x004a +0x04a +0x29b4 +0x18f1 +0x17fc 0x4707 OP_ChangeSize -0x3e15 -0x7248 -0x57c6 -0x7679 -0x6c8b -0x14c3 -0x3a02 -0x7900 -0x5688 -0x3a58 -0x75dd -0x39e1 -0x47cb -0x171e -0xdab0 -0x618f -0x27b1 -0x3d0c -0x0d2a -0x8c30 -0x2b42 -0x17f8 -0x1665 -0x059d -0x72c9 -0x675d -0x28e0 -0x61df -0x3ef8 -0x4d59 -0x3763 +0x3e15 +0x7248 +0x57c6 +0x7679 +0x6c8b +0x14c3 +0x3a02 +0x7900 +0x5688 +0x3a58 +0x75dd +0x39e1 +0x47cb +0x171e +0xdab0 +0x618f +0x27b1 +0x3d0c +0x0d2a +0x8c30 +0x2b42 +0x17f8 +0x1665 +0x059d +0x72c9 +0x675d +0x28e0 +0x61df +0x3ef8 +0x4d59 +0x3763 0x672f OP_XTargetResponse 0x45be OP_XTargetRequest 0x792c OP_XTargetAutoAddHaters -0x66bf -0x66df -0x2aa7 -0x76c6 -0x6032 -0x3e00 -0x0c13 -0x0a59 -0x393a -0x45ed -0x507f -0x68ba -0x5a63 -0x6fd0 -0x63fd -0x4f09 -0x485d -0x3968 -0x69e1 -0x3d94 -0x0351 -0x5f0a -0x36be -0x59f9 -0x7075 -0x6ee6 -0x2691 -0x3c8b -0x01df -0x218c -0x233b -0x2cf7 -0x1097 -0x1baf -0x6f35 -0x1228 -0x1cef -0x7d28 -0x087f -0x1967 -0x6917 -0x613d -0x37d9 -0x58c7 -0x21ec -0x3424 -0x2036 -0x7ad1 -0x01c3 -0x626e -0x711a -0x5b41 -0x21ba -0x2933 -0x050c -0x51e3 -0x64a3 -0x3146 -0x2aa6 -0x16df -0x2698 -0x21ea -0x796c -0x7bb1 -0x6af6 -0x499a -0x3c44 -0x3cbc -0x6561 -0x12f7 -0x6292 -0x3de0 -0x1409 -0x4604 -0x0ca1 -0x754e -0x4f2b -0x6995 -0x3745 -0x789b -0xcadf -0x4abe -0x6014 -0x7fff -0x6228 -0x10e0 -0x7452 -0x6d9f -0x7f80 -0xb07f +0x66bf +0x66df +0x2aa7 +0x76c6 +0x6032 +0x3e00 +0x0c13 +0x0a59 +0x393a +0x45ed +0x507f +0x68ba +0x5a63 +0x6fd0 +0x63fd +0x4f09 +0x485d +0x3968 +0x69e1 +0x3d94 +0x0351 +0x5f0a +0x36be +0x59f9 +0x7075 +0x6ee6 +0x2691 +0x3c8b +0x01df +0x218c +0x233b +0x2cf7 +0x1097 +0x1baf +0x6f35 +0x1228 +0x1cef +0x7d28 +0x087f +0x1967 +0x6917 +0x613d +0x37d9 +0x58c7 +0x21ec +0x3424 +0x2036 +0x7ad1 +0x01c3 +0x626e +0x711a +0x5b41 +0x21ba +0x2933 +0x050c +0x51e3 +0x64a3 +0x3146 +0x2aa6 +0x16df +0x2698 +0x21ea +0x796c +0x7bb1 +0x6af6 +0x499a +0x3c44 +0x3cbc +0x6561 +0x12f7 +0x6292 +0x3de0 +0x1409 +0x4604 +0x0ca1 +0x754e +0x4f2b +0x6995 +0x3745 +0x789b +0xcadf +0x4abe +0x6014 +0x7fff +0x6228 +0x10e0 +0x7452 +0x6d9f +0x7f80 OP_ItemPreviewRequest +0xb07f 0x6b5c OP_ItemPreview -0x02b4 -0x3bd3 -0x3036 -0x5c3f -0x433d -0x78e2 -0x543f -0x58b4 -0x2563 -0x1820 -0x40be -0x62d6 -0x0d68 -0x6d10 -0x3a32 -0x2fed -0x1303 -0x49df -0x3e24 -0x2b35 -0x0910 -0x3849 -0x6873 -0x15e2 -0x2d85 -0x39bb -0x42e9 -0x6860 -0x15a8 -0x52b5 -0x3311 -0x58df -0x2a7f -0x6573 -0x7a4d -0x7497 -0x1fcc -0x7c23 -0x2d28 -0x5dab -0x005a -0x05a -0x4506 -0x046d -0x36db -0x5a40 -0x4cd9 -0x63d7 -0x48d4 -0x035d -0x11f5 -0x7b84 -0x4f05 -0x7369 -0x7b32 -0x4fe6 -0x6cd0 -0x6770 -0x5c24 -0x063a -0x0d93 -0x4c2a -0x2235 -0x7b95 -0x6a1f -0x46f0 -0x2de2 -0xadd7 -0x2cc6 -0x7db7 -0x7588 -0x4957 -0x6a98 - +0x02b4 +0x3bd3 +0x3036 +0x5c3f +0x433d +0x78e2 +0x543f +0x58b4 +0x2563 +0x1820 +0x40be +0x62d6 +0x0d68 +0x6d10 +0x3a32 +0x2fed +0x1303 +0x49df +0x3e24 +0x2b35 +0x0910 +0x3849 +0x6873 +0x15e2 +0x2d85 +0x39bb +0x42e9 +0x6860 +0x15a8 +0x52b5 +0x3311 +0x58df +0x2a7f +0x6573 +0x7a4d +0x7497 +0x1fcc +0x7c23 +0x2d28 +0x5dab +0x005a +0x05a +0x4506 +0x046d +0x36db +0x5a40 +0x4cd9 +0x63d7 +0x48d4 +0x035d +0x11f5 +0x7b84 +0x4f05 +0x7369 +0x7b32 +0x4fe6 +0x6cd0 +0x6770 +0x5c24 +0x063a +0x0d93 +0x4c2a +0x2235 +0x7b95 +0x6a1f +0x46f0 +0x2de2 +0xadd7 +0x2cc6 +0x7db7 +0x7588 +0x4957 +0x6a98 + 0x057b OP_SendMembershipDetails 0x7acc OP_SendMembership -0x431f -0x42c4 -0x05a8 +0x431f +0x42c4 +0x05a8 0x5475 OP_SendMaxCharacters -0x4ad2 -0x03d9 -0x771e -0x2a34 -0x7273 -0x1c9e -0x5f17 -0x37a9 -0x6530 -0x3157 -0x1f5e -0x2bc8 -0x593a -0x1643 -0x16bc -0x1781 -0x53d3 -0x5369 -0x25b3 -0x2fa0 -0x73a9 -0x1595 -0x6b6f -0x65f2 -0x3562 -0x309d -0x4efa -0x1da9 -0x6678 -0x16e8 \ No newline at end of file +0x4ad2 +0x03d9 +0x771e +0x2a34 +0x7273 +0x1c9e +0x5f17 +0x37a9 +0x6530 +0x3157 +0x1f5e +0x2bc8 +0x593a +0x1643 +0x16bc +0x1781 +0x53d3 +0x5369 +0x25b3 +0x2fa0 +0x73a9 +0x1595 +0x6b6f +0x65f2 +0x3562 +0x309d +0x4efa +0x1da9 +0x6678 +0x16e8 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 92ef6a3d90..38c3e299d5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -283,6 +283,7 @@ void MapOpcodes() ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; + ConnectedOpcodes[OP_ItemPreviewRequest] = &Client::Handle_OP_ItemPreviewRequest; ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; @@ -9300,6 +9301,30 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) return; } +void Client::Handle_OP_ItemPreviewRequest(const EQApplicationPacket* app) +{ + VERIFY_PACKET_LENGTH(OP_ItemPreviewRequest, app, ItemPreview_Struct); + auto ips = (ItemPreview_Struct*) app->pBuffer; + const EQ::ItemData* item = database.GetItem(ips->itemid); + + if (item) { + EQ::ItemInstance* inst = database.CreateItem(item); + if (inst) { + std::string packet = inst->Serialize(-1); + auto outapp = new EQApplicationPacket(OP_ItemPreviewRequest, packet.length()); + memcpy(outapp->pBuffer, packet.c_str(), packet.length()); + +#if EQDEBUG >= 9 + DumpPacket(outapp); +#endif + + QueuePacket(outapp); + safe_delete(outapp); + safe_delete(inst); + } + } +} + void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { using EQ::spells::CastingSlot; diff --git a/zone/client_packet.h b/zone/client_packet.h index 2970f91dc6..587e14ac7b 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -189,6 +189,7 @@ void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); void Handle_OP_ItemName(const EQApplicationPacket *app); void Handle_OP_ItemPreview(const EQApplicationPacket *app); + void Handle_OP_ItemPreviewRequest(const EQApplicationPacket *app); void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); void Handle_OP_Jump(const EQApplicationPacket *app); void Handle_OP_KeyRing(const EQApplicationPacket *app); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 3e8325e4f2..61f30e4a78 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -896,6 +896,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("Weblink", static_cast(OP_Weblink)), luabind::value("InspectMessageUpdate", static_cast(OP_InspectMessageUpdate)), luabind::value("ItemPreview", static_cast(OP_ItemPreview)), + luabind::value("ItemPreviewRequest", static_cast(OP_ItemPreviewRequest)), luabind::value("MercenaryDataRequest", static_cast(OP_MercenaryDataRequest)), luabind::value("MercenaryDataResponse", static_cast(OP_MercenaryDataResponse)), luabind::value("MercenaryHire", static_cast(OP_MercenaryHire)), From 1a27127c39c00947a5eb3d67458b443142e172fd Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 20 Jan 2025 23:21:37 -0600 Subject: [PATCH 58/73] [Hotfix] Fix query error in character_evolving_items --- common/shareddb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index c51b79885e..679bffe9d0 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -661,7 +661,7 @@ bool SharedDatabase::GetInventory(Client *c) // Retrieve character inventory auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`", char_id)); auto e_results = CharacterEvolvingItemsRepository::GetWhere( - *this, fmt::format("`char_id` = '{}' AND `deleted_at` IS NULL", char_id) + *this, fmt::format("`character_id` = '{}' AND `deleted_at` IS NULL", char_id) ); if (results.empty()) { From 25826c66868a5228d2e555a001a6b3bef4121530 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 21 Jan 2025 15:35:19 -0600 Subject: [PATCH 59/73] [Performance] Client / NPC Position Update Optimizations (#4602) * Zone optimizations * More changes * More * Update entity.cpp * Beautiful * Amazing * Feature flag all logic * Broadcast to group * Update mob.cpp * Updates * Update client.cpp * Update client.cpp * Add rule Zone:EnableEntityClipping * Little bit of cleanup * Don't send update to self while in group * Remove visibility work and feature flags * Cleanup * Logging * Improve CheckSendBulkNpcPositions * No need to cast * Field cleanup * Build initial list on zone-in --- common/database/database_update_manifest.cpp | 15 ++- common/eqemu_logsys.h | 4 +- common/eqemu_logsys_log_aliases.h | 10 ++ .../repositories/base/base_zone_repository.h | 24 ++--- common/timer.h | 3 + common/version.h | 2 +- common/zone_store.cpp | 6 -- common/zone_store.h | 1 - zone/bot.cpp | 33 ------ zone/client.cpp | 102 ++++++++++++++---- zone/client.h | 5 +- zone/client_packet.cpp | 12 ++- zone/client_process.cpp | 2 +- zone/embparser_api.cpp | 12 --- zone/entity.cpp | 31 +++--- zone/entity.h | 2 - zone/lua_general.cpp | 12 --- zone/mob.cpp | 7 +- zone/mob.h | 8 +- zone/mob_movement_manager.cpp | 26 ++++- zone/npc.cpp | 4 +- zone/spells.cpp | 6 +- zone/zone.cpp | 83 ++------------ zone/zone.h | 7 +- 24 files changed, 203 insertions(+), 214 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 8e17fb9519..be7f01e143 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6289,7 +6289,20 @@ INSERT INTO `items_evolving_details` VALUES )", .content_schema_update = true - } + }, + ManifestEntry{ + .version = 9291, + .description = "2025_01_21_add_remove_zone_fields", + .check = "SHOW COLUMNS FROM `zone` LIKE 'client_update_range'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE zone DROP COLUMN IF EXISTS npc_update_range; +ALTER TABLE zone DROP COLUMN IF EXISTS max_movement_update_range; +ALTER TABLE `zone` ADD COLUMN `client_update_range` int(11) NOT NULL DEFAULT 600 AFTER `npc_max_aggro_dist`; +)", + .content_schema_update = true + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 4d41885542..7ec2bab3f5 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -143,6 +143,7 @@ namespace Logs { Corpses, XTargets, EvolveItem, + PositionUpdate, MaxCategoryID /* Don't Remove this */ }; @@ -244,7 +245,8 @@ namespace Logs { "EqTime", "Corpses", "XTargets", - "EvolveItem" + "EvolveItem", + "PositionUpdate" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 638657f64b..bb65cece51 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -854,6 +854,16 @@ OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogPositionUpdate(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::PositionUpdate))\ + OutF(LogSys, Logs::General, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogPositionUpdateDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::PositionUpdate))\ + OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 3ba11466d6..1b42931098 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -111,7 +111,7 @@ class BaseZoneRepository { int32_t fast_regen_mana; int32_t fast_regen_endurance; int32_t npc_max_aggro_dist; - uint32_t max_movement_update_range; + uint32_t client_update_range; int32_t underworld_teleport_index; int32_t lava_damage; int32_t min_lava_damage; @@ -220,7 +220,7 @@ class BaseZoneRepository { "fast_regen_mana", "fast_regen_endurance", "npc_max_aggro_dist", - "max_movement_update_range", + "client_update_range", "underworld_teleport_index", "lava_damage", "min_lava_damage", @@ -325,7 +325,7 @@ class BaseZoneRepository { "fast_regen_mana", "fast_regen_endurance", "npc_max_aggro_dist", - "max_movement_update_range", + "client_update_range", "underworld_teleport_index", "lava_damage", "min_lava_damage", @@ -464,7 +464,7 @@ class BaseZoneRepository { e.fast_regen_mana = 180; e.fast_regen_endurance = 180; e.npc_max_aggro_dist = 600; - e.max_movement_update_range = 600; + e.client_update_range = 600; e.underworld_teleport_index = 0; e.lava_damage = 50; e.min_lava_damage = 10; @@ -599,7 +599,7 @@ class BaseZoneRepository { e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -730,7 +730,7 @@ class BaseZoneRepository { v.push_back(columns[89] + " = " + std::to_string(e.fast_regen_mana)); v.push_back(columns[90] + " = " + std::to_string(e.fast_regen_endurance)); v.push_back(columns[91] + " = " + std::to_string(e.npc_max_aggro_dist)); - v.push_back(columns[92] + " = " + std::to_string(e.max_movement_update_range)); + v.push_back(columns[92] + " = " + std::to_string(e.client_update_range)); v.push_back(columns[93] + " = " + std::to_string(e.underworld_teleport_index)); v.push_back(columns[94] + " = " + std::to_string(e.lava_damage)); v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage)); @@ -850,7 +850,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -978,7 +978,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -1110,7 +1110,7 @@ class BaseZoneRepository { e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -1233,7 +1233,7 @@ class BaseZoneRepository { e.fast_regen_mana = row[89] ? static_cast(atoi(row[89])) : 180; e.fast_regen_endurance = row[90] ? static_cast(atoi(row[90])) : 180; e.npc_max_aggro_dist = row[91] ? static_cast(atoi(row[91])) : 600; - e.max_movement_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; + e.client_update_range = row[92] ? static_cast(strtoul(row[92], nullptr, 10)) : 600; e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; @@ -1406,7 +1406,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); @@ -1527,7 +1527,7 @@ class BaseZoneRepository { v.push_back(std::to_string(e.fast_regen_mana)); v.push_back(std::to_string(e.fast_regen_endurance)); v.push_back(std::to_string(e.npc_max_aggro_dist)); - v.push_back(std::to_string(e.max_movement_update_range)); + v.push_back(std::to_string(e.client_update_range)); v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); diff --git a/common/timer.h b/common/timer.h index 6678be9526..599f7b2f22 100644 --- a/common/timer.h +++ b/common/timer.h @@ -86,6 +86,9 @@ struct BenchTimer void reset() { start_time = clock::now(); } // this is seconds double elapsed() { return std::chrono::duration (clock::now() - start_time).count(); } + std::chrono::milliseconds::rep elapsedMilliseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } + std::chrono::microseconds::rep elapsedMicroseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } + std::chrono::nanoseconds::rep elapsedNanoseconds() { return std::chrono::duration_cast(clock::now() - start_time).count(); } private: std::chrono::time_point start_time; }; diff --git a/common/version.h b/common/version.h index ba2bb092ba..84163134f2 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9290 +#define CURRENT_BINARY_DATABASE_VERSION 9291 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/common/zone_store.cpp b/common/zone_store.cpp index 23a59c31e6..fa64bd4924 100644 --- a/common/zone_store.cpp +++ b/common/zone_store.cpp @@ -676,12 +676,6 @@ int ZoneStore::GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version) return z ? z->npc_max_aggro_dist : DEFAULT_ZONE_MAX_AGGRO_DISTANCE; } -uint32 ZoneStore::GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version) -{ - const auto& z = GetZoneVersionWithFallback(zone_id, version); - return z ? z->max_movement_update_range : DEFAULT_ZONE_MAX_MOVEMENT_UPDATE_RANGE; -} - int8 ZoneStore::GetZoneMinimumExpansion(uint32 zone_id, int version) { const auto& z = GetZoneVersionWithFallback(zone_id, version); diff --git a/common/zone_store.h b/common/zone_store.h index b63e807f29..d8ee7233d8 100644 --- a/common/zone_store.h +++ b/common/zone_store.h @@ -94,7 +94,6 @@ class ZoneStore { int GetZoneFastRegenMana(uint32 zone_id, int version = 0); int GetZoneFastRegenEndurance(uint32 zone_id, int version = 0); int GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version = 0); - uint32 GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version = 0); int8 GetZoneMinimumExpansion(uint32 zone_id, int version = 0); int8 GetZoneMaximumExpansion(uint32 zone_id, int version = 0); const std::string GetZoneContentFlags(uint32 zone_id, int version = 0); diff --git a/zone/bot.cpp b/zone/bot.cpp index aa746e7677..b0f32b406f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7390,39 +7390,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { return; } -/** - * @param close_mobs - * @param scanning_mob - */ -void EntityList::ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob) -{ - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - - close_mobs.clear(); - - for (auto& e : mob_list) { - auto mob = e.second; - - if (!mob->IsClient()) { - continue; - } - - if (mob->GetID() <= 0) { - continue; - } - - float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); - if (distance <= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - else if (mob->GetAggroRange() >= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - } - - LogAIScanCloseDetail("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); -} - uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid) { uint8 need_healed = 0; diff --git a/zone/client.cpp b/zone/client.cpp index f6286f47c3..667142f30c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -157,7 +157,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( endupkeep_timer(1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), - m_client_zone_wide_full_position_update_timer(5 * 60 * 1000), + m_client_bulk_npc_pos_update_timer(60 * 1000), tribute_timer(Tribute_duration), proximity_timer(ClientProximity_interval), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), @@ -12939,50 +12939,77 @@ void Client::SendTopLevelInventory() } } -// On a normal basis we limit mob movement updates based on distance -// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes -// -// For very large zones we will also force a full update based on distance -// -// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear -// to full stop when they are actually still pathing -void Client::CheckSendBulkClientPositionUpdate() +void Client::CheckSendBulkNpcPositions() { float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition()); - bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance(); + float update_range = RuleI(Range, MobCloseScanDistance); + bool moved_far_enough_before_bulk_update = distance_moved >= update_range; bool is_ready_to_update = ( - m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update + m_client_bulk_npc_pos_update_timer.Check() || moved_far_enough_before_bulk_update ); - if (IsMoving() && is_ready_to_update) { - LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName()); - + int updated_count = 0; + int skipped_count = 0; + if (is_ready_to_update) { auto &mob_movement_manager = MobMovementManager::Get(); - auto &mob_list = entity_list.GetMobList(); - for (auto &it : mob_list) { - Mob *entity = it.second; - if (!entity->IsNPC()) { + for (auto &e: entity_list.GetMobList()) { + Mob *mob = e.second; + if (!mob->IsNPC()) { continue; } int animation_speed = 0; - if (entity->IsMoving()) { - if (entity->IsRunning()) { - animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed()); + if (mob->IsMoving()) { + if (mob->IsRunning()) { + animation_speed = (mob->IsFeared() ? mob->GetFearSpeed() : mob->GetRunspeed()); } else { - animation_speed = entity->GetWalkspeed(); + animation_speed = mob->GetWalkspeed(); + } + } + + // if we have seen this mob before, and it hasn't moved, skip it + if (m_last_seen_mob_position.contains(mob->GetID())) { + if (m_last_seen_mob_position[mob->GetID()] == mob->GetPosition()) { + LogPositionUpdateDetail( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + GetCleanName() + ); + skipped_count++; + continue; } } - mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this); + mob_movement_manager.SendCommandToClients( + mob, + 0.0, + 0.0, + 0.0, + 0.0, + animation_speed, + ClientRangeAny, + this + ); + + updated_count++; } + LogPositionUpdate( + "[{}] Sent [{}] bulk updated NPC positions, skipped [{}] distance_moved [{}] update_range [{}]", + GetCleanName(), + updated_count, + skipped_count, + distance_moved, + update_range + ); + m_last_position_before_bulk_update = GetPosition(); } } + const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval); const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval); @@ -13123,3 +13150,32 @@ void Client::SetAAEXPPercentage(uint8 percentage) SendAlternateAdvancementStats(); SendAlternateAdvancementTable(); } + +void Client::BroadcastPositionUpdate() +{ + EQApplicationPacket outapp(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct *spu = (PlayerPositionUpdateServer_Struct *) outapp.pBuffer; + + memset(spu, 0x00, sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(GetX()); + spu->y_pos = FloatToEQ19(GetY()); + spu->z_pos = FloatToEQ19(GetZ()); + spu->heading = FloatToEQ12(GetHeading()); + spu->delta_x = FloatToEQ13(0); + spu->delta_y = FloatToEQ13(0); + spu->delta_z = FloatToEQ13(0); + spu->delta_heading = FloatToEQ10(0); + spu->animation = 0; + + entity_list.QueueCloseClients(this, &outapp, true, zone->GetClientUpdateRange()); + + Group *g = GetGroup(); + if (g) { + for (auto & m : g->members) { + if (m && m->IsClient() && m != this) { + m->CastToClient()->QueuePacket(&outapp); + } + } + } +} diff --git a/zone/client.h b/zone/client.h index e8a93bac50..3ec680a879 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2087,12 +2087,13 @@ class Client : public Mob Timer m_client_npc_aggro_scan_timer; void CheckClientToNpcAggroTimer(); void ClientToNpcAggroProcess(); + void BroadcastPositionUpdate(); // bulk position updates glm::vec4 m_last_position_before_bulk_update; - Timer m_client_zone_wide_full_position_update_timer; + Timer m_client_bulk_npc_pos_update_timer; Timer m_position_update_timer; - void CheckSendBulkClientPositionUpdate(); + void CheckSendBulkNpcPositions(); void BulkSendInventoryItems(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 38c3e299d5..066cb49cf5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -971,6 +971,16 @@ void Client::CompleteConnect() RecordStats(); AutoGrantAAPoints(); + // set initial position for mob tracking + m_last_seen_mob_position.reserve(entity_list.GetMobList().size()); + for (auto& mob : entity_list.GetMobList()) { + if (!mob.second->IsNPC()) { + continue; + } + + m_last_seen_mob_position[mob.second->GetID()] = mob.second->GetPosition(); + } + // enforce some rules.. if (!CanEnterZone()) { LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName()); @@ -4956,7 +4966,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { CheckScanCloseMobsMovingTimer(); } - CheckSendBulkClientPositionUpdate(); + CheckSendBulkNpcPositions(); int32 new_animation = ppu->animation; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index fd0028268e..2f70dcb2da 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -122,7 +122,7 @@ bool Client::Process() { /* I haven't naturally updated my position in 10 seconds, updating manually */ if (!IsMoving() && m_position_update_timer.Check()) { - SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); + BroadcastPositionUpdate(); } if (mana_timer.Check()) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index c451db90b8..af2d2eaf66 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5653,16 +5653,6 @@ int Perl__GetZoneNPCMaximumAggroDistance(uint32 zone_id, int version) return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version); } -uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id); -} - -uint32 Perl__GetZoneMaximumMovementUpdateRange(uint32 zone_id, int version) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version); -} - int8 Perl__GetZoneMinimumExpansion(uint32 zone_id) { return zone_store.GetZoneMinimumExpansion(zone_id); @@ -6112,8 +6102,6 @@ void perl_register_quest() package.add("GetZoneMaximumExpansion", (int8(*)(uint32, int))&Perl__GetZoneMaximumExpansion); package.add("GetZoneMaximumLevel", (uint8(*)(uint32))&Perl__GetZoneMaximumLevel); package.add("GetZoneMaximumLevel", (uint8(*)(uint32, int))&Perl__GetZoneMaximumLevel); - package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32))&Perl__GetZoneMaximumMovementUpdateRange); - package.add("GetZoneMaximumMovementUpdateRange", (uint32(*)(uint32, int))&Perl__GetZoneMaximumMovementUpdateRange); package.add("GetZoneMaximumPlayers", (int(*)(uint32))&Perl__GetZoneMaximumPlayers); package.add("GetZoneMaximumPlayers", (int(*)(uint32, int))&Perl__GetZoneMaximumPlayers); package.add("GetZoneMinimumClip", (float(*)(uint32))&Perl__GetZoneMinimumClip); diff --git a/zone/entity.cpp b/zone/entity.cpp index fab1be99f7..85f492c587 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1717,15 +1717,6 @@ void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *a } } -/** - * @param sender - * @param app - * @param ignore_sender - * @param distance - * @param skipped_mob - * @param is_ack_required - * @param filter - */ void EntityList::QueueCloseClients( Mob *sender, const EQApplicationPacket *app, @@ -1742,7 +1733,7 @@ void EntityList::QueueCloseClients( } if (distance <= 0) { - distance = 600; + distance = zone->GetClientUpdateRange(); } float distance_squared = distance * distance; @@ -2878,6 +2869,8 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) ); it->second->m_close_mobs.erase(entity_id); + it->second->m_last_seen_mob_position.erase(entity_id); + ++it; } @@ -2931,6 +2924,9 @@ void EntityList::RemoveAuraFromMobs(Mob *aura) // All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude // less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire // entity list (zone wide) + +BenchTimer g_scan_bench_timer; + void EntityList::ScanCloseMobs(Mob *scanning_mob) { if (!scanning_mob) { @@ -2941,7 +2937,9 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) return; } - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); + g_scan_bench_timer.reset(); + + float scan_range = RuleI(Range, MobCloseScanDistance); // Reserve memory in m_close_mobs to avoid frequent re-allocations if not already reserved. // Assuming mob_list.size() as an upper bound for reservation. @@ -2957,7 +2955,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) continue; } - float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + float distance = Distance(scanning_mob->GetPosition(), mob->GetPosition()); if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { // add mob to scanning_mob's close list and vice versa // check if the mob is already in the close mobs list before inserting @@ -2969,10 +2967,11 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) } LogAIScanClose( - "[{}] Scanning close list > list_size [{}] moving [{}]", + "[{}] Scanning close list > list_size [{}] moving [{}] elapsed [{}] us", scanning_mob->GetCleanName(), scanning_mob->m_close_mobs.size(), - scanning_mob->IsMoving() ? "true" : "false" + scanning_mob->IsMoving() ? "true" : "false", + g_scan_bench_timer.elapsedMicroseconds() ); } @@ -5759,10 +5758,6 @@ void EntityList::ReloadMerchants() { * then we return the full list * * See comments @EntityList::ScanCloseMobs for system explanation - * - * @param mob - * @param distance - * @return */ std::unordered_map &EntityList::GetCloseMobList(Mob *mob, float distance) { diff --git a/zone/entity.h b/zone/entity.h index 33313f14ee..f073a4fb5d 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -631,8 +631,6 @@ class EntityList bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff - void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); - void GetBotList(std::list &b_list); private: std::list bot_list; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 6567d24761..bc1b044163 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4668,16 +4668,6 @@ int lua_get_zone_npc_maximum_aggro_distance(uint32 zone_id, int version) return zone_store.GetZoneNPCMaximumAggroDistance(zone_id, version); } -uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id); -} - -uint32 lua_get_zone_maximum_movement_update_range(uint32 zone_id, int version) -{ - return zone_store.GetZoneMaximumMovementUpdateRange(zone_id, version); -} - int8 lua_get_zone_minimum_expansion(uint32 zone_id) { return zone_store.GetZoneMinimumExpansion(zone_id); @@ -6287,8 +6277,6 @@ luabind::scope lua_register_general() { luabind::def("get_zone_fast_regen_endurance", (int(*)(uint32,int))&lua_get_zone_fast_regen_endurance), luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32))&lua_get_zone_npc_maximum_aggro_distance), luabind::def("get_zone_npc_maximum_aggro_distance", (int(*)(uint32,int))&lua_get_zone_npc_maximum_aggro_distance), - luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32))&lua_get_zone_maximum_movement_update_range), - luabind::def("get_zone_npc_maximum_movement_update_range", (uint32(*)(uint32,int))&lua_get_zone_maximum_movement_update_range), luabind::def("get_zone_minimum_expansion", (int8(*)(uint32))&lua_get_zone_minimum_expansion), luabind::def("get_zone_minimum_expansion", (int8(*)(uint32,int))&lua_get_zone_minimum_expansion), luabind::def("get_zone_maximum_expansion", (int8(*)(uint32))&lua_get_zone_maximum_expansion), diff --git a/zone/mob.cpp b/zone/mob.cpp index dd62d93291..41ed631bb4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -129,6 +129,7 @@ Mob::Mob( position_update_melee_push_timer(500), hate_list_cleanup_timer(6000), m_scan_close_mobs_timer(6000), + m_see_close_mobs_timer(1000), m_mob_check_moving_timer(1000), bot_attack_flag_timer(10000) { @@ -8612,15 +8613,15 @@ std::unordered_map &Mob::GetCloseMobList(float distance) void Mob::ClearDataBucketCache() { if (IsOfClientBot()) { - uint64 id = 0; + uint64 id = 0; DataBucketLoadType::Type t{}; if (IsBot()) { id = CastToBot()->GetBotID(); - t = DataBucketLoadType::Bot; + t = DataBucketLoadType::Bot; } else if (IsClient()) { id = CastToClient()->CharacterID(); - t = DataBucketLoadType::Client; + t = DataBucketLoadType::Client; } DataBucket::DeleteFromCache(id, t); diff --git a/zone/mob.h b/zone/mob.h index 297aa80cba..e4a495d6b2 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -201,9 +201,11 @@ class Mob : public Entity { void DisplayInfo(Mob *mob); - std::unordered_map m_close_mobs; - Timer m_scan_close_mobs_timer; - Timer m_mob_check_moving_timer; + std::unordered_map m_close_mobs; + std::unordered_map m_last_seen_mob_position; + Timer m_scan_close_mobs_timer; + Timer m_see_close_mobs_timer; + Timer m_mob_check_moving_timer; // Bot attack flag Timer bot_attack_flag_timer; diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index 3bb5996a25..661c046a10 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -851,12 +851,24 @@ void MobMovementManager::SendCommandToClients( _impl->Stats.TotalSentPosition++; } + if (c->m_last_seen_mob_position.contains(mob->GetID())) { + if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) { + LogPositionUpdate( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + c->GetCleanName() + ); + continue; + } + } + c->QueuePacket(&outapp, false); + c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition(); } } else { float short_range = RuleR(Pathing, ShortMovementUpdateRange); - float long_range = zone->GetNpcPositionUpdateDistance(); + float long_range = RuleI(Range, MobCloseScanDistance); for (auto &c : _impl->Clients) { if (single_client && c != single_client) { @@ -901,7 +913,19 @@ void MobMovementManager::SendCommandToClients( _impl->Stats.TotalSentPosition++; } + if (c->m_last_seen_mob_position.contains(mob->GetID())) { + if (c->m_last_seen_mob_position[mob->GetID()] == mob->GetPosition() && anim == 0) { + LogPositionUpdate( + "Mob [{}] has already been sent to client [{}] at this position, skipping", + mob->GetCleanName(), + c->GetCleanName() + ); + continue; + } + } + c->QueuePacket(&outapp, false); + c->m_last_seen_mob_position[mob->GetID()] = mob->GetPosition(); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index 38f597e631..ba5782c347 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -997,8 +997,8 @@ NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 npc_type->current_hp = 4000000; npc_type->max_hp = 4000000; - npc_type->race = 2254; - npc_type->gender = 2; + npc_type->race = 127; + npc_type->gender = 0; npc_type->class_ = 9; npc_type->deity = 1; npc_type->level = 200; diff --git a/zone/spells.cpp b/zone/spells.cpp index 4ca95255c3..723c6833ef 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7378,15 +7378,17 @@ void Mob::SetHP(int64 hp) void Mob::DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec) { - NPC *node = nullptr; + NPC *node = nullptr; + for (const auto &n: entity_list.GetNPCList()) { - if (n.second->GetCleanName() == node_name) { + if (n.second->GetEntityVariable("node_parent_id") == std::to_string(GetID())) { node = n.second; break; } } if (!node) { node = NPC::SpawnNodeNPC(node_name, "", GetPosition()); + node->SetEntityVariable("node_parent_id", std::to_string(GetID())); } } diff --git a/zone/zone.cpp b/zone/zone.cpp index f02be2e2bd..5488ed9147 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1091,7 +1091,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) mMovementManager = &MobMovementManager::Get(); - SetNpcPositionUpdateDistance(0); SetQuestHotReloadQueued(false); } @@ -1374,17 +1373,17 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version) newzone_data.suspend_buffs = z->suspendbuffs; // local attributes - can_bind = z->canbind != 0; - is_city = z->canbind == 2; - can_combat = z->cancombat != 0; - can_levitate = z->canlevitate != 0; - can_castoutdoor = z->castoutdoor != 0; - is_hotzone = z->hotzone != 0; - max_movement_update_range = z->max_movement_update_range; - default_ruleset = z->ruleset; - allow_mercs = true; - m_graveyard_id = z->graveyard_id; - m_max_clients = z->maxclients; + can_bind = z->canbind != 0; + is_city = z->canbind == 2; + can_combat = z->cancombat != 0; + can_levitate = z->canlevitate != 0; + can_castoutdoor = z->castoutdoor != 0; + is_hotzone = z->hotzone != 0; + m_client_update_range = z->client_update_range; + default_ruleset = z->ruleset; + allow_mercs = true; + m_graveyard_id = z->graveyard_id; + m_max_clients = z->maxclients; SetIdleWhenEmpty(z->idle_when_empty); SetSecondsBeforeIdle(z->seconds_before_idle); @@ -1538,10 +1537,6 @@ bool Zone::Process() { if (adv_data && !did_adventure_actions) { DoAdventureActions(); } - - if (GetNpcPositionUpdateDistance() == 0) { - CalculateNpcUpdateDistanceSpread(); - } } if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) { @@ -2735,62 +2730,6 @@ void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) { m_ucss_available = ucss_available; } -int Zone::GetNpcPositionUpdateDistance() const -{ - return npc_position_update_distance; -} - -void Zone::SetNpcPositionUpdateDistance(int in_npc_position_update_distance) -{ - Zone::npc_position_update_distance = in_npc_position_update_distance; -} - -void Zone::CalculateNpcUpdateDistanceSpread() -{ - float max_x = 0; - float max_y = 0; - float min_x = 0; - float min_y = 0; - - auto &mob_list = entity_list.GetMobList(); - - for (auto &it : mob_list) { - Mob *entity = it.second; - if (!entity->IsNPC()) { - continue; - } - - if (entity->GetX() <= min_x) { - min_x = entity->GetX(); - } - - if (entity->GetY() <= min_y) { - min_y = entity->GetY(); - } - - if (entity->GetX() >= max_x) { - max_x = entity->GetX(); - } - - if (entity->GetY() >= max_y) { - max_y = entity->GetY(); - } - } - - int x_spread = int(std::abs(max_x - min_x)); - int y_spread = int(std::abs(max_y - min_y)); - int combined_spread = int(std::abs((x_spread + y_spread) / 2)); - int update_distance = EQ::ClampLower(int(combined_spread / 4), int(zone->GetMaxMovementUpdateRange())); - - SetNpcPositionUpdateDistance(update_distance); - - Log(Logs::General, Logs::Debug, - "NPC update spread distance set to [%i] combined_spread [%i]", - update_distance, - combined_spread - ); -} - bool Zone::IsQuestHotReloadQueued() const { return quest_hot_reload_queued; diff --git a/zone/zone.h b/zone/zone.h index c29a89b29c..1d46a04157 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -156,9 +156,6 @@ class Zone { bool SaveZoneCFG(); bool DoesAlternateCurrencyExist(uint32 currency_id); - int GetNpcPositionUpdateDistance() const; - void SetNpcPositionUpdateDistance(int in_npc_position_update_distance); - char *adv_data; const char *GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location); @@ -417,7 +414,7 @@ class Zone { SendDiscordMessage(webhook_id, message_prefix + Discord::FormatDiscordMessage(log_category, message)); }; - double GetMaxMovementUpdateRange() const { return max_movement_update_range; } + double GetClientUpdateRange() const { return m_client_update_range; } void SetIsHotzone(bool is_hotzone); @@ -469,7 +466,7 @@ class Zone { bool staticzone; bool zone_has_current_time; bool quest_hot_reload_queued; - double max_movement_update_range; + double m_client_update_range; char *long_name; char *map_name; char *short_name; From d13c725a741b6670027753ea520b12e3ca6cad58 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 21 Jan 2025 15:50:20 -0600 Subject: [PATCH 60/73] [Linux] Implement KSM Kernel Samepage Merging with Maps (#4601) * KSM work * Windows fixes * Add KSM logging, cleanup * Cleanup raycast logging --- .gitignore | 1 + common/CMakeLists.txt | 1 + common/eqemu_logsys.h | 4 +- common/eqemu_logsys_log_aliases.h | 12 +- common/memory/ksm.hpp | 220 ++++++++++++++++++++++++++++++ world/cli/test.cpp | 20 ++- zone/gm_commands/loc.cpp | 13 ++ zone/map.cpp | 2 + zone/raycast_mesh.cpp | 117 ++++++++++++---- 9 files changed, 357 insertions(+), 33 deletions(-) create mode 100644 common/memory/ksm.hpp diff --git a/.gitignore b/.gitignore index 2f37a33f18..627d3c6306 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ compile_flags.txt # CMake Files cmake-build-relwithdebinfo/* +skill-caps.diff diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ff130adb0a..c8105eb83a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -98,6 +98,7 @@ SET(common_sources json/json.hpp json/jsoncpp.cpp zone_store.cpp + memory/ksm.hpp net/console_server.cpp net/console_server_connection.cpp net/crc32.cpp diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 7ec2bab3f5..0697739599 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -144,6 +144,7 @@ namespace Logs { XTargets, EvolveItem, PositionUpdate, + KSM, MaxCategoryID /* Don't Remove this */ }; @@ -246,7 +247,8 @@ namespace Logs { "Corpses", "XTargets", "EvolveItem", - "PositionUpdate" + "PositionUpdate", + "KSM" // Kernel Samepage Merging }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index bb65cece51..4f1eadde7f 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -861,7 +861,17 @@ #define LogPositionUpdateDetail(message, ...) do {\ if (LogSys.IsLogEnabled(Logs::Detail, Logs::PositionUpdate))\ - OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ + OutF(LogSys, Logs::Detail, Logs::PositionUpdate, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__); \ +} while (0) + +#define LogKSM(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::KSM))\ + OutF(LogSys, Logs::General, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogKSMDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::KSM))\ + OutF(LogSys, Logs::Detail, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) #define Log(debug_level, log_category, message, ...) do {\ diff --git a/common/memory/ksm.hpp b/common/memory/ksm.hpp new file mode 100644 index 0000000000..079b83f1a5 --- /dev/null +++ b/common/memory/ksm.hpp @@ -0,0 +1,220 @@ +#ifndef EQEMU_KSM_HPP +#define EQEMU_KSM_HPP + +#include "../eqemu_logsys.h" +#include +#include +#include +#ifdef _WIN32 +#include // For _aligned_malloc, _aligned_free +#include +#else +#include // For madvise +#include // For sysconf, sbrk +#endif + + +// Page-aligned allocator for std::vector +template +class PageAlignedAllocator { +public: + using value_type = T; + + PageAlignedAllocator() noexcept = default; + template + PageAlignedAllocator(const PageAlignedAllocator&) noexcept {} + + T* allocate(std::size_t n) { + void* ptr = nullptr; + size_t size = n * sizeof(T); + +#ifdef _WIN32 + // Simply allocate memory without alignment + ptr = malloc(size); + if (!ptr) throw std::bad_alloc(); +#else + size_t alignment = getPageSize(); // Get the system's page size + if (posix_memalign(&ptr, alignment, size) != 0) { + throw std::bad_alloc(); + } +#endif + return static_cast(ptr); + } + + void deallocate(T* p, std::size_t) noexcept { + free(p); + } + +private: + size_t getPageSize() const + { +#ifdef _WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return sysInfo.dwPageSize; // Page size in bytes +#else + return static_cast(sysconf(_SC_PAGESIZE)); +#endif + }; +}; + +template +bool operator==(const PageAlignedAllocator&, const PageAlignedAllocator&) noexcept { + return true; +} + +template +bool operator!=(const PageAlignedAllocator&, const PageAlignedAllocator&) noexcept { + return false; +} + +// Kernel Samepage Merging (KSM) functionality +namespace KSM { + +#ifdef _WIN32 + // Windows-specific placeholder functions (no-op) + inline void CheckPageAlignment(void* ptr) { + } + + inline void* AllocatePageAligned(size_t size) { + return memset(malloc(size), 0, size); + } + + inline void MarkMemoryForKSM(void* start, size_t size) { + } + + inline void AlignHeapToPageBoundary() { + } + + inline void* MarkHeapStart() { + return nullptr; + } + + inline size_t MeasureHeapUsage(void* start) { + return 0; + } +#else + // Linux-specific functionality + inline void CheckPageAlignment(void* ptr) { + size_t page_size = sysconf(_SC_PAGESIZE); + if (reinterpret_cast(ptr) % page_size == 0) { + LogKSMDetail("Memory is page-aligned [{}]", ptr); + } else { + LogKSMDetail("Memory is NOT page-aligned [{}]", ptr); + } + } + + inline void* AllocatePageAligned(size_t size) { + size_t page_size = sysconf(_SC_PAGESIZE); + void* aligned_ptr = nullptr; + if (posix_memalign(&aligned_ptr, page_size, size) != 0) { + LogKSM("Failed to allocate page-aligned memory on Linux. page_size [{}] size [{}] bytes", page_size, size); + } + std::memset(aligned_ptr, 0, size); + return aligned_ptr; + } + + inline void MarkMemoryForKSM(void* start, size_t size) { + if (madvise(start, size, MADV_MERGEABLE) == 0) { + LogKSM("Marked memory for KSM | start [{}] size [{}] bytes", start, size); + } else { + perror("madvise failed"); + } + } + + inline void AlignHeapToPageBoundary() { + size_t page_size = sysconf(_SC_PAGESIZE); + if (page_size == 0) { + LogKSM("Failed to retrieve page size SC_PAGESIZE [{}]", page_size); + return; + } + + void* current_break = sbrk(0); + if (current_break == (void*)-1) { + LogKSM("Failed to retrieve the current program break"); + return; + } + + uintptr_t current_address = reinterpret_cast(current_break); + size_t misalignment = current_address % page_size; + + if (misalignment != 0) { + size_t adjustment = page_size - misalignment; + if (sbrk(adjustment) == (void*)-1) { + LogKSM("Failed to align heap to page boundary. adjustment [{}] bytes", adjustment); + return; + } + } + + LogKSMDetail("Heap aligned to next page boundary. Current break [{}]", sbrk(0)); + } + + inline void* MarkHeapStart() { + void* current_pos = sbrk(0); + AlignHeapToPageBoundary(); + return current_pos; + } + + inline size_t MeasureHeapUsage(void* start) { + void* current_break = sbrk(0); + return static_cast(current_break) - static_cast(start); + } +#endif + + + inline size_t getPageSize() + { +#ifdef _WIN32 + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return sysInfo.dwPageSize; // Page size in bytes +#else + return static_cast(sysconf(_SC_PAGESIZE)); // POSIX page size +#endif + }; + + template + inline void PageAlignVectorAligned(std::vector>& vec) { + if (vec.empty()) { + return; + } + + size_t page_size = getPageSize(); + void* start = vec.data(); + size_t size = vec.size() * sizeof(T); + + // Check if the memory is page-aligned + if (reinterpret_cast(start) % page_size != 0) { + // Allocate a new aligned vector + std::vector> aligned_vec(vec.get_allocator()); + aligned_vec.reserve(vec.capacity()); // Match capacity to avoid reallocation during copy + + // Copy elements from the original vector + aligned_vec.insert(aligned_vec.end(), vec.begin(), vec.end()); + + // Swap the aligned vector with the original vector + vec.swap(aligned_vec); + + // Clear the temporary aligned vector to free its memory + aligned_vec.clear(); + + // Verify the new alignment + start = vec.data(); + if (reinterpret_cast(start) % page_size != 0) { + throw std::runtime_error("Failed to align vector memory to page boundaries."); + } + + LogKSMDetail("Vector reallocated to ensure page alignment. start [{}] size [{}] bytes", start, size); + } else { + LogKSMDetail("Vector is already page-aligned. start [{}] size [{}] bytes", start, size); + } + +#ifndef _WIN32 + // Mark memory for KSM (only on non-Windows systems) + MarkMemoryForKSM(start, size); +#endif + } + +} + +#endif // EQEMU_KSM_HPP diff --git a/world/cli/test.cpp b/world/cli/test.cpp index 738741723a..172d46d21f 100644 --- a/world/cli/test.cpp +++ b/world/cli/test.cpp @@ -1,6 +1,8 @@ #include #include +#include #include "../../common/events/player_events.h" +#include "../../common/memory/ksm.hpp" void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description) { @@ -10,5 +12,21 @@ void WorldserverCLI::TestCommand(int argc, char **argv, argh::parser &cmd, std:: return; } - + void* start_marker = KSM::MarkHeapStart(); + std::cout << "Start marker: " << start_marker << "\n"; + + std::vector vec = {}; + for (int i = 0; i < 100000; i++) { + vec.push_back("Some random string"); + } + + // Measure allocated memory size + size_t allocated_size = KSM::MeasureHeapUsage(start_marker); + // Convert to MB as a float and output with precision + double allocated_size_mb = static_cast(allocated_size) / (1024 * 1024); + std::cout << std::fixed << std::setprecision(3) + << "Allocated size: " << allocated_size_mb << " MB\n"; + + // Mark memory for KSM + KSM::MarkMemoryForKSM(start_marker, allocated_size); } diff --git a/zone/gm_commands/loc.cpp b/zone/gm_commands/loc.cpp index 62fdf4be45..4e4085addc 100755 --- a/zone/gm_commands/loc.cpp +++ b/zone/gm_commands/loc.cpp @@ -9,6 +9,19 @@ void command_loc(Client *c, const Seperator *sep) auto target_position = target->GetPosition(); + // check los benchmark + BenchTimer timer; + for (int i = 0; i < 1000; i++) { + zone->zonemap->CheckLoS(c->GetPosition(), target_position); + } + c->Message( + Chat::White, + fmt::format( + "CheckLoS benchmark took [{}]", + timer.elapsed() + ).c_str() + ); + c->Message( Chat::White, fmt::format( diff --git a/zone/map.cpp b/zone/map.cpp index 96feddb9c5..7e3d8822ce 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -7,6 +7,7 @@ #include "raycast_mesh.h" #include "zone.h" #include "../common/file.h" +#include "../common/memory/ksm.hpp" #include #include @@ -953,6 +954,7 @@ bool Map::LoadV2(FILE *f) { return true; } + void Map::RotateVertex(glm::vec3 &v, float rx, float ry, float rz) { glm::vec3 nv = v; diff --git a/zone/raycast_mesh.cpp b/zone/raycast_mesh.cpp index 8873c293e0..20c3f6b710 100644 --- a/zone/raycast_mesh.cpp +++ b/zone/raycast_mesh.cpp @@ -1,4 +1,6 @@ #include "raycast_mesh.h" +#include "../common/memory/ksm.hpp" +#include "../common/eqemu_logsys.h" #include #include #include @@ -9,7 +11,7 @@ // This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do // high-speed raycasting. // -// There are much better implementations of this available on the internet. In particular I recommend that you use +// There are much better implementations of this available on the internet. In particular I recommend that you use // OPCODE written by Pierre Terdiman. // @see: http://www.codercorner.com/Opcode.htm // @@ -17,7 +19,7 @@ // // I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting. // I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However, -// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders +// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders // of magnitude; so this implementation should work fine for simple tools and utilities. // // It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees. @@ -32,14 +34,14 @@ // // The official source can be found at: http://code.google.com/p/raycastmesh/ // -// +// #pragma warning(disable:4100) namespace RAYCAST_MESH { -typedef std::vector< RmUint32 > TriVector; +typedef std::vector> TriVector; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -365,7 +367,7 @@ class BoundsAABB { RmUint32 ret = 0; - if ( p[0] < mMin[0] ) + if ( p[0] < mMin[0] ) { ret|=CC_MINX; } @@ -374,7 +376,7 @@ class BoundsAABB ret|=CC_MAXX; } - if ( p[1] < mMin[1] ) + if ( p[1] < mMin[1] ) { ret|=CC_MINY; } @@ -383,7 +385,7 @@ class BoundsAABB ret|=CC_MAXY; } - if ( p[2] < mMin[2] ) + if ( p[2] < mMin[2] ) { ret|=CC_MINZ; } @@ -514,7 +516,7 @@ class NodeInterface // the width of the longest axis is less than the minimum axis size then... // we create the leaf node and copy the triangles into the leaf node triangle array. if ( count < minLeafSize || depth >= maxDepth || laxis < minAxisSize ) - { + { // Copy the triangle indices into the leaf triangles array mLeafTriangleIndex = leafTriangles.size(); // assign the array start location for these leaf triangles. leafTriangles.push_back(count); @@ -542,7 +544,7 @@ class NodeInterface // and another array that includes all triangles which intersect the 'right' half of the bounding volume node. for (auto i = triangles.begin(); i != triangles.end(); ++i) { - RmUint32 tri = (*i); + RmUint32 tri = (*i); { RmUint32 i1 = indices[tri*3+0]; @@ -590,7 +592,7 @@ class NodeInterface { leftBounds.clamp(b1); // we have to clamp the bounding volume so it stays inside the parent volume. mLeft = callback->getNode(); // get a new AABB node - new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values. + new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values. // Then recursively split this node. mLeft->split(leftTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles); } @@ -662,7 +664,7 @@ class NodeInterface RmReal nd = nearestDistance; if ( !intersectLineSegmentAABB(mBounds.mMin,mBounds.mMax,from,dir,nd,sect) ) { - return; + return; } if ( mLeafTriangleIndex != TRI_EOF ) { @@ -754,28 +756,60 @@ class MyRaycastMesh : public RaycastMesh, public NodeInterface { mMaxNodeCount+=pow2Table[i]; } - mNodes = new NodeAABB[mMaxNodeCount]; + // Allocate page-aligned memory + mNodes = static_cast(KSM::AllocatePageAligned(sizeof(NodeAABB) * mMaxNodeCount)); + if (!mNodes) { + throw std::bad_alloc(); + } mNodeCount = 0; + KSM::CheckPageAlignment(mNodes); + + mVertices = static_cast(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * vcount)); + if (!mVertices) { + throw std::bad_alloc(); + } + std::memcpy(mVertices, vertices, sizeof(RmReal) * 3 * vcount); mVcount = vcount; - mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount); - memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount); + + mIndices = static_cast(KSM::AllocatePageAligned(sizeof(RmUint32) * 3 * tcount)); + if (!mIndices) { + throw std::bad_alloc(); + } + std::memcpy(mIndices, indices, sizeof(RmUint32) * 3 * tcount); mTcount = tcount; - mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3); - memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3); - mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32)); - memset(mRaycastTriangles,0,tcount*sizeof(RmUint32)); + + mRaycastTriangles = static_cast(KSM::AllocatePageAligned(sizeof(RmUint32) * tcount)); + if (!mRaycastTriangles) { + throw std::bad_alloc(); + } + std::memset(mRaycastTriangles, 0, sizeof(RmUint32) * tcount); + + mFaceNormals = static_cast(KSM::AllocatePageAligned(sizeof(RmReal) * 3 * tcount)); + if (!mFaceNormals) { + throw std::bad_alloc(); + } + std::memset(mFaceNormals, 0, sizeof(RmReal) * 3 * tcount); + + // Mark memory as mergeable for KSM + KSM::MarkMemoryForKSM(mVertices, sizeof(RmReal) * 3 * vcount); + KSM::MarkMemoryForKSM(mIndices, sizeof(RmUint32) * 3 * tcount); + KSM::MarkMemoryForKSM(mRaycastTriangles, sizeof(RmUint32) * tcount); + KSM::MarkMemoryForKSM(mFaceNormals, sizeof(RmReal) * 3 * tcount); + mRoot = getNode(); mFaceNormals = NULL; new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles); + + KSM::MarkMemoryForKSM(mLeafTriangles.data(), mLeafTriangles.size() * sizeof(RmUint32)); } ~MyRaycastMesh(void) { - delete []mNodes; - ::free(mVertices); - ::free(mIndices); - ::free(mFaceNormals); - ::free(mRaycastTriangles); + if (mNodes) { free(mNodes); } + if (mVertices) { free(mVertices); } + if (mIndices) { free(mIndices); } + if (mRaycastTriangles) { free(mRaycastTriangles); } + if (mFaceNormals) { free(mFaceNormals); } } virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) @@ -812,7 +846,7 @@ class MyRaycastMesh : public RaycastMesh, public NodeInterface return mRoot->mBounds.mMax; } - virtual NodeAABB * getNode(void) + virtual NodeAABB * getNode(void) { assert( mNodeCount < mMaxNodeCount ); NodeAABB *ret = &mNodes[mNodeCount]; @@ -820,7 +854,7 @@ class MyRaycastMesh : public RaycastMesh, public NodeInterface return ret; } - virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) + virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) { if ( mFaceNormals == NULL ) { @@ -938,6 +972,29 @@ RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in t ) { auto m = new MyRaycastMesh(vcount, vertices, tcount, indices, maxDepth, minLeafSize, minAxisSize); + + // Calculate memory usage + size_t vertex_size = vcount * sizeof(RmReal) * 3; // Each vertex has 3 floats + size_t index_size = tcount * 3 * sizeof(RmUint32); // Each triangle has 3 indices + size_t bvh_node_size = m->mNodeCount * sizeof(NodeAABB); // BVH Node memory usage + size_t bvh_leaf_size = m->mLeafTriangles.size() * sizeof(RmUint32); // BVH leaf triangles + + size_t bvh_size = bvh_node_size + bvh_leaf_size; // Total BVH size + size_t total_size = vertex_size + index_size + bvh_size; + + KSM::CheckPageAlignment(m->mNodes); + KSM::CheckPageAlignment(m->mVertices); + + LogInfo( + "Map Raycast Memory Usage | Vertices [{:.2f}] MB Indices [{:.2f}] MB BVH Nodes [{:.2f}] MB BVH Leaves [{:.2f}] MB BVH Total [{:.2f}] MB", + vertex_size / (1024.0 * 1024.0), + index_size / (1024.0 * 1024.0), + bvh_node_size / (1024.0 * 1024.0), + bvh_leaf_size / (1024.0 * 1024.0), + bvh_size / (1024.0 * 1024.0) + ); + LogInfo("Total Raycast Memory [{:.2f}] MB", total_size / (1024.0 * 1024.0)); + return static_cast< RaycastMesh * >(m); } @@ -984,12 +1041,12 @@ MyRaycastMesh::MyRaycastMesh(std::vector& rm_buffer) return; char* buf = rm_buffer.data(); - + chunk_size = sizeof(RmUint32); memcpy(&mVcount, buf, chunk_size); buf += chunk_size; bytes_read += chunk_size; - + chunk_size = (sizeof(RmReal) * (3 * mVcount)); mVertices = (RmReal *)::malloc(chunk_size); memcpy(mVertices, buf, chunk_size); @@ -1037,7 +1094,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector& rm_buffer) buf += chunk_size; bytes_read += chunk_size; } - + chunk_size = sizeof(RmUint32); memcpy(&mNodeCount, buf, chunk_size); buf += chunk_size; @@ -1071,7 +1128,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector& rm_buffer) mNodes[index].mLeft = &mNodes[lNodeIndex]; buf += chunk_size; bytes_read += chunk_size; - + RmUint32 rNodeIndex; chunk_size = sizeof(RmUint32); memcpy(&rNodeIndex, buf, chunk_size); @@ -1106,7 +1163,7 @@ MyRaycastMesh::MyRaycastMesh(std::vector& rm_buffer) void MyRaycastMesh::serialize(std::vector& rm_buffer) { rm_buffer.clear(); - + size_t rm_buffer_size_ = 0; rm_buffer_size_ += sizeof(RmUint32); // mVcount From 37ced4b003c62c10f4d57d84c7e4ff8ddb1b5a98 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 21 Jan 2025 16:06:18 -0600 Subject: [PATCH 61/73] [Databuckets] Add Account Scoped Databuckets (#4603) * [Databuckets] Add Account Scoped Databuckets * Add variation * Fix Lua after testing --- common/database/database_update_manifest.cpp | 19 ++++++- .../base/base_data_buckets_repository.h | 37 +++++++++----- zone/client.cpp | 50 ++++++++++++++++++- zone/client.h | 7 +++ zone/data_bucket.cpp | 49 +++++++++++++----- zone/data_bucket.h | 9 ++-- zone/lua_client.cpp | 42 ++++++++++++++++ zone/lua_client.h | 8 +++ zone/perl_client.cpp | 36 +++++++++++++ 9 files changed, 228 insertions(+), 29 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index be7f01e143..582864c434 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6301,7 +6301,24 @@ ALTER TABLE zone DROP COLUMN IF EXISTS npc_update_range; ALTER TABLE zone DROP COLUMN IF EXISTS max_movement_update_range; ALTER TABLE `zone` ADD COLUMN `client_update_range` int(11) NOT NULL DEFAULT 600 AFTER `npc_max_aggro_dist`; )", - .content_schema_update = true + .content_schema_update = true, + }, + ManifestEntry{ + .version = 9292, + .description = "2025_01_21_data_buckets_account_id", + .check = "SHOW COLUMNS FROM `data_buckets` LIKE 'account_id'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `data_buckets` +ADD COLUMN `account_id` bigint(11) NULL DEFAULT 0 AFTER `expires`, +DROP INDEX `keys`, +ADD UNIQUE INDEX `keys` (`key`, `character_id`, `npc_id`, `bot_id`, `account_id`) USING BTREE; + +-- Add the INDEX for character_id and key +ALTER TABLE `data_buckets` ADD KEY `idx_account_id_key` (`account_id`, `key`); +)", + .content_schema_update = false }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/repositories/base/base_data_buckets_repository.h b/common/repositories/base/base_data_buckets_repository.h index 3e7ca05d08..42c85336c8 100644 --- a/common/repositories/base/base_data_buckets_repository.h +++ b/common/repositories/base/base_data_buckets_repository.h @@ -23,6 +23,7 @@ class BaseDataBucketsRepository { std::string key_; std::string value; uint32_t expires; + int64_t account_id; int64_t character_id; int64_t npc_id; int64_t bot_id; @@ -36,6 +37,7 @@ class BaseDataBucketsRepository { CEREAL_NVP(key_), CEREAL_NVP(value), CEREAL_NVP(expires), + CEREAL_NVP(account_id), CEREAL_NVP(character_id), CEREAL_NVP(npc_id), CEREAL_NVP(bot_id) @@ -55,6 +57,7 @@ class BaseDataBucketsRepository { "`key`", "value", "expires", + "account_id", "character_id", "npc_id", "bot_id", @@ -68,6 +71,7 @@ class BaseDataBucketsRepository { "`key`", "value", "expires", + "account_id", "character_id", "npc_id", "bot_id", @@ -115,6 +119,7 @@ class BaseDataBucketsRepository { e.key_ = ""; e.value = ""; e.expires = 0; + e.account_id = 0; e.character_id = 0; e.npc_id = 0; e.bot_id = 0; @@ -158,9 +163,10 @@ class BaseDataBucketsRepository { e.key_ = row[1] ? row[1] : ""; e.value = row[2] ? row[2] : ""; e.expires = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; - e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; - e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; + e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0; return e; } @@ -197,9 +203,10 @@ class BaseDataBucketsRepository { v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'"); v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'"); v.push_back(columns[3] + " = " + std::to_string(e.expires)); - v.push_back(columns[4] + " = " + std::to_string(e.character_id)); - v.push_back(columns[5] + " = " + std::to_string(e.npc_id)); - v.push_back(columns[6] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[4] + " = " + std::to_string(e.account_id)); + v.push_back(columns[5] + " = " + std::to_string(e.character_id)); + v.push_back(columns[6] + " = " + std::to_string(e.npc_id)); + v.push_back(columns[7] + " = " + std::to_string(e.bot_id)); auto results = db.QueryDatabase( fmt::format( @@ -225,6 +232,7 @@ class BaseDataBucketsRepository { v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back(std::to_string(e.expires)); + v.push_back(std::to_string(e.account_id)); v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.bot_id)); @@ -261,6 +269,7 @@ class BaseDataBucketsRepository { v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back(std::to_string(e.expires)); + v.push_back(std::to_string(e.account_id)); v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.bot_id)); @@ -301,9 +310,10 @@ class BaseDataBucketsRepository { e.key_ = row[1] ? row[1] : ""; e.value = row[2] ? row[2] : ""; e.expires = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; - e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; - e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; + e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0; all_entries.push_back(e); } @@ -332,9 +342,10 @@ class BaseDataBucketsRepository { e.key_ = row[1] ? row[1] : ""; e.value = row[2] ? row[2] : ""; e.expires = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.character_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; - e.npc_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; - e.bot_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.account_id = row[4] ? strtoll(row[4], nullptr, 10) : 0; + e.character_id = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.npc_id = row[6] ? strtoll(row[6], nullptr, 10) : 0; + e.bot_id = row[7] ? strtoll(row[7], nullptr, 10) : 0; all_entries.push_back(e); } @@ -413,6 +424,7 @@ class BaseDataBucketsRepository { v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back(std::to_string(e.expires)); + v.push_back(std::to_string(e.account_id)); v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.bot_id)); @@ -442,6 +454,7 @@ class BaseDataBucketsRepository { v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back(std::to_string(e.expires)); + v.push_back(std::to_string(e.account_id)); v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.bot_id)); diff --git a/zone/client.cpp b/zone/client.cpp index 667142f30c..697d7c752a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -402,6 +402,7 @@ Client::~Client() { mMovementManager->RemoveClient(this); + DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID()); DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID()); if (RuleB(Bots, Enabled)) { @@ -13172,10 +13173,57 @@ void Client::BroadcastPositionUpdate() Group *g = GetGroup(); if (g) { - for (auto & m : g->members) { + for (auto &m: g->members) { if (m && m->IsClient() && m != this) { m->CastToClient()->QueuePacket(&outapp); } } } } + +std::string Client::GetAccountBucket(std::string bucket_name) +{ + DataBucketKey k = {}; + k.account_id = AccountID(); + k.key = bucket_name; + + return DataBucket::GetData(k).value; +} + +void Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration) +{ + DataBucketKey k = {}; + k.account_id = AccountID(); + k.key = bucket_name; + k.expires = expiration; + k.value = bucket_value; + + DataBucket::SetData(k); +} + +void Client::DeleteAccountBucket(std::string bucket_name) +{ + DataBucketKey k = {}; + k.account_id = AccountID(); + k.key = bucket_name; + + DataBucket::DeleteData(k); +} + +std::string Client::GetAccountBucketExpires(std::string bucket_name) +{ + DataBucketKey k = {}; + k.account_id = AccountID(); + k.key = bucket_name; + + return DataBucket::GetDataExpires(k); +} + +std::string Client::GetAccountBucketRemaining(std::string bucket_name) +{ + DataBucketKey k = {}; + k.account_id = AccountID(); + k.key = bucket_name; + + return DataBucket::GetDataRemaining(k); +} diff --git a/zone/client.h b/zone/client.h index 3ec680a879..146cabd34d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1829,6 +1829,13 @@ class Client : public Mob void SendEvolveXPTransferWindow(); void SendEvolveTransferResults(const EQ::ItemInstance &inst_from, const EQ::ItemInstance &inst_to, const EQ::ItemInstance &inst_from_new, const EQ::ItemInstance &inst_to_new, const uint32 compatibility, const uint32 max_transfer_level); + // Account buckets + std::string GetAccountBucket(std::string bucket_name); + void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = ""); + void DeleteAccountBucket(std::string bucket_name); + std::string GetAccountBucketExpires(std::string bucket_name); + std::string GetAccountBucketRemaining(std::string bucket_name); + protected: friend class Mob; void CalcEdibleBonuses(StatBonuses* newbon); diff --git a/zone/data_bucket.cpp b/zone/data_bucket.cpp index db2222348d..23d4eb9d6f 100644 --- a/zone/data_bucket.cpp +++ b/zone/data_bucket.cpp @@ -16,6 +16,7 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke .key = bucket_key, .value = bucket_value, .expires = expires_time, + .account_id = 0, .character_id = 0, .npc_id = 0, .bot_id = 0 @@ -37,6 +38,9 @@ void DataBucket::SetData(const DataBucketKey &k) if (k.character_id > 0) { b.character_id = k.character_id; } + else if (k.account_id > 0) { + b.account_id = k.account_id; + } else if (k.npc_id > 0) { b.npc_id = k.npc_id; } @@ -95,9 +99,10 @@ std::string DataBucket::GetData(const std::string &bucket_key) DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, bool ignore_misses_cache) { LogDataBuckets( - "Getting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}]", + "Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]", k.key, k.bot_id, + k.account_id, k.character_id, k.npc_id ); @@ -151,6 +156,7 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b .key_ = k.key, .value = "", .expires = 0, + .account_id = k.account_id, .character_id = k.character_id, .npc_id = k.npc_id, .bot_id = k.bot_id @@ -158,8 +164,9 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, b ); LogDataBuckets( - "Key [{}] not found in database, adding to cache as a miss character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]", + "Key [{}] not found in database, adding to cache as a miss account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]", k.key, + k.account_id, k.character_id, k.npc_id, k.bot_id, @@ -216,8 +223,6 @@ bool DataBucket::DeleteData(const std::string &bucket_key) // GetDataBuckets bulk loads all data buckets for a mob bool DataBucket::GetDataBuckets(Mob *mob) { - DataBucketLoadType::Type t{}; - const uint32 id = mob->GetMobTypeIdentifier(); if (!id) { @@ -225,14 +230,13 @@ bool DataBucket::GetDataBuckets(Mob *mob) } if (mob->IsBot()) { - t = DataBucketLoadType::Bot; + BulkLoadEntitiesToCache(DataBucketLoadType::Bot, {id}); } else if (mob->IsClient()) { - t = DataBucketLoadType::Client; + BulkLoadEntitiesToCache(DataBucketLoadType::Account, {id}); + BulkLoadEntitiesToCache(DataBucketLoadType::Client, {id}); } - BulkLoadEntitiesToCache(t, {id}); - return true; } @@ -254,9 +258,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k) ); LogDataBuckets( - "Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]", + "Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]", k.key, k.bot_id, + k.account_id, k.character_id, k.npc_id, size_before, @@ -277,9 +282,10 @@ bool DataBucket::DeleteData(const DataBucketKey &k) std::string DataBucket::GetDataExpires(const DataBucketKey &k) { LogDataBuckets( - "Getting bucket expiration key [{}] bot_id [{}] character_id [{}] npc_id [{}]", + "Getting bucket expiration key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]", k.key, k.bot_id, + k.account_id, k.character_id, k.npc_id ); @@ -295,9 +301,10 @@ std::string DataBucket::GetDataExpires(const DataBucketKey &k) std::string DataBucket::GetDataRemaining(const DataBucketKey &k) { LogDataBuckets( - "Getting bucket remaining key [{}] bot_id [{}] character_id [{}] npc_id [{}]", + "Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]", k.key, k.bot_id, + k.account_id, k.character_id, k.npc_id ); @@ -320,6 +327,13 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k) query.emplace_back("character_id = 0"); } + if (k.account_id > 0) { + query.emplace_back(fmt::format("account_id = {}", k.account_id)); + } + else { + query.emplace_back("account_id = 0"); + } + if (k.npc_id > 0) { query.emplace_back(fmt::format("npc_id = {}", k.npc_id)); } @@ -346,6 +360,7 @@ bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, return ( dbe.key_ == k.key && dbe.bot_id == k.bot_id && + dbe.account_id == k.account_id && dbe.character_id == k.character_id && dbe.npc_id == k.npc_id ); @@ -364,6 +379,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector if (t == DataBucketLoadType::Bot) { has_cache = e.bot_id == ids[0]; } + else if (t == DataBucketLoadType::Account) { + has_cache = e.account_id == ids[0]; + } else if (t == DataBucketLoadType::Client) { has_cache = e.character_id == ids[0]; } @@ -384,6 +402,9 @@ void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector case DataBucketLoadType::Client: column = "character_id"; break; + case DataBucketLoadType::Account: + column = "account_id"; + break; default: LogError("Incorrect LoadType [{}]", static_cast(t)); break; @@ -442,6 +463,7 @@ void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id) [&](DataBucketsRepository::DataBuckets &e) { return ( (type == DataBucketLoadType::Bot && e.bot_id == id) || + (type == DataBucketLoadType::Account && e.account_id == id) || (type == DataBucketLoadType::Client && e.character_id == id) ); } @@ -481,6 +503,7 @@ void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e) g_data_bucket_cache.end(), [&](DataBucketsRepository::DataBuckets &ce) { return ce.id == 0 && ce.key_ == e.key_ && + ce.account_id == e.account_id && ce.character_id == e.character_id && ce.npc_id == e.npc_id && ce.bot_id == e.bot_id; @@ -516,6 +539,8 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type) return e.bot_id == id; case DataBucketLoadType::Client: return e.character_id == id; + case DataBucketLoadType::Account: + return e.account_id == id; default: return false; } @@ -539,7 +564,7 @@ void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type) // npcs (ids) can be in multiple zones so we can't cache locally to the zone bool DataBucket::CanCache(const DataBucketKey &key) { - if (key.character_id > 0 || key.bot_id > 0) { + if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0) { return true; } diff --git a/zone/data_bucket.h b/zone/data_bucket.h index 8503330f23..1bd2166307 100644 --- a/zone/data_bucket.h +++ b/zone/data_bucket.h @@ -12,20 +12,23 @@ struct DataBucketKey { std::string key; std::string value; std::string expires; - int64_t character_id; - int64_t npc_id; - int64_t bot_id; + int64_t account_id = 0; + int64_t character_id = 0; + int64_t npc_id = 0; + int64_t bot_id = 0; }; namespace DataBucketLoadType { enum Type : uint8 { Bot, + Account, Client, MaxType }; static const std::string Name[Type::MaxType] = { "Bot", + "Account", "Client", }; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 566510cf9d..4be953bb40 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3464,6 +3464,42 @@ void Lua_Client::SetAAEXPPercentage(uint8 percentage) self->SetAAEXPPercentage(percentage); } +void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value) +{ + Lua_Safe_Call_Void(); + self->SetAccountBucket(bucket_name, bucket_value); +} + +void Lua_Client::SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration) +{ + Lua_Safe_Call_Void(); + self->SetAccountBucket(bucket_name, bucket_value, expiration); +} + +void Lua_Client::DeleteAccountBucket(std::string bucket_name) +{ + Lua_Safe_Call_Void(); + self->DeleteAccountBucket(bucket_name); +} + +std::string Lua_Client::GetAccountBucket(std::string bucket_name) +{ + Lua_Safe_Call_String(); + return self->GetAccountBucket(bucket_name); +} + +std::string Lua_Client::GetAccountBucketExpires(std::string bucket_name) +{ + Lua_Safe_Call_String(); + return self->GetAccountBucketExpires(bucket_name); +} + +std::string Lua_Client::GetAccountBucketRemaining(std::string bucket_name) +{ + Lua_Safe_Call_String(); + return self->GetAccountBucketRemaining(bucket_name); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3552,6 +3588,7 @@ luabind::scope lua_register_client() { .def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone) .def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID) .def("DescribeSpecialAbilities", (void(Lua_Client::*)(Lua_NPC))&Lua_Client::DescribeSpecialAbilities) + .def("DeleteAccountBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteAccountBucket) .def("DeleteBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteBucket) .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory) .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory) @@ -3630,6 +3667,9 @@ luabind::scope lua_register_client() { .def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel) .def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit) .def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotSpawnLimit) + .def("GetAccountBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucket) + .def("GetAccountBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketExpires) + .def("GetAccountBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountBucketRemaining) .def("GetBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucket) .def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires) .def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining) @@ -3922,6 +3962,8 @@ luabind::scope lua_register_client() { .def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel) .def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit) .def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit) + .def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountBucket) + .def("SetAccountBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetAccountBucket) .def("SetBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetBucket) .def("SetBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket) .def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel) diff --git a/zone/lua_client.h b/zone/lua_client.h index d86705f2e7..082504fc06 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -512,6 +512,14 @@ class Lua_Client : public Lua_Mob luabind::object GetInventorySlots(lua_State* L); void SetAAEXPPercentage(uint8 percentage); + // account data buckets + void SetAccountBucket(std::string bucket_name, std::string bucket_value); + void SetAccountBucket(std::string bucket_name, std::string bucket_value, std::string expiration = ""); + void DeleteAccountBucket(std::string bucket_name); + std::string GetAccountBucket(std::string bucket_name); + std::string GetAccountBucketExpires(std::string bucket_name); + std::string GetAccountBucketRemaining(std::string bucket_name); + void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); void ApplySpell(int spell_id, int duration, int level); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index d86c70bf40..c4a66ec2d3 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3234,6 +3234,36 @@ void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage) self->SetAAEXPPercentage(percentage); } +void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value) +{ + self->SetAccountBucket(bucket_name, bucket_value); +} + +void Perl_Client_SetAccountBucket(Client* self, std::string bucket_name, std::string bucket_value, std::string expiration = "") +{ + self->SetAccountBucket(bucket_name, bucket_value, expiration); +} + +void Perl_Client_DeleteAccountBucket(Client* self, std::string bucket_name) +{ + self->DeleteAccountBucket(bucket_name); +} + +std::string Perl_Client_GetAccountBucket(Client* self, std::string bucket_name) +{ + return self->GetAccountBucket(bucket_name); +} + +std::string Perl_Client_GetAccountBucketExpires(Client* self, std::string bucket_name) +{ + return self->GetAccountBucketExpires(bucket_name); +} + +std::string Perl_Client_GetAccountBucketRemaining(Client* self, std::string bucket_name) +{ + return self->GetAccountBucketRemaining(bucket_name); +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3325,6 +3355,7 @@ void perl_register_client() package.add("CreateTaskDynamicZone", &Perl_Client_CreateTaskDynamicZone); package.add("DecreaseByID", &Perl_Client_DecreaseByID); package.add("DescribeSpecialAbilities", &Perl_Client_DescribeSpecialAbilities); + package.add("DeleteAccountBucket", &Perl_Client_DeleteAccountBucket); package.add("DeleteItemInInventory", (void(*)(Client*, int16))&Perl_Client_DeleteItemInInventory); package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16))&Perl_Client_DeleteItemInInventory); package.add("DeleteItemInInventory", (void(*)(Client*, int16, int16, bool))&Perl_Client_DeleteItemInInventory); @@ -3362,6 +3393,9 @@ void perl_register_client() package.add("GetAAPoints", &Perl_Client_GetAAPoints); package.add("GetAFK", &Perl_Client_GetAFK); package.add("GetAccountAge", &Perl_Client_GetAccountAge); + package.add("GetAccountBucket", &Perl_Client_GetAccountBucket); + package.add("GetAccountBucketExpires", &Perl_Client_GetAccountBucketExpires); + package.add("GetGetAccountBucketRemaining", &Perl_Client_GetAccountBucketRemaining); package.add("GetAccountFlag", &Perl_Client_GetAccountFlag); package.add("GetAccountFlags", &Perl_Client_GetAccountFlags); package.add("GetAggroCount", &Perl_Client_GetAggroCount); @@ -3668,6 +3702,8 @@ void perl_register_client() package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle); package.add("SetAFK", &Perl_Client_SetAFK); package.add("SetAccountFlag", &Perl_Client_SetAccountFlag); + package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string))&Perl_Client_SetAccountBucket); + package.add("SetAccountBucket", (void(*)(Client*, std::string, std::string, std::string))&Perl_Client_SetAccountBucket); package.add("SetAlternateCurrencyValue", &Perl_Client_SetAlternateCurrencyValue); package.add("SetAnon", &Perl_Client_SetAnon); package.add("SetAutoLoginCharacterName", (bool(*)(Client*))&Perl_Client_SetAutoLoginCharacterName); From dba494cd8e644289c4a34bfff6495a82121270bf Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 21 Jan 2025 17:39:33 -0600 Subject: [PATCH 62/73] [Hotfix] Update database version to match manifest --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index 84163134f2..671a0c921d 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9291 +#define CURRENT_BINARY_DATABASE_VERSION 9292 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif From 90c37390f1b78323658971841849257edcdc29f3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 21 Jan 2025 18:48:02 -0600 Subject: [PATCH 63/73] [Hotfix] CLI help menu from parsing correctly in World --- world/cli/copy_character.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/world/cli/copy_character.cpp b/world/cli/copy_character.cpp index 86c2682170..ebe8e1831b 100644 --- a/world/cli/copy_character.cpp +++ b/world/cli/copy_character.cpp @@ -11,13 +11,13 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std "destination_account_name" }; std::vector options = {}; - - EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - + if (cmd[{"-h", "--help"}]) { return; } + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + std::string source_character_name = cmd(2).str(); std::string destination_character_name = cmd(3).str(); std::string destination_account_name = cmd(4).str(); From 0acad18067f4d7f33646f21bf672796a2c188754 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 21 Jan 2025 19:11:58 -0600 Subject: [PATCH 64/73] [CLI] Add --skip-backup to world database:updates (#4605) --- common/database/database_update.cpp | 12 +++++++++++- common/database/database_update.h | 3 +++ world/cli/database_dump.cpp | 4 ++-- world/cli/database_updates.cpp | 5 +++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/common/database/database_update.cpp b/common/database/database_update.cpp index cd33d2abfe..0542d130dd 100644 --- a/common/database/database_update.cpp +++ b/common/database/database_update.cpp @@ -169,7 +169,10 @@ bool DatabaseUpdate::UpdateManifest( LogSys.EnableMySQLErrorLogs(); LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH)); - if (!missing_migrations.empty()) { + if (!missing_migrations.empty() && m_skip_backup) { + LogInfo("Skipping database backup"); + } + else if (!missing_migrations.empty()) { LogInfo("Automatically backing up database before applying updates"); LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH)); auto s = DatabaseDumpService(); @@ -271,6 +274,13 @@ DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db) return this; } +DatabaseUpdate *DatabaseUpdate::SetSkipBackup(bool skip) +{ + m_skip_backup = skip; + + return this; +} + bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b) { LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH)); diff --git a/common/database/database_update.h b/common/database/database_update.h index 0ba7b8df5b..be820a9e8d 100644 --- a/common/database/database_update.h +++ b/common/database/database_update.h @@ -29,12 +29,15 @@ class DatabaseUpdate { DatabaseUpdate *SetDatabase(Database *db); DatabaseUpdate *SetContentDatabase(Database *db); + DatabaseUpdate *SetSkipBackup(bool skip); bool HasPendingUpdates(); private: + bool m_skip_backup = false; Database *m_database; Database *m_content_database; static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b); void InjectBotsVersionColumn(); + }; #endif //EQEMU_DATABASE_UPDATE_H diff --git a/world/cli/database_dump.cpp b/world/cli/database_dump.cpp index 0465396886..e9068d63b9 100644 --- a/world/cli/database_dump.cpp +++ b/world/cli/database_dump.cpp @@ -24,12 +24,12 @@ void WorldserverCLI::DatabaseDump(int argc, char **argv, argh::parser &cmd, std: "--compress" }; - EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - if (cmd[{"-h", "--help"}]) { return; } + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + auto s = new DatabaseDumpService(); bool dump_all = cmd[{"-a", "--all"}]; diff --git a/world/cli/database_updates.cpp b/world/cli/database_updates.cpp index f85316de58..f967349fa9 100644 --- a/world/cli/database_updates.cpp +++ b/world/cli/database_updates.cpp @@ -4,12 +4,17 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s { description = "Runs database updates manually"; + std::vector options = { + "--skip-backup", + }; + if (cmd[{"-h", "--help"}]) { return; } DatabaseUpdate update; update.SetDatabase(&database) + ->SetSkipBackup(cmd[{"--skip-backup"}] ) ->SetContentDatabase(&content_db) ->CheckDbUpdates(); } From 31abaf8016b52153e99b15ff7c804df4749a9aa6 Mon Sep 17 00:00:00 2001 From: catapultam-habeo <97849758+catapultam-habeo@users.noreply.github.com> Date: Wed, 22 Jan 2025 03:31:05 -0600 Subject: [PATCH 65/73] [Feature] Implement Custom Pet Names (#4594) * rebase\tidy up to address commends * I blame git for this one * last typo * spaces * formating fixes I think? * Repository fixes * Cleanup --------- Co-authored-by: Akkadius --- common/database.cpp | 7 + common/database/database_update_manifest.cpp | 13 + common/database_schema.h | 1 + common/emu_oplist.h | 3 + common/eq_packet_structs.h | 15 + .../base/base_character_pet_name_repository.h | 392 ++++++++++++++++++ .../character_pet_name_repository.h | 13 + common/version.h | 2 +- utils/patches/patch_RoF2.conf | 9 + zone/client.cpp | 96 ++++- zone/client.h | 6 + zone/client_packet.cpp | 27 ++ zone/lua_client.cpp | 7 + zone/lua_client.h | 1 + zone/perl_client.cpp | 6 + zone/pets.cpp | 7 + 16 files changed, 595 insertions(+), 10 deletions(-) create mode 100644 common/repositories/base/base_character_pet_name_repository.h create mode 100644 common/repositories/character_pet_name_repository.h diff --git a/common/database.cpp b/common/database.cpp index a3405970dd..f1d3a3757b 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -50,6 +50,7 @@ #include "../common/repositories/raid_members_repository.h" #include "../common/repositories/reports_repository.h" #include "../common/repositories/variables_repository.h" +#include "../common/repositories/character_pet_name_repository.h" #include "../common/events/player_event_logs.h" // Disgrace: for windows compile @@ -313,6 +314,12 @@ bool Database::ReserveName(uint32 account_id, const std::string& name) return false; } + const auto& p = CharacterPetNameRepository::GetWhere(*this, where_filter); + if (!p.empty()) { + LogInfo("Account [{}] requested name [{}] but name is already taken by an Pet", account_id, name); + return false; + } + auto e = CharacterDataRepository::NewEntity(); e.account_id = account_id; diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 582864c434..57819b700a 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6320,6 +6320,19 @@ ALTER TABLE `data_buckets` ADD KEY `idx_account_id_key` (`account_id`, `key`); )", .content_schema_update = false }, + ManifestEntry{ + .version = 9293, + .description = "2025_01_10_create_pet_names_table.sql", + .check = "SHOW TABLES LIKE 'character_pet_name'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `character_pet_name` ( + `character_id` INT(11) NOT NULL PRIMARY KEY, + `name` VARCHAR(64) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +)", + }, // -- 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 b86e16f448..9f23001b18 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -64,6 +64,7 @@ namespace DatabaseSchema { {"character_pet_buffs", "char_id"}, {"character_pet_info", "char_id"}, {"character_pet_inventory", "char_id"}, + {"character_pet_name", "character_id"}, {"character_peqzone_flags", "id"}, {"character_potionbelt", "id"}, {"character_skills", "id"}, diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 6b5ab12ab9..eefcdfee87 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -77,6 +77,7 @@ N(OP_CashReward), N(OP_CastSpell), N(OP_ChangeSize), N(OP_ChannelMessage), +N(OP_ChangePetName), N(OP_CharacterCreate), N(OP_CharacterCreateRequest), N(OP_CharInventory), @@ -284,6 +285,8 @@ N(OP_InspectMessageUpdate), N(OP_InspectRequest), N(OP_InstillDoubt), N(OP_InterruptCast), +N(OP_InvokeChangePetName), +N(OP_InvokeChangePetNameImmediate), N(OP_ItemLinkClick), N(OP_ItemLinkResponse), N(OP_ItemLinkText), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index bfe5abf442..f6e2b9ed1b 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5819,6 +5819,21 @@ struct ChangeSize_Struct /*16*/ }; +struct ChangePetName_Struct { +/*00*/ char new_pet_name[64]; +/*40*/ char pet_owner_name[64]; +/*80*/ int response_code; +}; + +enum ChangePetNameResponse : int { + Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name. + Accepted = 1, // 5976 Your request for a name change was successful. + Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes. + NotEligible = -4, // 5980 Your character is not eligible for a name change. + Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change. + Unhandled = -1 +}; + // New OpCode/Struct for SoD+ struct GroupMakeLeader_Struct { diff --git a/common/repositories/base/base_character_pet_name_repository.h b/common/repositories/base/base_character_pet_name_repository.h new file mode 100644 index 0000000000..59a8a572f2 --- /dev/null +++ b/common/repositories/base/base_character_pet_name_repository.h @@ -0,0 +1,392 @@ +/** + * 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_CHARACTER_PET_NAME_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_PET_NAME_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseCharacterPetNameRepository { +public: + struct CharacterPetName { + int32_t character_id; + std::string name; + }; + + static std::string PrimaryKey() + { + return std::string("character_id"); + } + + static std::vector Columns() + { + return { + "character_id", + "name", + }; + } + + static std::vector SelectColumns() + { + return { + "character_id", + "name", + }; + } + + 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("character_pet_name"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterPetName NewEntity() + { + CharacterPetName e{}; + + e.character_id = 0; + e.name = ""; + + return e; + } + + static CharacterPetName GetCharacterPetName( + const std::vector &character_pet_names, + int character_pet_name_id + ) + { + for (auto &character_pet_name : character_pet_names) { + if (character_pet_name.character_id == character_pet_name_id) { + return character_pet_name; + } + } + + return NewEntity(); + } + + static CharacterPetName FindOne( + Database& db, + int character_pet_name_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + character_pet_name_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CharacterPetName e{}; + + e.character_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.name = row[1] ? row[1] : ""; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_pet_name_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_pet_name_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const CharacterPetName &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.character_id)); + v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.character_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterPetName InsertOne( + Database& db, + CharacterPetName e + ) + { + std::vector v; + + v.push_back(std::to_string(e.character_id)); + v.push_back("'" + Strings::Escape(e.name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.character_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.character_id)); + v.push_back("'" + Strings::Escape(e.name) + "'"); + + 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) { + CharacterPetName e{}; + + e.character_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.name = row[1] ? row[1] : ""; + + 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) { + CharacterPetName e{}; + + e.character_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.name = row[1] ? row[1] : ""; + + 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 CharacterPetName &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.character_id)); + v.push_back("'" + Strings::Escape(e.name) + "'"); + + 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.character_id)); + v.push_back("'" + Strings::Escape(e.name) + "'"); + + 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_CHARACTER_PET_NAME_REPOSITORY_H diff --git a/common/repositories/character_pet_name_repository.h b/common/repositories/character_pet_name_repository.h new file mode 100644 index 0000000000..8fbb1e94bf --- /dev/null +++ b/common/repositories/character_pet_name_repository.h @@ -0,0 +1,13 @@ +#ifndef EQEMU_CHARACTER_PET_NAME_REPOSITORY_H +#define EQEMU_CHARACTER_PET_NAME_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_character_pet_name_repository.h" + +class CharacterPetNameRepository: public BaseCharacterPetNameRepository { +public: + // Custom extended repository methods here +}; + +#endif //EQEMU_CHARACTER_PET_NAME_REPOSITORY_H diff --git a/common/version.h b/common/version.h index 671a0c921d..a99b965751 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9292 +#define CURRENT_BINARY_DATABASE_VERSION 9293 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 93d6f04637..a4e6d68ed9 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -735,3 +735,12 @@ OP_PickZone=0xaaba #evolve item related OP_EvolveItem=0x7cfb + +# This is bugged in ROF2 +# OP_InvokeChangePetNameImmediate is supposed to write DisablePetNameChangeReminder=0 to reset the 'nag reminder' +# It actually sets DisableNameChangeReminder=0 (player name change nag reminder). Additionally, clicking the +# 'Don't remind me later' button sets DisableNameChangeReminder=1 +# This can be fixed with a client patch. +OP_InvokeChangePetNameImmediate=0x046d +OP_InvokeChangePetName=0x4506 +OP_ChangePetName=0x5dab \ No newline at end of file diff --git a/zone/client.cpp b/zone/client.cpp index 697d7c752a..1c577c08d2 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -66,6 +66,7 @@ extern volatile bool RunLoops; #include "../common/repositories/character_spells_repository.h" #include "../common/repositories/character_disciplines_repository.h" #include "../common/repositories/character_data_repository.h" +#include "../common/repositories/character_pet_name_repository.h" #include "../common/repositories/discovered_items_repository.h" #include "../common/repositories/inventory_repository.h" #include "../common/repositories/keyring_repository.h" @@ -1828,7 +1829,7 @@ void Client::FriendsWho(char *FriendsString) { Message(0, "Error: World server disconnected"); else { auto pack = - new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString)); + new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString)); ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer; FriendsWho->FromID = GetID(); strcpy(FriendsWho->FromName, GetName()); @@ -2209,7 +2210,7 @@ void Client::Stand() { } void Client::Sit() { - SetAppearance(eaSitting, false); + SetAppearance(eaSitting, false); } void Client::ChangeLastName(std::string last_name) { @@ -4392,6 +4393,83 @@ void Client::KeyRingList() } } +bool Client::IsPetNameChangeAllowed() { + DataBucketKey k = GetScopedBucketKeys(); + k.key = "PetNameChangesAllowed"; + + auto b = DataBucket::GetData(k); + if (!b.value.empty()) { + return true; + } + + return false; +} + +void Client::InvokeChangePetName(bool immediate) { + if (!IsPetNameChangeAllowed()) { + return; + } + + auto packet_op = immediate ? OP_InvokeChangePetNameImmediate : OP_InvokeChangePetName; + + auto outapp = new EQApplicationPacket(packet_op, 0); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::GrantPetNameChange() { + DataBucketKey k = GetScopedBucketKeys(); + k.key = "PetNameChangesAllowed"; + k.value = "true"; + DataBucket::SetData(k); + + InvokeChangePetName(true); +} + +void Client::ClearPetNameChange() { + DataBucketKey k = GetScopedBucketKeys(); + k.key = "PetNameChangesAllowed"; + + DataBucket::DeleteData(k); +} + +bool Client::ChangePetName(std::string new_name) +{ + if (new_name.empty()) { + return false; + } + + if (!IsPetNameChangeAllowed()) { + return false; + } + + auto pet = GetPet(); + if (!pet) { + return false; + } + + if (pet->GetName() == new_name) { + return false; + } + + if (!database.CheckNameFilter(new_name) || database.IsNameUsed(new_name)) { + return false; + } + + CharacterPetNameRepository::ReplaceOne( + database, + CharacterPetNameRepository::CharacterPetName{ + .character_id = static_cast(CharacterID()), + .name = new_name + } + ); + + pet->TempName(new_name.c_str()); + + ClearPetNameChange(); + return true; +} + bool Client::IsDiscovered(uint32 item_id) { const auto& l = DiscoveredItemsRepository::GetWhere( database, @@ -5628,13 +5706,13 @@ bool Client::TryReward(uint32 claim_id) if (amt == 1) { query = StringFormat("DELETE FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); } else { query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); auto results = database.QueryDatabase(query); } @@ -8367,7 +8445,7 @@ int Client::GetQuiverHaste(int delay) for (int r = EQ::invslot::GENERAL_BEGIN; r <= EQ::invslot::GENERAL_END; r++) { pi = GetInv().GetItem(r); if (pi && pi->IsClassBag() && pi->GetItem()->BagType == EQ::item::BagTypeQuiver && - pi->GetItem()->BagWR > 0) + pi->GetItem()->BagWR > 0) break; if (r == EQ::invslot::GENERAL_END) // we will get here if we don't find a valid quiver @@ -9841,7 +9919,7 @@ const ExpeditionLockoutTimer* Client::GetExpeditionLockout( for (const auto& expedition_lockout : m_expedition_lockouts) { if ((include_expired || !expedition_lockout.IsExpired()) && - expedition_lockout.IsSameLockout(expedition_name, event_name)) + expedition_lockout.IsSameLockout(expedition_name, event_name)) { return &expedition_lockout; } @@ -9856,7 +9934,7 @@ std::vector Client::GetExpeditionLockouts( for (const auto& lockout : m_expedition_lockouts) { if ((include_expired || !lockout.IsExpired()) && - lockout.GetExpeditionName() == expedition_name) + lockout.GetExpeditionName() == expedition_name) { lockouts.emplace_back(lockout); } diff --git a/zone/client.h b/zone/client.h index 146cabd34d..d7a61f6622 100644 --- a/zone/client.h +++ b/zone/client.h @@ -307,6 +307,11 @@ class Client : public Mob void KeyRingAdd(uint32 item_id); bool KeyRingCheck(uint32 item_id); void KeyRingList(); + bool IsPetNameChangeAllowed(); + void GrantPetNameChange(); + void ClearPetNameChange(); + void InvokeChangePetName(bool immediate = true); + bool ChangePetName(std::string new_name); bool IsClient() const override { return true; } bool IsOfClientBot() const override { return true; } bool IsOfClientBotMerc() const override { return true; } @@ -2267,6 +2272,7 @@ class Client : public Mob const std::string &GetMailKeyFull() const; const std::string &GetMailKey() const; void ShowZoneShardMenu(); + void Handle_OP_ChangePetName(const EQApplicationPacket *app); }; #endif diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 066cb49cf5..95a9a970ec 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -66,6 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/repositories/character_corpses_repository.h" #include "../common/repositories/guild_tributes_repository.h" #include "../common/repositories/buyer_buy_lines_repository.h" +#include "../common/repositories/character_pet_name_repository.h" #include "../common/events/player_event_logs.h" #include "../common/repositories/character_stats_record_repository.h" @@ -160,6 +161,7 @@ void MapOpcodes() ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; + ConnectedOpcodes[OP_ChangePetName] = &Client::Handle_OP_ChangePetName; ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; @@ -820,6 +822,10 @@ void Client::CompleteConnect() CharacterID() ) ); + + if (IsPetNameChangeAllowed()) { + InvokeChangePetName(false); + } } if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) { @@ -4568,6 +4574,27 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) return; } +void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) { + if (app->size != sizeof(ChangePetName_Struct)) { + LogError("Got OP_ChangePetName of incorrect size. Expected [{}], got [{}].", sizeof(ChangePetName_Struct), app->size); + return; + } + + auto p = (ChangePetName_Struct *) app->pBuffer; + if (!IsPetNameChangeAllowed()) { + p->response_code = ChangePetNameResponse::NotEligible; + QueuePacket(app); + return; + } + + p->response_code = ChangePetNameResponse::Denied; + if (ChangePetName(p->new_pet_name)) { + p->response_code = ChangePetNameResponse::Accepted; + } + + QueuePacket(app); +} + void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) { if (!RuleB(Spells, EnableBlockedBuffs)) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 4be953bb40..83e2a3fb3e 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3458,6 +3458,12 @@ void Lua_Client::ShowZoneShardMenu() self->ShowZoneShardMenu(); } +void Lua_Client::GrantPetNameChange() +{ + Lua_Safe_Call_Void(); + self->GrantPetNameChange(); +} + void Lua_Client::SetAAEXPPercentage(uint8 percentage) { Lua_Safe_Call_Void(); @@ -3568,6 +3574,7 @@ luabind::scope lua_register_client() { .def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill) .def("CashReward", &Lua_Client::CashReward) .def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName) + .def("GrantPetNameChange", &Lua_Client::GrantPetNameChange) .def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID) .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill) .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill) diff --git a/zone/lua_client.h b/zone/lua_client.h index 082504fc06..ed02e65927 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -597,6 +597,7 @@ class Lua_Client : public Lua_Mob bool ReloadDataBuckets(); void ShowZoneShardMenu(); + void GrantPetNameChange(); Lua_Expedition CreateExpedition(luabind::object expedition_info); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index c4a66ec2d3..3cf1ad0d75 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3229,6 +3229,11 @@ perl::array Perl_Client_GetInventorySlots(Client* self) return result; } +void Perl_Client_GrantPetNameChange(Client* self) +{ + self->GrantPetNameChange(); +} + void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage) { self->SetAAEXPPercentage(percentage); @@ -3335,6 +3340,7 @@ void perl_register_client() package.add("CanHaveSkill", &Perl_Client_CanHaveSkill); package.add("CashReward", &Perl_Client_CashReward); package.add("ChangeLastName", &Perl_Client_ChangeLastName); + package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange); package.add("CharacterID", &Perl_Client_CharacterID); package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill); package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill); diff --git a/zone/pets.cpp b/zone/pets.cpp index 08fc523c15..da84ca4e14 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -22,6 +22,7 @@ #include "../common/repositories/pets_repository.h" #include "../common/repositories/pets_beastlord_data_repository.h" +#include "../common/repositories/character_pet_name_repository.h" #include "entity.h" #include "client.h" @@ -164,6 +165,12 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, // 4 - Keep DB name // 5 - `s ward + if (IsClient() && !petname) { + const auto vanity_name = CharacterPetNameRepository::FindOne(database, CastToClient()->CharacterID()); + if (!vanity_name.name.empty()) { + petname = vanity_name.name.c_str(); + } + } if (petname != nullptr) { // Name was provided, use it. From f30371a76df1816866d4d764b9ac952342ede985 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:17:33 -0400 Subject: [PATCH 66/73] [Feature] Implement "Big Bags" --- common/database/database_update_manifest.cpp | 97 +++ common/emu_constants.h | 68 +- common/inventory_profile.cpp | 2 +- common/patches/rof2_limits.h | 130 ++-- common/patches/titanium_structs.h | 12 +- .../base/base_inventory_repository.h | 272 ++++---- .../base/base_sharedbank_repository.h | 560 +++++++++++++++ common/repositories/sharedbank_repository.h | 339 +-------- common/ruletypes.h | 1 + common/shareddb.cpp | 655 ++++++++---------- common/version.h | 2 +- .../generators/repository-generator.pl | 1 - world/client.cpp | 27 +- zone/client.cpp | 8 +- zone/client_packet.cpp | 52 +- zone/client_process.cpp | 17 +- zone/corpse.cpp | 4 +- zone/embparser_api.cpp | 38 +- zone/gm_commands/show/inventory.cpp | 8 +- zone/inventory.cpp | 54 +- zone/zonedb.h | 8 +- 21 files changed, 1349 insertions(+), 1006 deletions(-) create mode 100644 common/repositories/base/base_sharedbank_repository.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 57819b700a..2bb2de881d 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6333,6 +6333,103 @@ CREATE TABLE `character_pet_name` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; )", }, + ManifestEntry{ + .version = 9294, + .description = "2024_10_24_sharedbank_guid_primary_key.sql", + .check = "SHOW COLUMN FROM `sharedbank` LIKE 'guid'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `sharedbank` +CHANGE COLUMN `acctid` `account_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST, +CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `account_id`, +CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `slot_id`, +CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`, +CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`, +CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`, +CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`, +CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`, +CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`, +MODIFY COLUMN `charges` smallint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `item_id`, +ADD COLUMN `color` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `charges`, +ADD COLUMN `ornament_icon` int(11) UNSIGNED NOT NULL AFTER `custom_data`, +ADD COLUMN `ornament_idfile` int(11) UNSIGNED NOT NULL AFTER `ornament_icon`, +ADD COLUMN `ornament_hero_model` int(11) NOT NULL AFTER `ornament_idfile`, +ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_model`, +ADD PRIMARY KEY (`account_id`, `slot_id`); +)" + }, + ManifestEntry{ + .version = 9295, + .description = "2024_10_24_inventory_changes.sql", + .check = "SHOW COLUMN FROM `inventory` LIKE 'charid'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `inventory` +CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST, +CHANGE COLUMN `slotid` `slot_id` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`, +CHANGE COLUMN `itemid` `item_id` int(11) UNSIGNED NULL DEFAULT 0 AFTER `slot_id`, +CHANGE COLUMN `augslot1` `augment_one` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `color`, +CHANGE COLUMN `augslot2` `augment_two` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_one`, +CHANGE COLUMN `augslot3` `augment_three` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_two`, +CHANGE COLUMN `augslot4` `augment_four` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_three`, +CHANGE COLUMN `augslot5` `augment_five` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_four`, +CHANGE COLUMN `augslot6` `augment_six` mediumint(7) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`, +CHANGE COLUMN `ornamenticon` `ornament_icon` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `custom_data`, +CHANGE COLUMN `ornamentidfile` `ornament_idfile` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_icon`, +DROP PRIMARY KEY, +ADD PRIMARY KEY (`character_id`, `slot_id`) USING BTREE; +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 251) + 4010) WHERE `slot_id` BETWEEN 251 AND 260; -- Bag 1 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 261) + 4210) WHERE `slot_id` BETWEEN 261 AND 270; -- Bag 2 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 271) + 4410) WHERE `slot_id` BETWEEN 271 AND 280; -- Bag 3 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 281) + 4610) WHERE `slot_id` BETWEEN 281 AND 290; -- Bag 4 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 291) + 4810) WHERE `slot_id` BETWEEN 291 AND 300; -- Bag 5 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 301) + 5010) WHERE `slot_id` BETWEEN 301 AND 310; -- Bag 6 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 311) + 5210) WHERE `slot_id` BETWEEN 311 AND 320; -- Bag 7 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 321) + 5410) WHERE `slot_id` BETWEEN 321 AND 330; -- Bag 8 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 331) + 5610) WHERE `slot_id` BETWEEN 331 AND 340; -- Bag 9 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 341) + 5810) WHERE `slot_id` BETWEEN 341 AND 350; -- Bag 10 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 351) + 6010) WHERE `slot_id` BETWEEN 351 AND 360; -- Cursor Bag +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2040; -- Bank Bag 1 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2041) + 6410) WHERE `slot_id` BETWEEN 2041 AND 2050; -- Bank Bag 2 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2051) + 6610) WHERE `slot_id` BETWEEN 2051 AND 2060; -- Bank Bag 3 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2061) + 6810) WHERE `slot_id` BETWEEN 2061 AND 2070; -- Bank Bag 4 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2071) + 7010) WHERE `slot_id` BETWEEN 2071 AND 2080; -- Bank Bag 5 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2081) + 7210) WHERE `slot_id` BETWEEN 2081 AND 2090; -- Bank Bag 6 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2091) + 7410) WHERE `slot_id` BETWEEN 2091 AND 2100; -- Bank Bag 7 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2101) + 7610) WHERE `slot_id` BETWEEN 2101 AND 2110; -- Bank Bag 8 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2111) + 7810) WHERE `slot_id` BETWEEN 2111 AND 2120; -- Bank Bag 9 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2121) + 8010) WHERE `slot_id` BETWEEN 2121 AND 2130; -- Bank Bag 10 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2131) + 8210) WHERE `slot_id` BETWEEN 2131 AND 2140; -- Bank Bag 11 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2141) + 8410) WHERE `slot_id` BETWEEN 2141 AND 2150; -- Bank Bag 12 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2151) + 8610) WHERE `slot_id` BETWEEN 2151 AND 2160; -- Bank Bag 13 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2161) + 8810) WHERE `slot_id` BETWEEN 2161 AND 2170; -- Bank Bag 14 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2171) + 9010) WHERE `slot_id` BETWEEN 2171 AND 2180; -- Bank Bag 15 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2181) + 9210) WHERE `slot_id` BETWEEN 2181 AND 2190; -- Bank Bag 16 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2191) + 9410) WHERE `slot_id` BETWEEN 2191 AND 2200; -- Bank Bag 17 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2201) + 9610) WHERE `slot_id` BETWEEN 2201 AND 2210; -- Bank Bag 18 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2211) + 9810) WHERE `slot_id` BETWEEN 2211 AND 2220; -- Bank Bag 19 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2221) + 10010) WHERE `slot_id` BETWEEN 2221 AND 2230; -- Bank Bag 20 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2231) + 10210) WHERE `slot_id` BETWEEN 2231 AND 2240; -- Bank Bag 21 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2241) + 10410) WHERE `slot_id` BETWEEN 2241 AND 2250; -- Bank Bag 22 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` BETWEEN 2251 AND 2260; -- Bank Bag 23 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 +UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 +UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 +)" + }, + ManifestEntry{ + .version = 9296, + .description = "2024_10_24_merchantlist_temp_uncap.sql", + .check = "SHOW CREATE TABLE `merchantlist_temp`", + .condition = "contains", + .match = "`slot` tinyint(3)", + .sql = R"( +ALTER TABLE `merchantlist_temp` +MODIFY COLUMN `slot` int UNSIGNED NOT NULL DEFAULT 0 AFTER `npcid`; +)" + } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/emu_constants.h b/common/emu_constants.h index 7da8196d34..63361dbf31 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -132,7 +132,7 @@ namespace EQ using RoF2::invtype::KRONO_SIZE; using RoF2::invtype::OTHER_SIZE; - using Titanium::invtype::TRADE_NPC_SIZE; + using RoF2::invtype::TRADE_NPC_SIZE; using RoF2::invtype::TYPE_INVALID; using RoF2::invtype::TYPE_BEGIN; @@ -159,7 +159,7 @@ namespace EQ using RoF2::invslot::SLOT_INVALID; using RoF2::invslot::SLOT_BEGIN; - using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + using RoF2::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value) @@ -179,28 +179,28 @@ namespace EQ using RoF2::invslot::BONUS_STAT_END; using RoF2::invslot::BONUS_SKILL_END; - using Titanium::invslot::BANK_BEGIN; - using SoF::invslot::BANK_END; + using RoF2::invslot::BANK_BEGIN; + using RoF2::invslot::BANK_END; - using Titanium::invslot::SHARED_BANK_BEGIN; - using Titanium::invslot::SHARED_BANK_END; + using RoF2::invslot::SHARED_BANK_BEGIN; + using RoF2::invslot::SHARED_BANK_END; - using Titanium::invslot::TRADE_BEGIN; - using Titanium::invslot::TRADE_END; + using RoF2::invslot::TRADE_BEGIN; + using RoF2::invslot::TRADE_END; - using Titanium::invslot::TRADE_NPC_END; + using RoF2::invslot::TRADE_NPC_END; - using Titanium::invslot::WORLD_BEGIN; - using Titanium::invslot::WORLD_END; + using RoF2::invslot::WORLD_BEGIN; + using RoF2::invslot::WORLD_END; - using Titanium::invslot::TRIBUTE_BEGIN; - using Titanium::invslot::TRIBUTE_END; + using RoF2::invslot::TRIBUTE_BEGIN; + using RoF2::invslot::TRIBUTE_END; - using Titanium::invslot::GUILD_TRIBUTE_BEGIN; - using Titanium::invslot::GUILD_TRIBUTE_END; + using RoF2::invslot::GUILD_TRIBUTE_BEGIN; + using RoF2::invslot::GUILD_TRIBUTE_END; const int16 CORPSE_BEGIN = invslot::slotGeneral1; - const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor; + const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor; using RoF2::invslot::EQUIPMENT_BITMASK; using RoF2::invslot::GENERAL_BITMASK; @@ -214,38 +214,40 @@ namespace EQ } // namespace invslot namespace invbag { - using Titanium::invbag::SLOT_INVALID; - using Titanium::invbag::SLOT_BEGIN; - using Titanium::invbag::SLOT_END; - using Titanium::invbag::SLOT_COUNT; + using RoF2::invbag::SLOT_INVALID; + using RoF2::invbag::SLOT_BEGIN; + using RoF2::invbag::SLOT_END; + using RoF2::invbag::SLOT_COUNT; - using Titanium::invbag::GENERAL_BAGS_BEGIN; + using RoF2::invslot::WORLD_END; + + const int16 GENERAL_BAGS_BEGIN = WORLD_END + 1; const int16 GENERAL_BAGS_COUNT = invslot::GENERAL_COUNT * SLOT_COUNT; - const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1; + const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1; const int16 GENERAL_BAGS_8_COUNT = 8 * SLOT_COUNT; - const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1; + const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1; - const int16 CURSOR_BAG_BEGIN = 351; + const int16 CURSOR_BAG_BEGIN = GENERAL_BAGS_END + 1; const int16 CURSOR_BAG_COUNT = SLOT_COUNT; - const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1; + const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1; - using Titanium::invbag::BANK_BAGS_BEGIN; + const int16 BANK_BAGS_BEGIN = CURSOR_BAG_END + 1; const int16 BANK_BAGS_COUNT = (invtype::BANK_SIZE * SLOT_COUNT); - const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1; + const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1; const int16 BANK_BAGS_16_COUNT = 16 * SLOT_COUNT; - const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1; + const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1; - using Titanium::invbag::SHARED_BANK_BAGS_BEGIN; + const int16 SHARED_BANK_BAGS_BEGIN = BANK_BAGS_END + 1; const int16 SHARED_BANK_BAGS_COUNT = invtype::SHARED_BANK_SIZE * SLOT_COUNT; - const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1; + const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1; - using Titanium::invbag::TRADE_BAGS_BEGIN; + const int16 TRADE_BAGS_BEGIN = SHARED_BANK_BAGS_END + 1; const int16 TRADE_BAGS_COUNT = invtype::TRADE_SIZE * SLOT_COUNT; - const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1; + const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1; - using Titanium::invbag::GetInvBagIndexName; + using RoF2::invbag::GetInvBagIndexName; } // namespace invbag diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index d83c2ab167..603f4111b3 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -1037,7 +1037,7 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { int16 slot_id = INVALID_INDEX; - if (bagslot_id == invslot::slotCursor || bagslot_id == 8000) { + if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; } else if (bagslot_id >= invslot::GENERAL_BEGIN && bagslot_id <= invslot::GENERAL_END) { diff --git a/common/patches/rof2_limits.h b/common/patches/rof2_limits.h index a2fe623e09..4230be80d0 100644 --- a/common/patches/rof2_limits.h +++ b/common/patches/rof2_limits.h @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ namespace RoF2 const bool AllowOverLevelEquipment = true; - const bool AllowEmptyBagInBag = true; + const bool AllowEmptyBagInBag = true; const bool AllowClickCastFromBag = true; } /*inventory*/ @@ -77,38 +77,38 @@ namespace RoF2 } // namespace enum_ using namespace enum_; - const int16 POSSESSIONS_SIZE = 34; - const int16 BANK_SIZE = 24; - const int16 SHARED_BANK_SIZE = 2; - const int16 TRADE_SIZE = 8; - const int16 WORLD_SIZE = 10; - const int16 LIMBO_SIZE = 36; - const int16 TRIBUTE_SIZE = 5; - const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown - const int16 GUILD_TRIBUTE_SIZE = 2;//unverified - const int16 MERCHANT_SIZE = 200; - const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab" - const int16 CORPSE_SIZE = POSSESSIONS_SIZE; - const int16 BAZAAR_SIZE = 200; - const int16 INSPECT_SIZE = 23; - const int16 REAL_ESTATE_SIZE = 0;//unknown - const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE; - const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE; + const int16 POSSESSIONS_SIZE = 34; + const int16 BANK_SIZE = 24; + const int16 SHARED_BANK_SIZE = 2; + const int16 TRADE_SIZE = 8; + const int16 WORLD_SIZE = 10; + const int16 LIMBO_SIZE = 36; + const int16 TRIBUTE_SIZE = 5; + const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown + const int16 GUILD_TRIBUTE_SIZE = 2;//unverified + const int16 MERCHANT_SIZE = 500; + const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab" + const int16 CORPSE_SIZE = POSSESSIONS_SIZE; + const int16 BAZAAR_SIZE = 200; + const int16 INSPECT_SIZE = 23; + const int16 REAL_ESTATE_SIZE = 0;//unknown + const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE; + const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE; const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE; - const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE; - const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank" - const int16 ARCHIVED_SIZE = 0;//unknown - const int16 MAIL_SIZE = 0;//unknown + const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE; + const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank" + const int16 ARCHIVED_SIZE = 0;//unknown + const int16 MAIL_SIZE = 0;//unknown const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown - const int16 KRONO_SIZE = 0;//unknown - const int16 OTHER_SIZE = 0;//unknown + const int16 KRONO_SIZE = 0;//unknown + const int16 OTHER_SIZE = 0;//unknown const int16 TRADE_NPC_SIZE = 4; // defined by implication const int16 TYPE_INVALID = IINVALID; - const int16 TYPE_BEGIN = typePossessions; - const int16 TYPE_END = typeOther; - const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1; + const int16 TYPE_BEGIN = typePossessions; + const int16 TYPE_END = typeOther; + const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1; int16 GetInvTypeSize(int16 inv_type); const char* GetInvTypeName(int16 inv_type); @@ -162,33 +162,54 @@ namespace RoF2 } // namespace enum_ using namespace enum_; - const int16 SLOT_INVALID = IINVALID; - const int16 SLOT_BEGIN = INULL; + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + + const int16 BANK_BEGIN = 2000; + const int16 BANK_END = (BANK_BEGIN + invtype::BANK_SIZE) - 1; + + const int16 SHARED_BANK_BEGIN = 2500; + const int16 SHARED_BANK_END = (SHARED_BANK_BEGIN + invtype::SHARED_BANK_SIZE) - 1; + + const int16 TRADE_BEGIN = 3000; + const int16 TRADE_END = (TRADE_BEGIN + invtype::TRADE_SIZE) - 1; + + const int16 TRADE_NPC_END = (TRADE_BEGIN + invtype::TRADE_NPC_SIZE) - 1; // defined by implication + + const int16 WORLD_BEGIN = 4000; + const int16 WORLD_END = (WORLD_BEGIN + invtype::WORLD_SIZE) - 1; + + const int16 TRIBUTE_BEGIN = 400; + const int16 TRIBUTE_END = (TRIBUTE_BEGIN + invtype::TRIBUTE_SIZE) - 1; + + const int16 GUILD_TRIBUTE_BEGIN = 450; + const int16 GUILD_TRIBUTE_END = (GUILD_TRIBUTE_BEGIN + invtype::GUILD_TRIBUTE_SIZE) - 1; const int16 POSSESSIONS_BEGIN = slotCharm; - const int16 POSSESSIONS_END = slotCursor; + const int16 POSSESSIONS_END = slotCursor; const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; const int16 EQUIPMENT_BEGIN = slotCharm; - const int16 EQUIPMENT_END = slotAmmo; + const int16 EQUIPMENT_END = slotAmmo; const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1; const int16 GENERAL_BEGIN = slotGeneral1; - const int16 GENERAL_END = slotGeneral10; + const int16 GENERAL_END = slotGeneral10; const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1; - const int16 BONUS_BEGIN = invslot::slotCharm; - const int16 BONUS_STAT_END = invslot::slotPowerSource; + const int16 BONUS_BEGIN = invslot::slotCharm; + const int16 BONUS_STAT_END = invslot::slotPowerSource; const int16 BONUS_SKILL_END = invslot::slotAmmo; const int16 CORPSE_BEGIN = invslot::slotGeneral1; - const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; + const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; - const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; - const uint64 GENERAL_BITMASK = 0x00000001FF800000; - const uint64 CURSOR_BITMASK = 0x0000000200000000; + const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; + const uint64 GENERAL_BITMASK = 0x00000001FF800000; + const uint64 CURSOR_BITMASK = 0x0000000200000000; const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 34-slot count (RoF+) - const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) + const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) const char* GetInvPossessionsSlotName(int16 inv_slot); @@ -199,10 +220,21 @@ namespace RoF2 namespace invbag { inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::RoF2; } - const int16 SLOT_INVALID = IINVALID; - const int16 SLOT_BEGIN = INULL; - const int16 SLOT_END = 9; //254; - const int16 SLOT_COUNT = 10; //255; // server Size will be 255..unsure what actual client is (test) + const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000; + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + const int16 SLOT_COUNT = 200; + const int16 SLOT_END = SLOT_COUNT - 1; + + const int16 GENERAL_BAGS_BEGIN = 251; + + const int16 CURSOR_BAG_BEGIN = 351; + + const int16 BANK_BAGS_BEGIN = 2031; + + const int16 SHARED_BANK_BAGS_BEGIN = 2531; + + const int16 TRADE_BAGS_BEGIN = 3031; const char* GetInvBagIndexName(int16 bag_index); @@ -212,9 +244,9 @@ namespace RoF2 inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::RoF2; } const int16 SOCKET_INVALID = IINVALID; - const int16 SOCKET_BEGIN = INULL; - const int16 SOCKET_END = 5; - const int16 SOCKET_COUNT = 6; + const int16 SOCKET_BEGIN = INULL; + const int16 SOCKET_END = 5; + const int16 SOCKET_COUNT = 6; const char* GetInvAugIndexName(int16 aug_index); @@ -292,7 +324,7 @@ namespace RoF2 namespace spells { inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::RoF2; } - + enum class CastingSlot : uint32 { Gem1 = 0, Gem2 = 1, @@ -315,7 +347,7 @@ namespace RoF2 const int SPELL_ID_MAX = 45000; const int SPELLBOOK_SIZE = 720; const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); - + const int LONG_BUFFS = 42; const int SHORT_BUFFS = 20; const int DISC_BUFFS = 1; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 8311983639..2d92759425 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -2463,25 +2463,25 @@ struct WhoAllReturnStruct { struct BeginTrader_Struct { uint32 action; uint32 unknown04; - uint64 serial_number[80]; - uint32 cost[80]; + uint64 serial_number[EQ::invtype::BAZAAR_SIZE]; + uint32 cost[EQ::invtype::BAZAAR_SIZE]; }; struct Trader_Struct { uint32 action; uint32 unknown004; - uint64 item_id[80]; - uint32 item_cost[80]; + uint64 item_id[EQ::invtype::BAZAAR_SIZE]; + uint32 item_cost[EQ::invtype::BAZAAR_SIZE]; }; struct ClickTrader_Struct { uint32 code; uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE - uint32 itemcost[80]; + uint32 itemcost[EQ::invtype::BAZAAR_SIZE]; }; struct GetItems_Struct{ - uint32 items[80]; + uint32 items[EQ::invtype::BAZAAR_SIZE]; }; struct BecomeTrader_Struct { diff --git a/common/repositories/base/base_inventory_repository.h b/common/repositories/base/base_inventory_repository.h index a029ac6b41..9a4b5122b6 100644 --- a/common/repositories/base/base_inventory_repository.h +++ b/common/repositories/base/base_inventory_repository.h @@ -19,48 +19,48 @@ class BaseInventoryRepository { public: struct Inventory { - uint32_t charid; - uint32_t slotid; - uint32_t itemid; + uint32_t character_id; + uint32_t slot_id; + uint32_t item_id; uint16_t charges; uint32_t color; - uint32_t augslot1; - uint32_t augslot2; - uint32_t augslot3; - uint32_t augslot4; - uint32_t augslot5; - int32_t augslot6; + uint32_t augment_one; + uint32_t augment_two; + uint32_t augment_three; + uint32_t augment_four; + uint32_t augment_five; + uint32_t augment_six; uint8_t instnodrop; std::string custom_data; - uint32_t ornamenticon; - uint32_t ornamentidfile; + uint32_t ornament_icon; + uint32_t ornament_idfile; int32_t ornament_hero_model; uint64_t guid; }; static std::string PrimaryKey() { - return std::string("charid"); + return std::string("character_id"); } static std::vector Columns() { return { - "charid", - "slotid", - "itemid", + "character_id", + "slot_id", + "item_id", "charges", "color", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "instnodrop", "custom_data", - "ornamenticon", - "ornamentidfile", + "ornament_icon", + "ornament_idfile", "ornament_hero_model", "guid", }; @@ -69,21 +69,21 @@ class BaseInventoryRepository { static std::vector SelectColumns() { return { - "charid", - "slotid", - "itemid", + "character_id", + "slot_id", + "item_id", "charges", "color", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", "instnodrop", "custom_data", - "ornamenticon", - "ornamentidfile", + "ornament_icon", + "ornament_idfile", "ornament_hero_model", "guid", }; @@ -126,21 +126,21 @@ class BaseInventoryRepository { { Inventory e{}; - e.charid = 0; - e.slotid = 0; - e.itemid = 0; + e.character_id = 0; + e.slot_id = 0; + e.item_id = 0; e.charges = 0; e.color = 0; - e.augslot1 = 0; - e.augslot2 = 0; - e.augslot3 = 0; - e.augslot4 = 0; - e.augslot5 = 0; - e.augslot6 = 0; + e.augment_one = 0; + e.augment_two = 0; + e.augment_three = 0; + e.augment_four = 0; + e.augment_five = 0; + e.augment_six = 0; e.instnodrop = 0; e.custom_data = ""; - e.ornamenticon = 0; - e.ornamentidfile = 0; + e.ornament_icon = 0; + e.ornament_idfile = 0; e.ornament_hero_model = 0; e.guid = 0; @@ -153,7 +153,7 @@ class BaseInventoryRepository { ) { for (auto &inventory : inventorys) { - if (inventory.charid == inventory_id) { + if (inventory.character_id == inventory_id) { return inventory; } } @@ -179,21 +179,21 @@ class BaseInventoryRepository { if (results.RowCount() == 1) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -229,21 +229,21 @@ class BaseInventoryRepository { auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.charid)); - v.push_back(columns[1] + " = " + std::to_string(e.slotid)); - v.push_back(columns[2] + " = " + std::to_string(e.itemid)); + v.push_back(columns[0] + " = " + std::to_string(e.character_id)); + v.push_back(columns[1] + " = " + std::to_string(e.slot_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); v.push_back(columns[3] + " = " + std::to_string(e.charges)); v.push_back(columns[4] + " = " + std::to_string(e.color)); - v.push_back(columns[5] + " = " + std::to_string(e.augslot1)); - v.push_back(columns[6] + " = " + std::to_string(e.augslot2)); - v.push_back(columns[7] + " = " + std::to_string(e.augslot3)); - v.push_back(columns[8] + " = " + std::to_string(e.augslot4)); - v.push_back(columns[9] + " = " + std::to_string(e.augslot5)); - v.push_back(columns[10] + " = " + std::to_string(e.augslot6)); + v.push_back(columns[5] + " = " + std::to_string(e.augment_one)); + v.push_back(columns[6] + " = " + std::to_string(e.augment_two)); + v.push_back(columns[7] + " = " + std::to_string(e.augment_three)); + v.push_back(columns[8] + " = " + std::to_string(e.augment_four)); + v.push_back(columns[9] + " = " + std::to_string(e.augment_five)); + v.push_back(columns[10] + " = " + std::to_string(e.augment_six)); v.push_back(columns[11] + " = " + std::to_string(e.instnodrop)); v.push_back(columns[12] + " = '" + Strings::Escape(e.custom_data) + "'"); - v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon)); - v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile)); + v.push_back(columns[13] + " = " + std::to_string(e.ornament_icon)); + v.push_back(columns[14] + " = " + std::to_string(e.ornament_idfile)); v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model)); v.push_back(columns[16] + " = " + std::to_string(e.guid)); @@ -253,7 +253,7 @@ class BaseInventoryRepository { TableName(), Strings::Implode(", ", v), PrimaryKey(), - e.charid + e.character_id ) ); @@ -267,21 +267,21 @@ class BaseInventoryRepository { { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -294,7 +294,7 @@ class BaseInventoryRepository { ); if (results.Success()) { - e.charid = results.LastInsertedID(); + e.character_id = results.LastInsertedID(); return e; } @@ -313,21 +313,21 @@ class BaseInventoryRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -363,21 +363,21 @@ class BaseInventoryRepository { for (auto row = results.begin(); row != results.end(); ++row) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -404,21 +404,21 @@ class BaseInventoryRepository { for (auto row = results.begin(); row != results.end(); ++row) { Inventory e{}; - e.charid = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.slotid = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.itemid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.augslot1 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.augslot2 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.augslot3 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.augslot4 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.augslot5 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.augslot6 = row[10] ? static_cast(atoi(row[10])) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; @@ -495,21 +495,21 @@ class BaseInventoryRepository { { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); @@ -534,21 +534,21 @@ class BaseInventoryRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.charid)); - v.push_back(std::to_string(e.slotid)); - v.push_back(std::to_string(e.itemid)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.charges)); v.push_back(std::to_string(e.color)); - v.push_back(std::to_string(e.augslot1)); - v.push_back(std::to_string(e.augslot2)); - v.push_back(std::to_string(e.augslot3)); - v.push_back(std::to_string(e.augslot4)); - v.push_back(std::to_string(e.augslot5)); - v.push_back(std::to_string(e.augslot6)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); - v.push_back(std::to_string(e.ornamenticon)); - v.push_back(std::to_string(e.ornamentidfile)); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.guid)); diff --git a/common/repositories/base/base_sharedbank_repository.h b/common/repositories/base/base_sharedbank_repository.h new file mode 100644 index 0000000000..d208251da4 --- /dev/null +++ b/common/repositories/base/base_sharedbank_repository.h @@ -0,0 +1,560 @@ +/** + * 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_SHAREDBANK_REPOSITORY_H +#define EQEMU_BASE_SHAREDBANK_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseSharedbankRepository { +public: + struct Sharedbank { + uint32_t account_id; + uint32_t slot_id; + uint32_t item_id; + uint16_t charges; + uint32_t color; + uint32_t augment_one; + uint32_t augment_two; + uint32_t augment_three; + uint32_t augment_four; + uint32_t augment_five; + uint32_t augment_six; + std::string custom_data; + uint32_t ornament_icon; + uint32_t ornament_idfile; + int32_t ornament_hero_model; + uint64_t guid; + }; + + static std::string PrimaryKey() + { + return std::string("account_id"); + } + + static std::vector Columns() + { + return { + "account_id", + "slot_id", + "item_id", + "charges", + "color", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", + "custom_data", + "ornament_icon", + "ornament_idfile", + "ornament_hero_model", + "guid", + }; + } + + static std::vector SelectColumns() + { + return { + "account_id", + "slot_id", + "item_id", + "charges", + "color", + "augment_one", + "augment_two", + "augment_three", + "augment_four", + "augment_five", + "augment_six", + "custom_data", + "ornament_icon", + "ornament_idfile", + "ornament_hero_model", + "guid", + }; + } + + 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("sharedbank"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static Sharedbank NewEntity() + { + Sharedbank e{}; + + e.account_id = 0; + e.slot_id = 0; + e.item_id = 0; + e.charges = 0; + e.color = 0; + e.augment_one = 0; + e.augment_two = 0; + e.augment_three = 0; + e.augment_four = 0; + e.augment_five = 0; + e.augment_six = 0; + e.custom_data = ""; + e.ornament_icon = 0; + e.ornament_idfile = 0; + e.ornament_hero_model = 0; + e.guid = 0; + + return e; + } + + static Sharedbank GetSharedbank( + const std::vector &sharedbanks, + int sharedbank_id + ) + { + for (auto &sharedbank : sharedbanks) { + if (sharedbank.account_id == sharedbank_id) { + return sharedbank; + } + } + + return NewEntity(); + } + + static Sharedbank FindOne( + Database& db, + int sharedbank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + sharedbank_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int sharedbank_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + sharedbank_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const Sharedbank &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.account_id)); + v.push_back(columns[1] + " = " + std::to_string(e.slot_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); + v.push_back(columns[3] + " = " + std::to_string(e.charges)); + v.push_back(columns[4] + " = " + std::to_string(e.color)); + v.push_back(columns[5] + " = " + std::to_string(e.augment_one)); + v.push_back(columns[6] + " = " + std::to_string(e.augment_two)); + v.push_back(columns[7] + " = " + std::to_string(e.augment_three)); + v.push_back(columns[8] + " = " + std::to_string(e.augment_four)); + v.push_back(columns[9] + " = " + std::to_string(e.augment_five)); + v.push_back(columns[10] + " = " + std::to_string(e.augment_six)); + v.push_back(columns[11] + " = '" + Strings::Escape(e.custom_data) + "'"); + v.push_back(columns[12] + " = " + std::to_string(e.ornament_icon)); + v.push_back(columns[13] + " = " + std::to_string(e.ornament_idfile)); + v.push_back(columns[14] + " = " + std::to_string(e.ornament_hero_model)); + v.push_back(columns[15] + " = " + std::to_string(e.guid)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.account_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static Sharedbank InsertOne( + Database& db, + Sharedbank e + ) + { + std::vector v; + + v.push_back(std::to_string(e.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.account_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.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + 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) { + Sharedbank e{}; + + e.account_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.slot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.charges = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.color = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.augment_one = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.augment_two = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.augment_three = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.augment_four = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.augment_five = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.custom_data = row[11] ? row[11] : ""; + e.ornament_icon = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; + e.ornament_idfile = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornament_hero_model = row[14] ? static_cast(atoi(row[14])) : 0; + e.guid = row[15] ? strtoull(row[15], nullptr, 10) : 0; + + 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 Sharedbank &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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.account_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.charges)); + v.push_back(std::to_string(e.color)); + v.push_back(std::to_string(e.augment_one)); + v.push_back(std::to_string(e.augment_two)); + v.push_back(std::to_string(e.augment_three)); + v.push_back(std::to_string(e.augment_four)); + v.push_back(std::to_string(e.augment_five)); + v.push_back(std::to_string(e.augment_six)); + v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornament_icon)); + v.push_back(std::to_string(e.ornament_idfile)); + v.push_back(std::to_string(e.ornament_hero_model)); + v.push_back(std::to_string(e.guid)); + + 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_SHAREDBANK_REPOSITORY_H diff --git a/common/repositories/sharedbank_repository.h b/common/repositories/sharedbank_repository.h index 15243d83e2..d0aa47eafb 100644 --- a/common/repositories/sharedbank_repository.h +++ b/common/repositories/sharedbank_repository.h @@ -3,310 +3,47 @@ #include "../database.h" #include "../strings.h" +#include "base/base_sharedbank_repository.h" -class SharedbankRepository { +class SharedbankRepository: public BaseSharedbankRepository { public: - struct Sharedbank { - int acctid; - int slotid; - int itemid; - int16 charges; - int augslot1; - int augslot2; - int augslot3; - int augslot4; - int augslot5; - int augslot6; - std::string custom_data; - }; - static std::string PrimaryKey() - { - return std::string(""); - } - - static std::vector Columns() - { - return { - "acctid", - "slotid", - "itemid", - "charges", - "augslot1", - "augslot2", - "augslot3", - "augslot4", - "augslot5", - "augslot6", - "custom_data", - }; - } - - 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("sharedbank"); - } - - static std::string BaseSelect() - { - return fmt::format( - "SELECT {} FROM {}", - ColumnsRaw(), - TableName() - ); - } - - static std::string BaseInsert() - { - return fmt::format( - "INSERT INTO {} ({}) ", - TableName(), - InsertColumnsRaw() - ); - } - - static Sharedbank NewEntity() - { - Sharedbank entry{}; - - entry.acctid = 0; - entry.slotid = 0; - entry.itemid = 0; - entry.charges = 0; - entry.augslot1 = 0; - entry.augslot2 = 0; - entry.augslot3 = 0; - entry.augslot4 = 0; - entry.augslot5 = 0; - entry.augslot6 = 0; - entry.custom_data = 0; - - return entry; - } - - static Sharedbank GetSharedbankEntry( - const std::vector &sharedbanks, - int sharedbank_id - ) - { - for (auto &sharedbank : sharedbanks) { - if (sharedbank. == sharedbank_id) { - return sharedbank; - } - } - - return NewEntity(); - } - - static Sharedbank FindOne( - int sharedbank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "{} WHERE id = {} LIMIT 1", - BaseSelect(), - sharedbank_id - ) - ); - - auto row = results.begin(); - if (results.RowCount() == 1) { - Sharedbank entry{}; - - entry.acctid = atoi(row[0]); - entry.slotid = atoi(row[1]); - entry.itemid = atoi(row[2]); - entry.charges = atoi(row[3]); - entry.augslot1 = atoi(row[4]); - entry.augslot2 = atoi(row[5]); - entry.augslot3 = atoi(row[6]); - entry.augslot4 = atoi(row[7]); - entry.augslot5 = atoi(row[8]); - entry.augslot6 = atoi(row[9]); - entry.custom_data = row[10]; - - return entry; - } - - return NewEntity(); - } - - static int DeleteOne( - int sharedbank_id - ) - { - auto results = database.QueryDatabase( - fmt::format( - "DELETE FROM {} WHERE {} = {}", - TableName(), - PrimaryKey(), - sharedbank_id - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static int UpdateOne( - Sharedbank sharedbank_entry - ) - { - std::vector update_values; - - auto columns = Columns(); - - update_values.push_back(columns[0] + " = " + std::to_string(sharedbank_entry.acctid)); - update_values.push_back(columns[1] + " = " + std::to_string(sharedbank_entry.slotid)); - update_values.push_back(columns[2] + " = " + std::to_string(sharedbank_entry.itemid)); - update_values.push_back(columns[3] + " = " + std::to_string(sharedbank_entry.charges)); - update_values.push_back(columns[4] + " = " + std::to_string(sharedbank_entry.augslot1)); - update_values.push_back(columns[5] + " = " + std::to_string(sharedbank_entry.augslot2)); - update_values.push_back(columns[6] + " = " + std::to_string(sharedbank_entry.augslot3)); - update_values.push_back(columns[7] + " = " + std::to_string(sharedbank_entry.augslot4)); - update_values.push_back(columns[8] + " = " + std::to_string(sharedbank_entry.augslot5)); - update_values.push_back(columns[9] + " = " + std::to_string(sharedbank_entry.augslot6)); - update_values.push_back(columns[10] + " = '" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "UPDATE {} SET {} WHERE {} = {}", - TableName(), - Strings::Implode(", ", update_values), - PrimaryKey(), - sharedbank_entry. - ) - ); - - return (results.Success() ? results.RowsAffected() : 0); - } - - static Sharedbank InsertOne( - Sharedbank sharedbank_entry - ) - { - std::vector insert_values; - - insert_values.push_back(std::to_string(sharedbank_entry.acctid)); - insert_values.push_back(std::to_string(sharedbank_entry.slotid)); - insert_values.push_back(std::to_string(sharedbank_entry.itemid)); - insert_values.push_back(std::to_string(sharedbank_entry.charges)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot1)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot2)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot3)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot4)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot5)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot6)); - insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - auto results = database.QueryDatabase( - fmt::format( - "{} VALUES ({})", - BaseInsert(), - Strings::Implode(",", insert_values) - ) - ); - - if (results.Success()) { - sharedbank_entry.id = results.LastInsertedID(); - return sharedbank_entry; - } - - sharedbank_entry = InstanceListRepository::NewEntity(); - - return sharedbank_entry; - } - - static int InsertMany( - std::vector sharedbank_entries - ) - { - std::vector insert_chunks; - - for (auto &sharedbank_entry: sharedbank_entries) { - std::vector insert_values; - - insert_values.push_back(std::to_string(sharedbank_entry.acctid)); - insert_values.push_back(std::to_string(sharedbank_entry.slotid)); - insert_values.push_back(std::to_string(sharedbank_entry.itemid)); - insert_values.push_back(std::to_string(sharedbank_entry.charges)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot1)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot2)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot3)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot4)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot5)); - insert_values.push_back(std::to_string(sharedbank_entry.augslot6)); - insert_values.push_back("'" + Strings::Escape(sharedbank_entry.custom_data) + "'"); - - 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) { - Sharedbank entry{}; - - entry.acctid = atoi(row[0]); - entry.slotid = atoi(row[1]); - entry.itemid = atoi(row[2]); - entry.charges = atoi(row[3]); - entry.augslot1 = atoi(row[4]); - entry.augslot2 = atoi(row[5]); - entry.augslot3 = atoi(row[6]); - entry.augslot4 = atoi(row[7]); - entry.augslot5 = atoi(row[8]); - entry.augslot6 = atoi(row[9]); - entry.custom_data = row[10]; - - all_entries.push_back(entry); - } - - return all_entries; - } + /** + * 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 + * + * SharedbankRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedbankRepository::GetWhereNeverExpires() + * SharedbankRepository::GetWhereXAndY() + * SharedbankRepository::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 }; diff --git a/common/ruletypes.h b/common/ruletypes.h index 2b52e081d1..af9e6215d7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -343,6 +343,7 @@ RULE_STRING(World, SupportedClients, "", "Comma-delimited list of clients to res RULE_STRING(World, CustomFilesKey, "", "Enable if the server requires custom files and sends a key to validate. Empty string to disable. Example: eqcustom_v1") RULE_STRING(World, CustomFilesUrl, "github.com/knervous/eqnexus/releases", "URL to display at character select if client is missing custom files") RULE_INT(World, CustomFilesAdminLevel, 20, "Admin level at which custom file key is not required when CustomFilesKey is specified") +RULE_STRING(World, SupportedClients, "RoF2", "Comma-delimited list of clients to restrict to. Supported values are Titanium | SoF | SoD | UF | RoF | RoF2. Example: Titanium,RoF2") RULE_CATEGORY_END() RULE_CATEGORY(Zone) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 679bffe9d0..36a793b456 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -50,6 +50,7 @@ #include "repositories/skill_caps_repository.h" #include "repositories/inventory_repository.h" #include "repositories/books_repository.h" +#include "repositories/sharedbank_repository.h" namespace ItemField { @@ -192,242 +193,268 @@ SharedDatabase::MailKeys SharedDatabase::GetMailKey(int character_id) return MailKeys{}; } -bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) +bool SharedDatabase::SaveCursor( + uint32 char_id, + std::list::const_iterator& start, + std::list::const_iterator& end +) { - // Delete cursor items - const std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i " - "AND ((slotid >= 8000 AND slotid <= 8999) " - "OR slotid = %i OR (slotid >= %i AND slotid <= %i) )", - char_id, EQ::invslot::slotCursor, - EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END); - const auto results = QueryDatabase(query); - if (!results.Success()) { - std::cout << "Clearing cursor failed: " << results.ErrorMessage() << std::endl; - return false; - } + const int deleted = InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND (`slot_id` = {} OR `slot_id` BETWEEN {} AND {})", + char_id, + EQ::invslot::slotCursor, + EQ::invbag::CURSOR_BAG_BEGIN, + EQ::invbag::CURSOR_BAG_END + ) + ); + + int16 i = EQ::invslot::slotCursor; + for (auto& it = start; it != end; ++it, i++) { + // shouldn't be anything in the queue that indexes this high + if (i > EQ::invbag::CURSOR_BAG_END) { + break; + } - int i = 8000; - for(auto& it = start; it != end; ++it, i++) { - if (i > 8999) { break; } // shouldn't be anything in the queue that indexes this high - const EQ::ItemInstance *inst = *it; - const int16 use_slot = (i == 8000) ? EQ::invslot::slotCursor : i; + const EQ::ItemInstance* inst = *it; + const int16 use_slot = i == EQ::invslot::slotCursor ? EQ::invslot::slotCursor : i; if (!SaveInventory(char_id, inst, use_slot)) { return false; } - } + } return true; } bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const EQ::ItemInstance* inst) { - // Delete cursor items - const std::string query = StringFormat("SELECT itemid, charges FROM sharedbank " - "WHERE acctid = %d AND slotid = %d", - account_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - //returning true is less harmful in the face of a query error - return true; + if (!inst || !inst->GetItem()) { + return false; } - if (results.RowCount() == 0) - return false; - - auto& row = results.begin(); - - const uint32 id = Strings::ToUnsignedInt(row[0]); - const uint16 charges = Strings::ToUnsignedInt(row[1]); + const auto& l = SharedbankRepository::GetWhere( + *this, + fmt::format( + "`account_id` = {} AND `slot_id` = {} LIMIT 1", + account_id, + slot_id + ) + ); - uint16 expect_charges; + if (l.empty()) { + return false; + } - if(inst->GetCharges() >= 0) - expect_charges = inst->GetCharges(); - else - expect_charges = 0x7FFF; + const auto& e = l.front(); - if(id != inst->GetItem()->ID || charges != expect_charges) - return false; + uint16 expect_charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); - return true; + return e.item_id == inst->GetID() && e.charges == expect_charges; } -bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - - //never save tribute slots: - if (slot_id >= EQ::invslot::TRIBUTE_BEGIN && slot_id <= EQ::invslot::TRIBUTE_END) - return true; - if (slot_id >= EQ::invslot::GUILD_TRIBUTE_BEGIN && slot_id <= EQ::invslot::GUILD_TRIBUTE_END) +bool SharedDatabase::SaveInventory(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + // Don't save any Tribute slots + if ( + EQ::ValueWithin(slot_id, EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END) || + EQ::ValueWithin(slot_id, EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END) + ) { return true; + } - if (slot_id >= EQ::invslot::SHARED_BANK_BEGIN && slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { - // Shared bank inventory + if ( + EQ::ValueWithin(slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) { if (!inst) { return DeleteSharedBankSlot(char_id, slot_id); - } - else { + } else { // Needed to clear out bag slots that 'REPLACE' in UpdateSharedBankSlot does not overwrite..otherwise, duplication occurs // (This requires that parent then child items be sent..which should be how they are currently passed) - if (EQ::InventoryProfile::SupportsContainers(slot_id)) + if (EQ::InventoryProfile::SupportsContainers(slot_id)) { DeleteSharedBankSlot(char_id, slot_id); + } + return UpdateSharedBankSlot(char_id, inst, slot_id); } - } - else if (!inst) { // All other inventory + } else if (!inst) { // All other inventory return DeleteInventorySlot(char_id, slot_id); } // Needed to clear out bag slots that 'REPLACE' in UpdateInventorySlot does not overwrite..otherwise, duplication occurs // (This requires that parent then child items be sent..which should be how they are currently passed) - if (EQ::InventoryProfile::SupportsContainers(slot_id)) + if (EQ::InventoryProfile::SupportsContainers(slot_id)) { DeleteInventorySlot(char_id, slot_id); - return UpdateInventorySlot(char_id, inst, slot_id); -} + } -bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - // need to check 'inst' argument for valid pointer + return UpdateInventorySlot(char_id, inst, slot_id); +} - uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 }; - if (inst->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - const EQ::ItemInstance *auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } +bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + if (!inst || !inst->GetItem()) { + return false; } - uint16 charges; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - // Update/Insert item - const std::string query = StringFormat("REPLACE INTO inventory " - "(charid, slotid, itemid, charges, instnodrop, custom_data, color, " - "augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model, guid) " - "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " - "%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", - static_cast(char_id), static_cast(slot_id), static_cast(inst->GetItem()->ID), - static_cast(charges), static_cast(inst->IsAttuned() ? 1 : 0), - inst->GetCustomDataString().c_str(), static_cast(inst->GetColor()), - static_cast(augslot[0]), static_cast(augslot[1]), static_cast(augslot[2]), - static_cast(augslot[3]), static_cast(augslot[4]), static_cast(augslot[5]), static_cast(inst->GetOrnamentationIcon()), - static_cast(inst->GetOrnamentationIDFile()), static_cast(inst->GetOrnamentHeroModel()), inst->GetSerialNumber()); - const auto results = QueryDatabase(query); + std::vector augment_ids = inst->GetAugmentIDs(); + + uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); + + auto e = InventoryRepository::NewEntity(); + + e.character_id = char_id; + e.slot_id = slot_id; + e.item_id = inst->GetID(); + e.charges = charges; + e.color = inst->GetColor(); + e.augment_one = augment_ids[0]; + e.augment_two = augment_ids[1]; + e.augment_three = augment_ids[2]; + e.augment_four = augment_ids[3]; + e.augment_five = augment_ids[4]; + e.augment_six = augment_ids[5]; + e.instnodrop = inst->IsAttuned() ? 1 : 0; + e.custom_data = inst->GetCustomDataString(); + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); + + const int replaced = InventoryRepository::ReplaceOne(*this, e); - // Save bag contents, if slot supports bag contents - if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) + // Save bag contents, if slot supports bag contents + if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) { // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' // messages through attrition (and the modded code in SaveInventory) - for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) { - const EQ::ItemInstance* baginst = inst->GetItem(idx); - SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx)); + for ( + uint8 i = EQ::invbag::SLOT_BEGIN; + i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END; + i++ + ) { + const EQ::ItemInstance* bag_inst = inst->GetItem(i); + SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i)); } + } - if (!results.Success()) { - return false; - } - - return true; + return replaced; } -bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) { - // need to check 'inst' argument for valid pointer - - uint32 augslot[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 }; - if (inst->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - const EQ::ItemInstance *auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } +bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const EQ::ItemInstance* inst, int16 slot_id) +{ + if (!inst || !inst->GetItem()) { + return false; } -// Update/Insert item + std::vector augment_ids = inst->GetAugmentIDs(); + + uint16 charges = inst->GetCharges() >= 0 ? inst->GetCharges() : std::numeric_limits::max(); + const uint32 account_id = GetAccountIDByChar(char_id); - uint16 charges; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - const std::string query = StringFormat("REPLACE INTO sharedbank " - "(acctid, slotid, itemid, charges, custom_data, " - "augslot1, augslot2, augslot3, augslot4, augslot5, augslot6) " - "VALUES( %lu, %lu, %lu, %lu, '%s', " - "%lu, %lu, %lu, %lu, %lu, %lu)", - static_cast(account_id), static_cast(slot_id), static_cast(inst->GetItem()->ID), - static_cast(charges), inst->GetCustomDataString().c_str(), static_cast(augslot[0]), - static_cast(augslot[1]), static_cast(augslot[2]), static_cast(augslot[3]), static_cast(augslot[4]), - static_cast(augslot[5])); - const auto results = QueryDatabase(query); - // Save bag contents, if slot supports bag contents + auto e = SharedbankRepository::NewEntity(); + + e.account_id = account_id; + e.slot_id = slot_id; + e.item_id = inst->GetID(); + e.charges = charges; + e.color = inst->GetColor(); + e.augment_one = augment_ids[0]; + e.augment_two = augment_ids[1]; + e.augment_three = augment_ids[2]; + e.augment_four = augment_ids[3]; + e.augment_five = augment_ids[4]; + e.augment_six = augment_ids[5]; + e.custom_data = inst->GetCustomDataString(); + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); + + const int replaced = SharedbankRepository::ReplaceOne(*this, e); + + // Save bag contents, if slot supports bag contents if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) { // Limiting to bag slot count will get rid of 'hidden' duplicated items and 'Invalid Slot ID' // messages through attrition (and the modded code in SaveInventory) - for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx < inst->GetItem()->BagSlots && idx <= EQ::invbag::SLOT_END; idx++) { - const EQ::ItemInstance* baginst = inst->GetItem(idx); - SaveInventory(char_id, baginst, EQ::InventoryProfile::CalcSlotId(slot_id, idx)); + for ( + uint8 i = EQ::invbag::SLOT_BEGIN; + i < inst->GetItem()->BagSlots && i <= EQ::invbag::SLOT_END; + i++ + ) { + const EQ::ItemInstance* bag_inst = inst->GetItem(i); + SaveInventory(char_id, bag_inst, EQ::InventoryProfile::CalcSlotId(slot_id, i)); } } - if (!results.Success()) { - return false; - } - - return true; + return replaced; } -bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) { +bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) +{ + const int deleted = InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` = {}", + char_id, + slot_id + ) + ); - // Delete item - std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid = %i", char_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + if (!deleted) { + return false; + } - // Delete bag slots, if need be - if (!EQ::InventoryProfile::SupportsContainers(slot_id)) - return true; + // Delete bag slots, if need be + if (!EQ::InventoryProfile::SupportsContainers(slot_id)) { + return true; + } const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); - query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid >= %i AND slotid < %i", - char_id, base_slot_id, (base_slot_id+10)); - results = QueryDatabase(query); - if (!results.Success()) { - return false; - } - // @merth: need to delete augments here - return true; + return InventoryRepository::DeleteWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` BETWEEN {} AND {}", + char_id, + base_slot_id, + base_slot_id + (EQ::invbag::SLOT_COUNT - 1) + ) + ); } -bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) { +bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) +{ + const uint32 account_id = GetAccountIDByChar(char_id); - // Delete item - const uint32 account_id = GetAccountIDByChar(char_id); - std::string query = StringFormat("DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + const int deleted = SharedbankRepository::DeleteWhere( + *this, + fmt::format( + "`account_id` = {} AND `slot_id` = {}", + account_id, + slot_id + ) + ); - // Delete bag slots, if need be - if (!EQ::InventoryProfile::SupportsContainers(slot_id)) - return true; - - const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); - query = StringFormat("DELETE FROM sharedbank WHERE acctid = %i " - "AND slotid >= %i AND slotid < %i", - account_id, base_slot_id, (base_slot_id+10)); - results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + if (!deleted) { + return false; + } - // @merth: need to delete augments here - return true; + if (!EQ::InventoryProfile::SupportsContainers(slot_id)) { + return true; + } + + const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); + + return SharedbankRepository::DeleteWhere( + *this, + fmt::format( + "`account_id` = {} AND `slotid` BETWEEN {} AND {}", + account_id, + base_slot_id, + base_slot_id + (EQ::invbag::SLOT_COUNT - 1) + ) + ); } @@ -552,96 +579,81 @@ bool SharedDatabase::SetStartingItems( // Retrieve shared bank inventory based on either account or character bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid) { - std::string query; - - if (is_charid) { - query = fmt::format( - "SELECT sb.slotid, sb.itemid, sb.charges, " - "sb.augslot1, sb.augslot2, sb.augslot3, " - "sb.augslot4, sb.augslot5, sb.augslot6, sb.custom_data " - "FROM sharedbank sb INNER JOIN character_data ch " - "ON ch.account_id = sb.acctid WHERE ch.id = {} ORDER BY sb.slotid", - id - ); - } else { - query = fmt::format( - "SELECT slotid, itemid, charges, " - "augslot1, augslot2, augslot3, " - "augslot4, augslot5, augslot6, custom_data " - "FROM sharedbank WHERE acctid = {} ORDER BY slotid", - id - ); - } + const uint32 account_id = is_charid ? GetAccountIDByChar(id) : id; - auto results = QueryDatabase(query); - // If we have no results we still need to return true - if (!results.Success()) { + if (!account_id) { return false; } - for (auto row : results) { - int16 slot_id = static_cast(Strings::ToInt(row[0])); - uint32 item_id = Strings::ToUnsignedInt(row[1]); - const int16 charges = static_cast(Strings::ToInt(row[2])); + const auto& l = SharedbankRepository::GetWhere( + *this, + fmt::format( + "`account_id` = {}", + account_id + ) + ); + + if (l.empty()) { + return true; + } - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = Strings::ToUnsignedInt(row[3]); - aug[1] = Strings::ToUnsignedInt(row[4]); - aug[2] = Strings::ToUnsignedInt(row[5]); - aug[3] = Strings::ToUnsignedInt(row[6]); - aug[4] = Strings::ToUnsignedInt(row[7]); - aug[5] = Strings::ToUnsignedInt(row[8]); + for (const auto& e : l) { + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + e.augment_one, + e.augment_two, + e.augment_three, + e.augment_four, + e.augment_five, + e.augment_six + }; - const EQ::ItemData *item = GetItem(item_id); + const EQ::ItemData* item = GetItem(e.item_id); if (!item) { LogError( - "Warning: [{}] [{}] has an invalid item_id [{}] in inventory slot [{}]", - is_charid ? "charid" : "acctid", + "Warning: {} [{}] has an invalid item_id [{}] in slot_id [{}]", + is_charid ? "character_id" : "account_id", id, - item_id, - slot_id + e.item_id, + e.slot_id ); continue; } - auto inst = CreateBaseItem(item, charges); + EQ::ItemInstance* inst = CreateBaseItem(item, e.charges); if (!inst) { continue; } - if (inst && item->IsClassCommon()) { + if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); } } } - if (inst && row[9]) { - std::string data_str(row[9]); - inst->SetCustomDataString(data_str); + if (!e.custom_data.empty()) { + inst->SetCustomDataString(e.custom_data); } - // theoretically inst can be nullptr ... this would be very bad ... - const int16 put_slot_id = inv->PutItem(slot_id, *inst); + const int16 put_slot_id = inv->PutItem(e.slot_id, *inst); safe_delete(inst); - // Save ptr to item in inventory if (put_slot_id != INVALID_INDEX) { continue; } LogError( - "Warning: Invalid slot_id for item in shared bank inventory: [{}]=[{}], item_id=[{}], slot_id=[{}]", - is_charid ? "charid" : "acctid", + "Warning: Invalid slot_id for item in shared bank inventory for {} [{}] item_id [{}] slot_id [{}]", + is_charid ? "character_id" : "account_id", id, - item_id, - slot_id + e.item_id, + e.slot_id ); if (is_charid) { - SaveInventory(id, nullptr, slot_id); + SaveInventory(id, nullptr, e.slot_id); } } @@ -669,60 +681,57 @@ bool SharedDatabase::GetInventory(Client *c) return false; } - for (auto const &row: results) { + for (auto const& row: results) { if (row.guid != 0) { EQ::ItemInstance::AddGUIDToMap(row.guid); } } - const auto timestamps = GetItemRecastTimestamps(char_id); - auto cv_conflict = false; - const auto pmask = inv.GetLookup()->PossessionsBitmask; - const auto bank_size = inv.GetLookup()->InventoryTypeSize.Bank; + const auto timestamps = GetItemRecastTimestamps(char_id); + auto cv_conflict = false; + const auto pmask = inv->GetLookup()->PossessionsBitmask; + const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; - std::vector queue{}; - for (auto &row: results) { - const int16 slot_id = row.slotid; - const uint32 item_id = row.itemid; + std::vector queue{ }; + for (auto& row: results) { + const int16 slot_id = row.slot_id; + const uint32 item_id = row.item_id; const uint16 charges = row.charges; const uint32 color = row.color; - const bool instnodrop = row.instnodrop; - const uint32 ornament_icon = row.ornamenticon; - const uint32 ornament_idfile = row.ornamentidfile; + const bool instnodrop = row.instnodrop; + const uint32 ornament_icon = row.ornament_icon; + const uint32 ornament_idfile = row.ornament_idfile; const uint32 ornament_hero_model = row.ornament_hero_model; - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = row.augslot1; - aug[1] = row.augslot2; - aug[2] = row.augslot3; - aug[3] = row.augslot4; - aug[4] = row.augslot5; - aug[5] = row.augslot6; + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + row.augment_one, + row.augment_two, + row.augment_three, + row.augment_four, + row.augment_five, + row.augment_six + }; - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_END, EQ::invslot::POSSESSIONS_BEGIN)) { // Titanium thru UF check if (((static_cast(1) << slot_id) & pmask) == 0) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { // Titanium thru UF check - const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ( - (slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); + const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if (((static_cast(1) << parent_slot) & pmask) == 0) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_END, EQ::invslot::BANK_BEGIN)) { // Titanium check if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) { cv_conflict = true; continue; } - } - else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(slot_id, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { // Titanium check const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if (parent_index >= bank_size) { @@ -731,7 +740,7 @@ bool SharedDatabase::GetInventory(Client *c) } } - auto *item = GetItem(item_id); + auto* item = GetItem(item_id); if (!item) { LogError( "Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", @@ -742,7 +751,7 @@ bool SharedDatabase::GetInventory(Client *c) continue; } - auto *inst = CreateBaseItem(item, charges); + auto* inst = CreateBaseItem(item, charges); if (!inst) { continue; } @@ -755,8 +764,13 @@ bool SharedDatabase::GetInventory(Client *c) inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentHeroModel(item->HerosForgeModel); - if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= - EQ::invslot::EQUIPMENT_END)) { + if ( + instnodrop || + ( + inst->GetItem()->Attuneable && + EQ::ValueWithin(slot_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) + ) + ) { inst->SetAttuned(true); } @@ -764,33 +778,29 @@ bool SharedDatabase::GetInventory(Client *c) inst->SetColor(color); } - if (charges == 0x7FFF) { + if (charges == std::numeric_limits::max()) { inst->SetCharges(-1); - } - else if (charges == 0 && inst->IsStackable()) { + } else if (charges == 0 && inst->IsStackable()) { // Stackable items need a minimum charge of 1 remain moveable. inst->SetCharges(1); - } - else { + } else { inst->SetCharges(charges); } if (item->RecastDelay) { if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) { inst->SetRecastTimestamp(timestamps.at(item->RecastType)); - } - else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { + } else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { inst->SetRecastTimestamp(timestamps.at(item->ID)); - } - else { + } else { inst->SetRecastTimestamp(0); } } if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); } } } @@ -842,21 +852,10 @@ bool SharedDatabase::GetInventory(Client *c) } int16 put_slot_id; - if (slot_id >= 8000 && slot_id <= 8999) { - put_slot_id = inv.PushCursor(*inst); - } - else if (slot_id >= 3111 && slot_id <= 3179) { - // Admins: please report any occurrences of this error - LogError( - "Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...", - char_id, - item_id, - slot_id - ); - put_slot_id = inv.PushCursor(*inst); - } - else { - put_slot_id = inv.PutItem(slot_id, *inst); + if (slot_id > EQ::invbag::TRADE_BAGS_END) { + put_slot_id = inv->PushCursor(*inst); + } else { + put_slot_id = inv->PutItem(slot_id, *inst); } row.guid = inst->GetSerialNumber(); @@ -867,7 +866,7 @@ bool SharedDatabase::GetInventory(Client *c) // Save ptr to item in inventory if (put_slot_id == INVALID_INDEX) { LogError( - "Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]", + "Warning: Invalid slot_id for item in inventory for character_id [{}] item_id [{}] slot_id [{}]", char_id, item_id, slot_id @@ -876,7 +875,7 @@ bool SharedDatabase::GetInventory(Client *c) } if (cv_conflict) { - const std::string &char_name = GetCharName(char_id); + const std::string& char_name = GetCharName(char_id); LogError( "ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", char_name, @@ -896,94 +895,6 @@ bool SharedDatabase::GetInventory(Client *c) return GetSharedBank(char_id, &inv, true); } -// Overloaded: Retrieve character inventory based on account_id and character name (char select) -bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv) // deprecated -{ - // Retrieve character inventory - const std::string query = - StringFormat("SELECT slotid, itemid, charges, color, augslot1, " - "augslot2, augslot3, augslot4, augslot5, augslot6, instnodrop, custom_data, ornamenticon, " - "ornamentidfile, ornament_hero_model " - "FROM inventory INNER JOIN character_data ch " - "ON ch.id = charid WHERE ch.name = '%s' AND ch.account_id = %i ORDER BY slotid", - name, account_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogError("If you got an error related to the 'instnodrop' field, run the " - "following SQL Queries:\nalter table inventory add instnodrop " - "tinyint(1) unsigned default 0 not null;\n"); - return false; - } - - for (auto& row = results.begin(); row != results.end(); ++row) { - int16 slot_id = Strings::ToInt(row[0]); - uint32 item_id = Strings::ToUnsignedInt(row[1]); - const int8 charges = Strings::ToInt(row[2]); - const uint32 color = Strings::ToUnsignedInt(row[3]); - - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = Strings::ToUnsignedInt(row[4]); - aug[1] = Strings::ToUnsignedInt(row[5]); - aug[2] = Strings::ToUnsignedInt(row[6]); - aug[3] = Strings::ToUnsignedInt(row[7]); - aug[4] = Strings::ToUnsignedInt(row[8]); - aug[5] = Strings::ToUnsignedInt(row[9]); - - const bool instnodrop = (row[10] && static_cast(Strings::ToUnsignedInt(row[10]))); - const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]); - const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]); - uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]); - - const EQ::ItemData *item = GetItem(item_id); - if (!item) - continue; - - EQ::ItemInstance *inst = CreateBaseItem(item, charges); - - if (inst == nullptr) - continue; - - inst->SetAttuned(instnodrop); - - if (row[11]) { - std::string data_str(row[11]); - inst->SetCustomDataString(data_str); - } - - inst->SetOrnamentIcon(ornament_icon); - inst->SetOrnamentationIDFile(ornament_idfile); - inst->SetOrnamentHeroModel(item->HerosForgeModel); - - if (color > 0) - inst->SetColor(color); - - inst->SetCharges(charges); - - if (item->IsClassCommon()) { - for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) - inst->PutAugment(this, i, aug[i]); - } - } - - int16 put_slot_id; - if (slot_id >= 8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) - LogError("Warning: Invalid slot_id for item in inventory: name={}, acctid={}, item_id={}, slot_id={}", - name, account_id, item_id, slot_id); - } - - // Retrieve shared inventory - return GetSharedBank(account_id, inv, false); -} - std::map SharedDatabase::GetItemRecastTimestamps(uint32 char_id) { std::map timers; @@ -1335,7 +1246,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ // Bag item.BagSize = static_cast(Strings::ToUnsignedInt(row[ItemField::bagsize])); - item.BagSlots = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, 10)); // Will need to be changed from std::min to just use database value when bag slots are increased + item.BagSlots = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagslots]), 0, static_cast(EQ::invbag::SLOT_COUNT))); item.BagType = static_cast(Strings::ToUnsignedInt(row[ItemField::bagtype])); item.BagWR = static_cast(EQ::Clamp(Strings::ToInt(row[ItemField::bagwr]), 0, 100)); diff --git a/common/version.h b/common/version.h index a99b965751..b86cb155fc 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9293 +#define CURRENT_BINARY_DATABASE_VERSION 9296 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index 492bf3c969..396b1e8756 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -142,7 +142,6 @@ "guild_bank", "inventory_versions", "raid_leaders", - "sharedbank", "trader_audit", "eqtime", "db_version", diff --git a/world/client.cpp b/world/client.cpp index 1c4b61632c..e75d8824d0 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -2380,21 +2380,26 @@ bool Client::StoreCharacter( auto e = InventoryRepository::NewEntity(); - e.charid = character_id; + e.character_id = character_id; for (int16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invbag::BANK_BAGS_END;) { const auto inst = p_inventory_profile->GetItem(slot_id); if (inst) { - e.slotid = slot_id; - e.itemid = inst->GetItem()->ID; - e.charges = inst->GetCharges(); - e.color = inst->GetColor(); - e.augslot1 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN); - e.augslot2 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1); - e.augslot3 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2); - e.augslot4 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3); - e.augslot5 = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4); - e.augslot6 = inst->GetAugmentItemID(EQ::invaug::SOCKET_END); + e.slot_id = slot_id; + e.item_id = inst->GetItem()->ID; + e.charges = inst->GetCharges(); + e.color = inst->GetColor(); + e.augment_one = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN); + e.augment_two = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1); + e.augment_three = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2); + e.augment_four = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3); + e.augment_five = inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4); + e.augment_six = inst->GetAugmentItemID(EQ::invaug::SOCKET_END); + e.instnodrop = inst->IsAttuned() ? 1 : 0; + e.ornament_icon = inst->GetOrnamentationIcon(); + e.ornament_idfile = inst->GetOrnamentationIDFile(); + e.ornament_hero_model = inst->GetOrnamentHeroModel(); + e.guid = inst->GetSerialNumber(); v.emplace_back(e); } diff --git a/zone/client.cpp b/zone/client.cpp index 1c577c08d2..30e3be5d69 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9203,7 +9203,7 @@ void Client::SetPrimaryWeaponOrnamentation(uint32 model_id) auto l = InventoryRepository::GetWhere( database, fmt::format( - "`charid` = {} AND `slotid` = {}", + "`character_id` = {} AND `slot_id` = {}", character_id, EQ::invslot::slotPrimary ) @@ -9215,7 +9215,7 @@ void Client::SetPrimaryWeaponOrnamentation(uint32 model_id) auto e = l.front(); - e.ornamentidfile = model_id; + e.ornament_idfile = model_id; const int updated = InventoryRepository::UpdateOne(database, e); @@ -9236,7 +9236,7 @@ void Client::SetSecondaryWeaponOrnamentation(uint32 model_id) auto l = InventoryRepository::GetWhere( database, fmt::format( - "`charid` = {} AND `slotid` = {}", + "`character_id` = {} AND `slot_id` = {}", character_id, EQ::invslot::slotSecondary ) @@ -9248,7 +9248,7 @@ void Client::SetSecondaryWeaponOrnamentation(uint32 model_id) auto e = l.front(); - e.ornamentidfile = model_id; + e.ornament_idfile = model_id; const int updated = InventoryRepository::UpdateOne(database, e); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 95a9a970ec..94f5c70da4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10747,8 +10747,7 @@ void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) { - if (!CharacterID()) - { + if (!CharacterID()) { return; } @@ -10757,57 +10756,34 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) return; } - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if (mi->from_slot != mi->to_slot && (mi->from_slot <= EQ::invslot::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - const EQ::ItemInstance *itm_from = GetInv().GetItem(mi->from_slot); - const EQ::ItemInstance *itm_to = GetInv().GetItem(mi->to_slot); - auto message = fmt::format("Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.", + MoveItem_Struct* mi = (MoveItem_Struct*) app->pBuffer; + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) { + if (mi->from_slot != mi->to_slot && (mi->from_slot <= EQ::invslot::GENERAL_END || mi->from_slot > 39) && + IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { + const EQ::ItemInstance* itm_from = GetInv().GetItem(mi->from_slot); + const EQ::ItemInstance* itm_to = GetInv().GetItem(mi->to_slot); + auto message = fmt::format( + "Player issued a move item from {}(item id {}) to {}(item id {}) while casting {}.", mi->from_slot, itm_from ? itm_from->GetID() : 0, mi->to_slot, itm_to ? itm_to->GetID() : 0, - casting_spell_id); - RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message}); + casting_spell_id + ); + RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{ .message = message }); Kick("Inventory desync"); // Kick client to prevent client and server from getting out-of-sync inventory slots return; } } - // Illegal bagslot usage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if (mi->from_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && mi->from_slot <= EQ::invbag::CURSOR_BAG_END) { - if (mi->from_slot >= EQ::invbag::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if (!m_inv[from_parent]) { mi_hack = true; } - else if (!m_inv[from_parent]->IsClassBag()) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi->to_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && mi->to_slot <= EQ::invbag::CURSOR_BAG_END) { - if (mi->to_slot >= EQ::invbag::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if (!m_inv[to_parent]) { mi_hack = true; } - else if (!m_inv[to_parent]->IsClassBag()) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi_hack) { Message(Chat::Yellow, "Caution: Illegal use of inaccessible bag slots!"); } - if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); bool error = false; InterrogateInventory(this, false, true, false, error, false); - if (error) + if (error) { InterrogateInventory(this, true, false, true, error); + } } for (int slot : {mi->to_slot, mi->from_slot}) { diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 2f70dcb2da..b2146dd813 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -770,11 +770,12 @@ void Client::BulkSendInventoryItems() EQ::OutBuffer ob; EQ::OutBuffer::pos_type last_pos = ob.tellp(); - // Possessions items - for (int16 slot_id = EQ::invslot::POSSESSIONS_BEGIN; slot_id <= EQ::invslot::POSSESSIONS_END; slot_id++) { + // Equipment items + for (int16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; slot_id++) { const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) + if (!inst) { continue; + } inst->Serialize(ob, slot_id); @@ -784,7 +785,7 @@ void Client::BulkSendInventoryItems() last_pos = ob.tellp(); } - if (!RuleB(Inventory, LazyLoadBank)) { + if (!RuleB(Inventory, LazyLoadBank)) { // Bank items for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { const EQ::ItemInstance* inst = m_inv[slot_id]; @@ -823,12 +824,6 @@ void Client::BulkSendInventoryItems() void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { const EQ::ItemData* handy_item = nullptr; - - uint32 merchant_slots = 80; //The max number of items passed in the transaction. - if (m_ClientVersionBit & EQ::versions::maskRoFAndLater) { // RoF+ can send 200 items - merchant_slots = 200; - } - const EQ::ItemData *item = nullptr; auto merchant_list = zone->merchanttable[merchant_id]; auto npc = entity_list.GetMobByNpcTypeID(npcid); @@ -840,6 +835,8 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { } } + const int16 merchant_slots = (m_ClientVersionBit & EQ::versions::maskRoFAndLater) ? EQ::invtype::MERCHANT_SIZE : 80; + auto temporary_merchant_list = zone->tmpmerchanttable[npcid]; uint32 slot_id = 1; uint8 handy_chance = 0; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 4a0d8de09e..6d2a92da35 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -845,7 +845,7 @@ LootItem *Corpse::GetItem(uint16 lootslot, LootItem **bag_item_data) // convert above code to for loop for (const auto &item: m_item_list) { - if (item->equip_slot >= bagstart && item->equip_slot < bagstart + 10) { + if (item->equip_slot >= bagstart && item->equip_slot < bagstart + EQ::invbag::SLOT_COUNT) { bag_item_data[item->equip_slot - bagstart] = item; } } @@ -1472,7 +1472,7 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app) const EQ::ItemData *item = nullptr; EQ::ItemInstance *inst = nullptr; - LootItem *item_data = nullptr, *bag_item_data[10] = {}; + LootItem *item_data = nullptr, *bag_item_data[EQ::invbag::SLOT_COUNT] = {}; memset(bag_item_data, 0, sizeof(bag_item_data)); if (GetPlayerKillItem() > 1) { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index af2d2eaf66..931f433386 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -278,25 +278,25 @@ int Perl__getinventoryslotid(std::string identifier) else if (identifier == "generalbags.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN; else if (identifier == "generalbags.end") result = EQ::invbag::GENERAL_BAGS_END; else if (identifier == "general1bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN; - else if (identifier == "general1bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 9; - else if (identifier == "general2bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 10; - else if (identifier == "general2bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 19; - else if (identifier == "general3bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 20; - else if (identifier == "general3bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 29; - else if (identifier == "general4bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 30; - else if (identifier == "general4bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 39; - else if (identifier == "general5bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 40; - else if (identifier == "general5bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 49; - else if (identifier == "general6bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 50; - else if (identifier == "general6bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 59; - else if (identifier == "general7bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 60; - else if (identifier == "general7bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 69; - else if (identifier == "general8bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 70; - else if (identifier == "general8bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 79; - else if (identifier == "general9bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 80; - else if (identifier == "general9bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 89; - else if (identifier == "general10bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + 90; - else if (identifier == "general10bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + 99; + else if (identifier == "general1bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT - 1); + else if (identifier == "general2bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + EQ::invbag::SLOT_COUNT; + else if (identifier == "general2bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 2) - 1); + else if (identifier == "general3bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 2); + else if (identifier == "general3bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 3) - 1); + else if (identifier == "general4bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 3); + else if (identifier == "general4bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 4) - 1); + else if (identifier == "general5bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 4); + else if (identifier == "general5bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 5) - 1); + else if (identifier == "general6bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 5); + else if (identifier == "general6bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 6) - 1); + else if (identifier == "general7bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 6); + else if (identifier == "general7bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 7) - 1); + else if (identifier == "general8bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 7); + else if (identifier == "general8bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 8) - 1); + else if (identifier == "general9bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 8); + else if (identifier == "general9bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 9) - 1); + else if (identifier == "general10bag.begin") result = EQ::invbag::GENERAL_BAGS_BEGIN + (EQ::invbag::SLOT_COUNT * 9); + else if (identifier == "general10bag.end") result = EQ::invbag::GENERAL_BAGS_BEGIN + ((EQ::invbag::SLOT_COUNT * 10) - 1); else if (identifier == "cursorbag.begin") result = EQ::invbag::CURSOR_BAG_BEGIN; else if (identifier == "cursorbag.end") result = EQ::invbag::CURSOR_BAG_END; else if (identifier == "bank.begin") result = EQ::invslot::BANK_BEGIN; diff --git a/zone/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index c2d22b2d96..2078f2bdd6 100644 --- a/zone/gm_commands/show/inventory.cpp +++ b/zone/gm_commands/show/inventory.cpp @@ -310,7 +310,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), item_data->ID, linker.GenerateLink(), ( @@ -339,7 +339,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} (Augment Slot {}) | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), augment_index, linker.GenerateLink(), item_data->ID, @@ -375,7 +375,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), sub_index, linker.GenerateLink(), item_data->ID, @@ -407,7 +407,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}", - (8000 + limboIndex), + (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), sub_index, augment_index, linker.GenerateLink(), diff --git a/zone/inventory.cpp b/zone/inventory.cpp index f89c1005c1..afd366c234 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1919,13 +1919,20 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } //verify shared bank transactions in the database - if (src_inst && src_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && src_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { + if ( + src_inst && + ( + EQ::ValueWithin(src_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(src_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) + ) { if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { LogError("Player [{}] on account [{}] was found exploiting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(dst_slot_id,0,true); return(false); } - if (src_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && src_slot_id <= EQ::invslot::SHARED_BANK_END && src_inst->IsClassBag()){ + + if (EQ::ValueWithin(src_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) && src_inst->IsClassBag()){ for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx <= EQ::invbag::SLOT_END; idx++) { const EQ::ItemInstance* baginst = src_inst->GetItem(idx); if (baginst && !database.VerifyInventory(account_id, EQ::InventoryProfile::CalcSlotId(src_slot_id, idx), baginst)){ @@ -1934,13 +1941,21 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } } } - if (dst_inst && dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END) { + + if ( + dst_inst && + ( + EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(dst_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) + ) { if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { LogError("Player [{}] on account [{}] was found exploting the shared bank.\n", GetName(), account_name); DeleteItemInInventory(src_slot_id,0,true); return(false); } - if (dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invslot::SHARED_BANK_END && dst_inst->IsClassBag()){ + + if (EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) && dst_inst->IsClassBag()){ for (uint8 idx = EQ::invbag::SLOT_BEGIN; idx <= EQ::invbag::SLOT_END; idx++) { const EQ::ItemInstance* baginst = dst_inst->GetItem(idx); if (baginst && !database.VerifyInventory(account_id, EQ::InventoryProfile::CalcSlotId(dst_slot_id, idx), baginst)){ @@ -1953,10 +1968,20 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Check for No Drop Hacks Mob* with = trade->With(); - if (((with && with->IsClient() && !with->CastToClient()->IsBecomeNPC() && dst_slot_id >= EQ::invslot::TRADE_BEGIN && dst_slot_id <= EQ::invslot::TRADE_END) || - (dst_slot_id >= EQ::invslot::SHARED_BANK_BEGIN && dst_slot_id <= EQ::invbag::SHARED_BANK_BAGS_END)) - && GetInv().CheckNoDrop(src_slot_id) - && !CanTradeFVNoDropItem()) { + if ( + ( + ( + with && + with->IsClient() && + !with->CastToClient()->IsBecomeNPC() && + EQ::ValueWithin(dst_slot_id, EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END) + ) || + EQ::ValueWithin(dst_slot_id, EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END) || + EQ::ValueWithin(dst_slot_id, EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END) + ) && + GetInv().CheckNoDrop(src_slot_id) && + !CanTradeFVNoDropItem() + ) { auto ndh_inst = m_inv[src_slot_id]; std::string ndh_item_data; if (ndh_inst == nullptr) { @@ -3651,7 +3676,7 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool if (cursor_itr == m_inv.cursor_cbegin()) continue; - instmap[8000 + limbo] = *cursor_itr; + instmap[EQ::invbag::CURSOR_BAG_BEGIN + limbo] = *cursor_itr; } // call InterrogateInventory_ for error check @@ -3754,11 +3779,12 @@ bool Client::InterrogateInventory_error(int16 head, int16 index, const EQ::ItemI // very basic error checking - can be elaborated upon if more in-depth testing is needed... 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)) { + EQ::ValueWithin(head, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) || + EQ::ValueWithin(head, EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END) || + EQ::ValueWithin(head, EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END) || + EQ::ValueWithin(head, EQ::invslot::WORLD_BEGIN, EQ::invslot::WORLD_END) || + EQ::ValueWithin(head, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) + ) { switch (depth) { case 0: // requirement: inst is extant diff --git a/zone/zonedb.h b/zone/zonedb.h index ca0447758c..28c224dbb3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -208,10 +208,10 @@ struct ZoneSpellsBlocked { }; struct TraderCharges_Struct { - uint32 ItemID[80]; - int32 SerialNumber[80]; - uint32 ItemCost[80]; - int32 Charges[80]; + uint32 ItemID[EQ::invtype::BAZAAR_SIZE]; + int32 SerialNumber[EQ::invtype::BAZAAR_SIZE]; + uint32 ItemCost[EQ::invtype::BAZAAR_SIZE]; + int32 Charges[EQ::invtype::BAZAAR_SIZE]; }; const int MaxMercStanceID = 9; From 44f708ab0ef0a67f6e458a6c347f2deed729a45a Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:26:13 -0400 Subject: [PATCH 67/73] Update worlddb.cpp --- world/worlddb.cpp | 154 +++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 89 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 986843da5e..6a82eba4a5 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -26,6 +26,7 @@ #include #include "sof_char_create_data.h" #include "../common/repositories/character_instance_safereturns_repository.h" +#include "../common/repositories/inventory_repository.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/zone_store.h" @@ -857,115 +858,90 @@ bool WorldDatabase::LoadCharacterCreateCombos() // this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv) { - if (!account_id || !name || !inv) + if (!account_id || !name || !inv) { return false; + } - std::string query = StringFormat( - "SELECT" - " slotid," - " itemid," - " charges," - " color," - " augslot1," - " augslot2," - " augslot3," - " augslot4," - " augslot5," - " augslot6," - " instnodrop," - " custom_data," - " ornamenticon," - " ornamentidfile," - " ornament_hero_model " - "FROM" - " inventory " - "INNER JOIN" - " character_data ch " - "ON" - " ch.id = charid " - "WHERE" - " ch.name = '%s' " - "AND" - " ch.account_id = %i " - "AND" - " slotid >= %i " - "AND" - " slotid <= %i", - name, - account_id, - EQ::invslot::slotHead, - EQ::invslot::slotFeet - ); - auto results = QueryDatabase(query); - if (!results.Success()) + const uint32 character_id = GetCharacterID(name); + + if (!character_id) { return false; + } - for (auto row = results.begin(); row != results.end(); ++row) { - int16 slot_id = Strings::ToInt(row[0]); - - switch (slot_id) { - case EQ::invslot::slotFace: - case EQ::invslot::slotEar2: - case EQ::invslot::slotNeck: - case EQ::invslot::slotShoulders: - case EQ::invslot::slotBack: - case EQ::invslot::slotFinger1: - case EQ::invslot::slotFinger2: - continue; - default: - break; + const auto& l = InventoryRepository::GetWhere( + *this, + fmt::format( + "`character_id` = {} AND `slot_id` BETWEEN {} AND {}", + character_id, + EQ::invslot::slotHead, + EQ::invslot::slotFeet + ) + ); + + if (l.empty()) { + return true; + } + + for (const auto& e : l) { + switch (e.slot_id) { + case EQ::invslot::slotFace: + case EQ::invslot::slotEar2: + case EQ::invslot::slotNeck: + case EQ::invslot::slotShoulders: + case EQ::invslot::slotBack: + case EQ::invslot::slotFinger1: + case EQ::invslot::slotFinger2: + continue; + default: + break; } - uint32 item_id = Strings::ToInt(row[1]); - int8 charges = Strings::ToInt(row[2]); - uint32 color = Strings::ToUnsignedInt(row[3]); - - uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = (uint32)Strings::ToInt(row[4]); - aug[1] = (uint32)Strings::ToInt(row[5]); - aug[2] = (uint32)Strings::ToInt(row[6]); - aug[3] = (uint32)Strings::ToInt(row[7]); - aug[4] = (uint32)Strings::ToInt(row[8]); - aug[5] = (uint32)Strings::ToInt(row[9]); - - bool instnodrop = ((row[10] && (uint16)Strings::ToInt(row[10])) ? true : false); - uint32 ornament_icon = (uint32)Strings::ToUnsignedInt(row[12]); - uint32 ornament_idfile = (uint32)Strings::ToUnsignedInt(row[13]); - uint32 ornament_hero_model = (uint32)Strings::ToUnsignedInt(row[14]); - - const EQ::ItemData *item = content_db.GetItem(item_id); - if (!item) - continue; - EQ::ItemInstance *inst = content_db.CreateBaseItem(item, charges); + uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = { + e.augment_one, + e.augment_two, + e.augment_three, + e.augment_four, + e.augment_five, + e.augment_six + }; - if (inst == nullptr) + const EQ::ItemData* item = content_db.GetItem(e.item_id); + if (!item) { continue; + } - inst->SetAttuned(instnodrop); + EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges); - if (row[11]) { - std::string data_str(row[11]); - inst->SetCustomDataString(data_str); + if (!inst) { + continue; } - inst->SetOrnamentIcon(ornament_icon); - inst->SetOrnamentationIDFile(ornament_idfile); - inst->SetOrnamentHeroModel(item->HerosForgeModel); - - if (color > 0) - inst->SetColor(color); + inst->SetCharges(e.charges); - inst->SetCharges(charges); + if (e.color > 0) { + inst->SetColor(e.color); + } if (item->IsClassCommon()) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { - if (aug[i]) - inst->PutAugment(this, i, aug[i]); + if (augment_ids[i]) { + inst->PutAugment(this, i, augment_ids[i]); + } } } - inv->PutItem(slot_id, *inst); + inst->SetAttuned(e.instnodrop); + + if (!e.custom_data.empty()) { + inst->SetCustomDataString(e.custom_data); + } + + inst->SetOrnamentIcon(e.ornament_icon); + inst->SetOrnamentationIDFile(e.ornament_idfile); + inst->SetOrnamentHeroModel(e.ornament_hero_model); + + inv->PutItem(e.slot_id, *inst); safe_delete(inst); } From a2dba757355ac5c7ceb75fcaa878107af066b166 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 00:45:32 -0400 Subject: [PATCH 68/73] Update shareddb.cpp --- common/shareddb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 36a793b456..653efc35c4 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -712,7 +712,7 @@ bool SharedDatabase::GetInventory(Client *c) row.augment_six }; - if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_END, EQ::invslot::POSSESSIONS_BEGIN)) { + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { // Titanium thru UF check if (((static_cast(1) << slot_id) & pmask) == 0) { cv_conflict = true; @@ -725,7 +725,7 @@ bool SharedDatabase::GetInventory(Client *c) cv_conflict = true; continue; } - } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_END, EQ::invslot::BANK_BEGIN)) { + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { // Titanium check if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) { cv_conflict = true; @@ -852,7 +852,7 @@ bool SharedDatabase::GetInventory(Client *c) } int16 put_slot_id; - if (slot_id > EQ::invbag::TRADE_BAGS_END) { + if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) || slot_id == EQ::invslot::slotCursor) { put_slot_id = inv->PushCursor(*inst); } else { put_slot_id = inv->PutItem(slot_id, *inst); From 0e2c9c1deee71df903ce0dfb991e53a71604e2fb Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 18:12:07 -0400 Subject: [PATCH 69/73] Cleanup --- common/inventory_profile.cpp | 1002 +++++++++++++++++----------------- common/inventory_profile.h | 9 - zone/client_packet.cpp | 5 - 3 files changed, 514 insertions(+), 502 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 603f4111b3..b65de87926 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -28,6 +28,7 @@ #include "strings.h" #include "../common/light_source.h" +#include "data_verification.h" //#include @@ -44,6 +45,7 @@ ItemInstQueue::~ItemInstQueue() for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) { safe_delete(*iter); } + m_list.clear(); } @@ -62,8 +64,9 @@ void ItemInstQueue::push_front(EQ::ItemInstance* inst) // Remove item from front of queue EQ::ItemInstance* ItemInstQueue::pop() { - if (m_list.empty()) + if (m_list.empty()) { return nullptr; + } EQ::ItemInstance* inst = m_list.front(); m_list.pop_front(); @@ -73,8 +76,9 @@ EQ::ItemInstance* ItemInstQueue::pop() // Remove item from back of queue EQ::ItemInstance* ItemInstQueue::pop_back() { - if (m_list.empty()) + if (m_list.empty()) { return nullptr; + } EQ::ItemInstance* inst = m_list.back(); m_list.pop_back(); @@ -87,7 +91,6 @@ EQ::ItemInstance* ItemInstQueue::peek_front() const return (m_list.empty()) ? nullptr : m_list.front(); } - // // class EQ::InventoryProfile // @@ -96,26 +99,31 @@ EQ::InventoryProfile::~InventoryProfile() for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { safe_delete(iter->second); } + m_worn.clear(); for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { safe_delete(iter->second); } + m_inv.clear(); for (auto iter = m_bank.begin(); iter != m_bank.end(); ++iter) { safe_delete(iter->second); } + m_bank.clear(); for (auto iter = m_shbank.begin(); iter != m_shbank.end(); ++iter) { safe_delete(iter->second); } + m_shbank.clear(); for (auto iter = m_trade.begin(); iter != m_trade.end(); ++iter) { safe_delete(iter->second); } + m_trade.clear(); } @@ -136,6 +144,7 @@ void EQ::InventoryProfile::CleanDirty() { delete (*iter); ++iter; } + dirty_inst.clear(); } @@ -157,59 +166,44 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id) const } // Non bag slots - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { result = _GetItem(m_trade, slot_id); - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { - // Shared Bank slots + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { result = _GetItem(m_shbank, slot_id); - } - else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { - // Bank slots + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { result = _GetItem(m_bank, slot_id); - } - else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { - // Personal inventory slots + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { 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::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END)) { - // Equippable slots (on body) + } else if ( + EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END) || + EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END) || + EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, invslot::GUILD_TRIBUTE_END) + ) { result = _GetItem(m_worn, slot_id); } // Inner bag slots - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { - // Trade bag slots + else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { ItemInstance* inst = _GetItem(m_trade, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { - // Shared Bank bag slots + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { ItemInstance* inst = _GetItem(m_shbank, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { - // Bank bag slots + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { ItemInstance* inst = _GetItem(m_bank, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { - // Cursor bag slots + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { ItemInstance* inst = m_cursor.peek_front(); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); } - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { - // Personal inventory bag slots + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { ItemInstance* inst = _GetItem(m_inv, InventoryProfile::CalcSlotId(slot_id)); if (inst && inst->IsClassBag()) { result = inst->GetItem(InventoryProfile::CalcBagIdx(slot_id)); @@ -228,25 +222,27 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id, uint8 bagidx) con // Put an item into specified slot int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) { - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) == 0) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } + } else if (EQ::ValueWithin(slot_id, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); - if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { - if ((slot_id - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if ((slot_id - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { return EQ::invslot::SLOT_INVALID; - } - else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { + } + } else if (EQ::ValueWithin(slot_id, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; - if (temp_slot >= m_lookup->InventoryTypeSize.Bank) + if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { return EQ::invslot::SLOT_INVALID; + } } + // Clean up item already in slot (if exists) DeleteItem(slot_id); @@ -281,26 +277,23 @@ bool EQ::InventoryProfile::SwapItem( ) { fail_state = swapInvalid; - if (source_slot <= EQ::invslot::POSSESSIONS_END && source_slot >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64) 1 << source_slot) & m_lookup->PossessionsBitmask) == 0) { + if (EQ::ValueWithin(source_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64)1 << source_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invbag::GENERAL_BAGS_END && source_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((source_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invslot::BANK_END && source_slot >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { if ((source_slot - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; return false; } - } - else if (source_slot <= EQ::invbag::BANK_BAGS_END && source_slot >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(source_slot, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (source_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; @@ -308,26 +301,23 @@ bool EQ::InventoryProfile::SwapItem( } } - if (destination_slot <= EQ::invslot::POSSESSIONS_END && destination_slot >= EQ::invslot::POSSESSIONS_BEGIN) { + if (EQ::ValueWithin(destination_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { if ((((uint64)1 << destination_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invbag::GENERAL_BAGS_END && destination_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END)) { auto temp_slot = EQ::invslot::GENERAL_BEGIN + ((destination_slot - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); if ((((uint64)1 << temp_slot) & m_lookup->PossessionsBitmask) == 0) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invslot::BANK_END && destination_slot >= EQ::invslot::BANK_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { if ((destination_slot - EQ::invslot::BANK_BEGIN) >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; return false; } - } - else if (destination_slot <= EQ::invbag::BANK_BAGS_END && destination_slot >= EQ::invbag::BANK_BAGS_BEGIN) { + } else if (EQ::ValueWithin(destination_slot, EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END)) { auto temp_slot = (destination_slot - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT; if (temp_slot >= m_lookup->InventoryTypeSize.Bank) { fail_state = swapNotAllowed; @@ -335,6 +325,7 @@ bool EQ::InventoryProfile::SwapItem( } } + // Temp holding areas for source and destination ItemInstance *source_item_instance = GetItem(source_slot); ItemInstance *destination_item_instance = GetItem(destination_slot); @@ -346,20 +337,23 @@ bool EQ::InventoryProfile::SwapItem( } source_item_instance->SetEvolveEquipped(false); - if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { + if (EQ::ValueWithin(destination_slot, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { auto source_item = source_item_instance->GetItem(); if (!source_item) { fail_state = swapNullData; return false; } + if (race_id && class_id && !source_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } + if (deity_id && source_item->Deity && !(Deity::GetBitmask(deity_id) & source_item->Deity)) { fail_state = swapDeity; return false; } + if (level && source_item->ReqLevel && level < source_item->ReqLevel) { fail_state = swapLevel; return false; @@ -377,20 +371,23 @@ bool EQ::InventoryProfile::SwapItem( } destination_item_instance->SetEvolveEquipped(false); - if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { + if (EQ::ValueWithin(source_slot, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { auto destination_item = destination_item_instance->GetItem(); if (!destination_item) { fail_state = swapNullData; return false; } + if (race_id && class_id && !destination_item->IsEquipable(race_id, class_id)) { fail_state = swapRaceClass; return false; } + if (deity_id && destination_item->Deity && !(Deity::GetBitmask(deity_id) & destination_item->Deity)) { fail_state = swapDeity; return false; } + if (level && destination_item->ReqLevel && level < destination_item->ReqLevel) { fail_state = swapLevel; return false; @@ -449,10 +446,8 @@ bool EQ::InventoryProfile::DeleteItem(int16 slot_id, int16 quantity) { bool EQ::InventoryProfile::CheckNoDrop(int16 slot_id, bool recurse) { ItemInstance* inst = GetItem(slot_id); - if (!inst) - return false; - return (!inst->IsDroppable(recurse)); + return inst ? !inst->IsDroppable(recurse) : false; } // Remove item from bucket without memory delete @@ -463,40 +458,32 @@ EQ::ItemInstance* EQ::InventoryProfile::PopItem(int16 slot_id) if (slot_id == invslot::slotCursor) { p = m_cursor.pop(); - } - else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + } else if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { p = m_worn[slot_id]; m_worn.erase(slot_id); - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { p = m_inv[slot_id]; m_inv.erase(slot_id); - } - else if (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END)) { p = m_worn[slot_id]; m_worn.erase(slot_id); - } - else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, 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) { + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { p = m_bank[slot_id]; m_bank.erase(slot_id); - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { p = m_shbank[slot_id]; m_shbank.erase(slot_id); - } - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { p = m_trade[slot_id]; m_trade.erase(slot_id); - } - else { - // Is slot inside bag? - ItemInstance* baginst = GetItem(InventoryProfile::CalcSlotId(slot_id)); - if (baginst != nullptr && baginst->IsClassBag()) { - p = baginst->PopItem(InventoryProfile::CalcBagIdx(slot_id)); + } else { + // Is slot inside bag? + ItemInstance* bag_inst = GetItem(InventoryProfile::CalcSlotId(slot_id)); + if (bag_inst && bag_inst->IsClassBag()) { + p = bag_inst->PopItem(InventoryProfile::CalcBagIdx(slot_id)); } } @@ -504,43 +491,49 @@ EQ::ItemInstance* EQ::InventoryProfile::PopItem(int16 slot_id) return p; } -bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quantity) { - +bool EQ::InventoryProfile::HasSpaceForItem(const ItemData* ItemToTry, int16 Quantity) +{ if (ItemToTry->Stackable) { - for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { - if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) { continue; + } - ItemInstance* InvItem = GetItem(i); - - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + ItemInstance* inv_item = GetItem(i); - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + if ( + inv_item && + inv_item->GetItem()->ID == ItemToTry->ID && + inv_item->GetCharges() < inv_item->GetItem()->StackSize + ) { + int charges_left = inv_item->GetItem()->StackSize - inv_item->GetCharges(); - if (Quantity <= ChargeSlotsLeft) + if (Quantity <= charges_left) { return true; + } - Quantity -= ChargeSlotsLeft; - + Quantity -= charges_left; } - if (InvItem && InvItem->IsClassBag()) { - int16 BaseSlotID = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); - uint8 BagSize = InvItem->GetItem()->BagSlots; - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlot < BagSize; BagSlot++) { + if (inv_item && inv_item->IsClassBag()) { + int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + uint8 bag_slots = inv_item->GetItem()->BagSlots; - InvItem = GetItem(BaseSlotID + BagSlot); + for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_slots; bag_slot++) { + inv_item = GetItem(base_slot_id + bag_slot); - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && - (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + if ( + inv_item && + inv_item->GetItem()->ID == ItemToTry->ID && + inv_item->GetCharges() < inv_item->GetItem()->StackSize + ) { + int charges_left = inv_item->GetItem()->StackSize - inv_item->GetCharges(); - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); - - if (Quantity <= ChargeSlotsLeft) + if (Quantity <= charges_left) { return true; + } - Quantity -= ChargeSlotsLeft; + Quantity -= charges_left; } } } @@ -548,51 +541,46 @@ bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quan } for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) { - if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) { continue; + } - ItemInstance* InvItem = GetItem(i); - - if (!InvItem) { + ItemInstance* inv_item = GetItem(i); + if (!inv_item) { if (!ItemToTry->Stackable) { - - if (Quantity == 1) + if (Quantity == 1) { return true; - else + } else { Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) + } + } else { + if (Quantity <= ItemToTry->StackSize) { return true; - else + } else { Quantity -= ItemToTry->StackSize; + } } + } else if (inv_item->IsClassBag() && CanItemFitInContainer(ItemToTry, inv_item->GetItem())) { + int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + uint8 bag_slots = inv_item->GetItem()->BagSlots; - } - else if (InvItem->IsClassBag() && CanItemFitInContainer(ItemToTry, InvItem->GetItem())) { - - int16 BaseSlotID = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); - - uint8 BagSize = InvItem->GetItem()->BagSlots; - - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlot < BagSize; BagSlot++) { - - InvItem = GetItem(BaseSlotID + BagSlot); + for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_slots; bag_slot++) { + inv_item = GetItem(base_slot_id + bag_slot); - if (!InvItem) { + if (!inv_item) { if (!ItemToTry->Stackable) { - - if (Quantity == 1) + if (Quantity == 1) { return true; - else + } else { Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) + } + } else { + if (Quantity <= ItemToTry->StackSize) { return true; - else + } else { Quantity -= ItemToTry->StackSize; + } } } } @@ -600,7 +588,6 @@ bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quan } return false; - } // Checks that user has at least 'quantity' number of items in a given inventory slot @@ -680,40 +667,46 @@ int16 EQ::InventoryProfile::HasItem(uint32 item_id, uint8 quantity, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItem(m_worn, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItem(m_inv, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItem(m_bank, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItem(m_shbank, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItem(m_trade, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItem(m_cursor, item_id, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -727,40 +720,46 @@ int16 EQ::InventoryProfile::HasItemByUse(uint8 use, uint8 quantity, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItemByUse(m_worn, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItemByUse(m_inv, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItemByUse(m_bank, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItemByUse(m_shbank, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItemByUse(m_trade, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByUse(m_cursor, use, quantity); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -773,40 +772,46 @@ int16 EQ::InventoryProfile::HasItemByLoreGroup(uint32 loregroup, uint8 where) // Check each inventory bucket if (where & invWhereWorn) { slot_id = _HasItemByLoreGroup(m_worn, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWherePersonal) { slot_id = _HasItemByLoreGroup(m_inv, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereBank) { slot_id = _HasItemByLoreGroup(m_bank, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereSharedBank) { slot_id = _HasItemByLoreGroup(m_shbank, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } if (where & invWhereTrading) { slot_id = _HasItemByLoreGroup(m_trade, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByLoreGroup(m_cursor, loregroup); - if (slot_id != INVALID_INDEX) + if (slot_id != INVALID_INDEX) { return slot_id; + } } return slot_id; @@ -834,7 +839,7 @@ int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 mi continue; } - const auto *inst = GetItem(i); + const EQ::ItemInstance* inst = GetItem(i); if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) { if (inst->GetItem()->BagType == item::BagTypeQuiver && inst->GetItem()->ItemType != item::ItemTypeArrow) { @@ -842,8 +847,8 @@ int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 mi } const int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN); + const uint8 slots = inst->GetItem()->BagSlots; - const uint8 slots = inst->GetItem()->BagSlots; for (uint8 j = invbag::SLOT_BEGIN; j < slots; j++) { if (!GetItem(base_slot_id + j)) { // Found available slot within bag @@ -871,22 +876,25 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // // I'll probably implement a bitmask in the new inventory system to avoid having to adjust stack bias - if ((general_start < invslot::GENERAL_BEGIN) || (general_start > invslot::GENERAL_END)) - return INVALID_INDEX; - if (bag_start > invbag::SLOT_END) - return INVALID_INDEX; - - if (!inst || !inst->GetID()) + if ( + !EQ::ValueWithin(general_start, invslot::GENERAL_BEGIN, invslot::GENERAL_END) || + bag_start > invbag::SLOT_END || + !inst || + !inst->GetID() + ) { return INVALID_INDEX; + } // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) if (inst->IsClassBag()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } - if (!m_inv[free_slot]) + if (!m_inv[free_slot]) { return free_slot; + } } return invslot::slotCursor; // return cursor since bags do not stack and will not fit inside other bags..yet...) @@ -895,37 +903,55 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 2: find partial room for stackables if (inst->IsStackable()) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { continue; + } - if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize)) + if ( + main_inst->GetID() == inst->GetID() && + main_inst->GetCharges() < main_inst->GetItem()->StackSize + ) { return free_slot; + } } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { continue; + } if (main_inst->IsClassBag()) { // if item-specific containers already have bad items, we won't fix it here... uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { const ItemInstance* sub_inst = main_inst->GetItem(free_bag_slot); - if (!sub_inst) + if (!sub_inst) { continue; + } - if ((sub_inst->GetID() == inst->GetID()) && (sub_inst->GetCharges() < sub_inst->GetItem()->StackSize)) + if ( + sub_inst->GetID() == inst->GetID() && + sub_inst->GetCharges() < sub_inst->GetItem()->StackSize + ) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -934,18 +960,30 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 3a: find room for container-specific items (ItemClassArrow) if (inst->GetItem()->ItemType == item::ItemTypeArrow) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeQuiver) || !main_inst->IsClassBag()) + if ( + !main_inst || + main_inst->GetItem()->BagType != item::BagTypeQuiver || + !main_inst->IsClassBag() + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -953,47 +991,73 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i // step 3b: find room for container-specific items (ItemClassSmallThrowing) if (inst->GetItem()->ItemType == item::ItemTypeSmallThrowing) { for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst || (main_inst->GetItem()->BagType != item::BagTypeBandolier) || !main_inst->IsClassBag()) + if ( + !main_inst || + main_inst->GetItem()->BagType != item::BagTypeBandolier || + !main_inst->IsClassBag() + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } // step 4: just find an empty slot for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; - if (!main_inst) + if (!main_inst) { return free_slot; + } } for (int16 free_slot = general_start; free_slot <= invslot::GENERAL_END; ++free_slot) { - if ((((uint64)1 << free_slot) & m_lookup->PossessionsBitmask) == 0) + if ((((uint64) 1 << free_slot) & m_lookup->PossessionsBitmask) == 0) { continue; + } const ItemInstance* main_inst = m_inv[free_slot]; if (main_inst && main_inst->IsClassBag()) { - if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == item::BagTypeBandolier) || (main_inst->GetItem()->BagType == item::BagTypeQuiver)) + if ( + main_inst->GetItem()->BagSize < inst->GetItem()->Size || + main_inst->GetItem()->BagType == item::BagTypeBandolier || + main_inst->GetItem()->BagType == item::BagTypeQuiver + ) { continue; + } uint8 _bag_start = (free_slot > general_start) ? invbag::SLOT_BEGIN : bag_start; - for (uint8 free_bag_slot = _bag_start; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) + + for ( + uint8 free_bag_slot = _bag_start; + (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot <= invbag::SLOT_END); + ++free_bag_slot + ) { + if (!main_inst->GetItem(free_bag_slot)) { return InventoryProfile::CalcSlotId(free_slot, free_bag_slot); + } } } } @@ -1003,7 +1067,8 @@ int16 EQ::InventoryProfile::FindFreeSlotForTradeItem(const ItemInstance* inst, i } // Opposite of below: Get parent bag slot_id from a slot inside of bag -int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { +int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) +{ int16 parent_slot_id = INVALID_INDEX; // this is not a bag range... using this risks over-writing existing items @@ -1011,19 +1076,15 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { // parent_slot_id = EmuConstants::BANK_BEGIN + (slot_id - EmuConstants::BANK_BEGIN) / EmuConstants::ITEM_CONTAINER_SIZE; //else if (slot_id >= 3100 && slot_id <= 3179) should be {3031..3110}..where did this range come from!!? (verified db save range) - if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { parent_slot_id = invslot::GENERAL_BEGIN + (slot_id - invbag::GENERAL_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { parent_slot_id = invslot::slotCursor; - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { parent_slot_id = invslot::BANK_BEGIN + (slot_id - invbag::BANK_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { parent_slot_id = invslot::SHARED_BANK_BEGIN + (slot_id - invbag::SHARED_BANK_BAGS_BEGIN) / invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { parent_slot_id = invslot::TRADE_BEGIN + (slot_id - invbag::TRADE_BAGS_BEGIN) / invbag::SLOT_COUNT; } @@ -1031,26 +1092,24 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 slot_id) { } // Calculate slot_id for an item within a bag -int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { - if (!InventoryProfile::SupportsContainers(bagslot_id)) +int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) +{ + if (!InventoryProfile::SupportsContainers(bagslot_id)) { return INVALID_INDEX; + } int16 slot_id = INVALID_INDEX; if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; - } - else if (bagslot_id >= invslot::GENERAL_BEGIN && bagslot_id <= invslot::GENERAL_END) { - slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::BANK_BEGIN && bagslot_id <= invslot::BANK_END) { - slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invslot::BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::SHARED_BANK_BEGIN && bagslot_id <= invslot::SHARED_BANK_END) { - slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invslot::SHARED_BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; - } - else if (bagslot_id >= invslot::TRADE_BEGIN && bagslot_id <= invslot::TRADE_END) { - slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invslot::TRADE_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { + slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invbag::GENERAL_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { + slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invbag::BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { + slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invbag::SHARED_BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + } else if (EQ::ValueWithin(bagslot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { + slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invbag::TRADE_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; } return slot_id; @@ -1059,26 +1118,17 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) { uint8 EQ::InventoryProfile::CalcBagIdx(int16 slot_id) { uint8 index = 0; - // this is not a bag range... using this risks over-writing existing items - //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) - // index = (slot_id - EmuConstants::BANK_BEGIN) % EmuConstants::ITEM_CONTAINER_SIZE; - - if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { index = (slot_id - invbag::GENERAL_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::CURSOR_BAG_BEGIN && slot_id <= invbag::CURSOR_BAG_END) { + } else if (EQ::ValueWithin(slot_id, invbag::CURSOR_BAG_BEGIN, invbag::CURSOR_BAG_END)) { index = (slot_id - invbag::CURSOR_BAG_BEGIN); // % invbag::SLOT_COUNT; - not needed since range is 10 slots - } - else if (slot_id >= invbag::BANK_BAGS_BEGIN && slot_id <= invbag::BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::BANK_BAGS_BEGIN, invbag::BANK_BAGS_END)) { index = (slot_id - invbag::BANK_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::SHARED_BANK_BAGS_BEGIN && slot_id <= invbag::SHARED_BANK_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::SHARED_BANK_BAGS_BEGIN, invbag::SHARED_BANK_BAGS_END)) { index = (slot_id - invbag::SHARED_BANK_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invbag::TRADE_BAGS_BEGIN && slot_id <= invbag::TRADE_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::TRADE_BAGS_BEGIN, invbag::TRADE_BAGS_END)) { index = (slot_id - invbag::TRADE_BAGS_BEGIN) % invbag::SLOT_COUNT; - } - else if (slot_id >= invslot::WORLD_BEGIN && slot_id <= invslot::WORLD_END) { + } else if (EQ::ValueWithin(slot_id, invslot::WORLD_BEGIN, invslot::WORLD_END)) { index = (slot_id - invslot::WORLD_BEGIN); // % invbag::SLOT_COUNT; - not needed since range is 10 slots } @@ -1114,45 +1164,53 @@ int16 EQ::InventoryProfile::CalcSlotFromMaterial(uint8 material) uint8 EQ::InventoryProfile::CalcMaterialFromSlot(int16 equipslot) { - switch (equipslot) - { - case invslot::slotHead: - return textures::armorHead; - case invslot::slotChest: - return textures::armorChest; - case invslot::slotArms: - return textures::armorArms; - case invslot::slotWrist1: - //case SLOT_BRACER02: // non-live behavior - return textures::armorWrist; - case invslot::slotHands: - return textures::armorHands; - case invslot::slotLegs: - return textures::armorLegs; - case invslot::slotFeet: - return textures::armorFeet; - case invslot::slotPrimary: - return textures::weaponPrimary; - case invslot::slotSecondary: - return textures::weaponSecondary; - default: - return textures::materialInvalid; + switch (equipslot) { + case invslot::slotHead: + return textures::armorHead; + case invslot::slotChest: + return textures::armorChest; + case invslot::slotArms: + return textures::armorArms; + case invslot::slotWrist1: + return textures::armorWrist; + case invslot::slotHands: + return textures::armorHands; + case invslot::slotLegs: + return textures::armorLegs; + case invslot::slotFeet: + return textures::armorFeet; + case invslot::slotPrimary: + return textures::weaponPrimary; + case invslot::slotSecondary: + return textures::weaponSecondary; + default: + return textures::materialInvalid; } } bool EQ::InventoryProfile::CanItemFitInContainer(const ItemData *ItemToTry, const ItemData *Container) { - if (!ItemToTry || !Container) + if (!ItemToTry || !Container) { return false; + } - if (ItemToTry->Size > Container->BagSize) + if (ItemToTry->Size > Container->BagSize) { return false; + } - if ((Container->BagType == item::BagTypeQuiver) && (ItemToTry->ItemType != item::ItemTypeArrow)) + if ( + Container->BagType == item::BagTypeQuiver && + ItemToTry->ItemType != item::ItemTypeArrow + ) { return false; + } - if ((Container->BagType == item::BagTypeBandolier) && (ItemToTry->ItemType != item::ItemTypeSmallThrowing)) + if ( + Container->BagType == item::BagTypeBandolier && + ItemToTry->ItemType != item::ItemTypeSmallThrowing + ) { return false; + } return true; } @@ -1160,15 +1218,14 @@ bool EQ::InventoryProfile::CanItemFitInContainer(const ItemData *ItemToTry, cons bool EQ::InventoryProfile::SupportsClickCasting(int16 slot_id) { // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range - if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { return true; - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { return true; - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { - if (inventory::StaticLookup(m_mob_version)->AllowClickCastFromBag) + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { + if (inventory::StaticLookup(m_mob_version)->AllowClickCastFromBag) { return true; + } } return false; @@ -1177,13 +1234,11 @@ bool EQ::InventoryProfile::SupportsClickCasting(int16 slot_id) bool EQ::InventoryProfile::SupportsPotionBeltCasting(int16 slot_id) { // does this have the same criteria as 'SupportsClickCasting' above? (bag clicking per client) - if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { + if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { return true; - } - else if (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { return true; - } - else if (slot_id >= invbag::GENERAL_BAGS_BEGIN && slot_id <= invbag::GENERAL_BAGS_END) { + } else if (EQ::ValueWithin(slot_id, invbag::GENERAL_BAGS_BEGIN, invbag::GENERAL_BAGS_END)) { return true; } @@ -1193,11 +1248,12 @@ bool EQ::InventoryProfile::SupportsPotionBeltCasting(int16 slot_id) // Test whether a given slot can support a container item bool EQ::InventoryProfile::SupportsContainers(int16 slot_id) { - if ((slot_id == invslot::slotCursor) || - (slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END) || - (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) || - (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) || - (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) + if ( + slot_id == invslot::slotCursor || + EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END) || + EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END) || + EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END) || + EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END) ) { return true; } @@ -1246,84 +1302,65 @@ uint8 EQ::InventoryProfile::FindBrightestLightType() uint8 brightest_light_type = 0; for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { - if ((iter->first < invslot::EQUIPMENT_BEGIN || iter->first > invslot::EQUIPMENT_END)) + if (!EQ::ValueWithin(iter->first, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { continue; + } - if (iter->first == invslot::slotAmmo) + if (iter->first == invslot::slotAmmo) { continue; + } - auto inst = iter->second; - if (inst == nullptr) + EQ::ItemInstance* inst = iter->second; + if (!inst) { continue; + } - auto item = inst->GetItem(); - if (item == nullptr) + const EQ::ItemData* item = inst->GetItem(); + if (!item) { continue; + } - if (lightsource::IsLevelGreater(item->Light, brightest_light_type)) + if (lightsource::IsLevelGreater(item->Light, brightest_light_type)) { brightest_light_type = item->Light; + } } uint8 general_light_type = 0; for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { - if (iter->first < invslot::GENERAL_BEGIN || iter->first > invslot::GENERAL_END) + if (!EQ::ValueWithin(iter->first, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { continue; + } - auto inst = iter->second; - if (inst == nullptr) + EQ::ItemInstance* inst = iter->second; + if (!inst) { continue; + } - auto item = inst->GetItem(); - if (item == nullptr) + const EQ::ItemData* item = inst->GetItem(); + if (!item) { continue; + } - if (!item->IsClassCommon()) + if (!item->IsClassCommon()) { continue; - if (item->Light < 9 || item->Light > 13) + } + + if (!EQ::ValueWithin(item->Light, 9, 13)) { continue; + } - if (lightsource::TypeToLevel(item->Light)) + if (lightsource::TypeToLevel(item->Light)) { general_light_type = item->Light; + } } - if (lightsource::IsLevelGreater(general_light_type, brightest_light_type)) + if (lightsource::IsLevelGreater(general_light_type, brightest_light_type)) { brightest_light_type = general_light_type; + } return brightest_light_type; } -void EQ::InventoryProfile::dumpEntireInventory() { - - dumpWornItems(); - dumpInventory(); - dumpBankItems(); - dumpSharedBankItems(); - - std::cout << std::endl; -} - -void EQ::InventoryProfile::dumpWornItems() { - std::cout << "Worn items:" << std::endl; - dumpItemCollection(m_worn); -} - -void EQ::InventoryProfile::dumpInventory() { - std::cout << "Inventory items:" << std::endl; - dumpItemCollection(m_inv); -} - -void EQ::InventoryProfile::dumpBankItems() { - - std::cout << "Bank items:" << std::endl; - dumpItemCollection(m_bank); -} - -void EQ::InventoryProfile::dumpSharedBankItems() { - - std::cout << "Shared Bank items:" << std::endl; - dumpItemCollection(m_shbank); -} - int EQ::InventoryProfile::GetSlotByItemInstCollection(const std::map &collection, ItemInstance *inst) { for (auto iter = collection.begin(); iter != collection.end(); ++iter) { ItemInstance *t_inst = iter->second; @@ -1343,57 +1380,22 @@ int EQ::InventoryProfile::GetSlotByItemInstCollection(const std::map &collection) -{ - for (auto it = collection.cbegin(); it != collection.cend(); ++it) { - auto inst = it->second; - if (!inst || !inst->GetItem()) - continue; - - std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); - std::cout << slot << std::endl; - - dumpBagContents(inst, &it); - } -} - -void EQ::InventoryProfile::dumpBagContents(ItemInstance *inst, std::map::const_iterator *it) -{ - if (!inst || !inst->IsClassBag()) - return; - - // Go through bag, if bag - for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { - ItemInstance* baginst = itb->second; - if (!baginst || !baginst->GetItem()) - continue; - - std::string subSlot = StringFormat(" Slot %d: %s (%d)", InventoryProfile::CalcSlotId((*it)->first, itb->first), - baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges()); - std::cout << subSlot << std::endl; - } - -} - // Internal Method: Retrieves item within an inventory bucket EQ::ItemInstance* EQ::InventoryProfile::_GetItem(const std::map& bucket, int16 slot_id) const { - if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(slot_id, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) == 0) { return nullptr; - } - else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { - if (slot_id - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(slot_id, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (slot_id - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { return nullptr; + } } auto it = bucket.find(slot_id); - if (it != bucket.end()) { - return it->second; - } - // Not found! - return nullptr; + return it != bucket.end() ? it->second : nullptr; } // Internal Method: "put" item into bucket, without regard for what is currently in bucket @@ -1403,14 +1405,14 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... // // If putting a nullptr into slot, we need to remove slot without memory delete - if (inst == nullptr) { + if (!inst) { //Why do we not delete the poped item here???? PopItem(slot_id); return slot_id; } - int16 result = INVALID_INDEX; - int16 parentSlot = INVALID_INDEX; + int16 result = INVALID_INDEX; + int16 parent_slot = INVALID_INDEX; inst->SetEvolveEquipped(false); @@ -1419,57 +1421,49 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) m_cursor.pop(); // no memory delete, clients of this function know what they are doing m_cursor.push_front(inst); result = slot_id; - } - else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + } else if (EQ::ValueWithin(slot_id, invslot::EQUIPMENT_BEGIN, invslot::EQUIPMENT_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { if (inst->IsEvolving()) { inst->SetEvolveEquipped(true); } + m_worn[slot_id] = inst; result = slot_id; } - } - else if ((slot_id >= invslot::GENERAL_BEGIN && slot_id <= invslot::GENERAL_END)) { - if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + } else if (EQ::ValueWithin(slot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { + if ((((uint64) 1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { m_inv[slot_id] = inst; result = slot_id; } - } - else if (slot_id >= invslot::TRIBUTE_BEGIN && slot_id <= invslot::TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRIBUTE_BEGIN, invslot::TRIBUTE_END)) { m_worn[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::GUILD_TRIBUTE_BEGIN && slot_id <= invslot::GUILD_TRIBUTE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::GUILD_TRIBUTE_BEGIN, invslot::GUILD_TRIBUTE_END)) { m_worn[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::BANK_BEGIN && slot_id <= invslot::BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { if (slot_id - EQ::invslot::BANK_BEGIN < m_lookup->InventoryTypeSize.Bank) { m_bank[slot_id] = inst; result = slot_id; } - } - else if (slot_id >= invslot::SHARED_BANK_BEGIN && slot_id <= invslot::SHARED_BANK_END) { + } else if (EQ::ValueWithin(slot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { m_shbank[slot_id] = inst; result = slot_id; - } - else if (slot_id >= invslot::TRADE_BEGIN && slot_id <= invslot::TRADE_END) { + } else if (EQ::ValueWithin(slot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { m_trade[slot_id] = inst; result = slot_id; - } - else { + } else { // Slot must be within a bag - parentSlot = InventoryProfile::CalcSlotId(slot_id); - ItemInstance* baginst = GetItem(parentSlot); // Get parent bag - if (baginst && baginst->IsClassBag()) - { + parent_slot = InventoryProfile::CalcSlotId(slot_id); + ItemInstance* baginst = GetItem(parent_slot); // Get parent bag + if (baginst && baginst->IsClassBag()) { baginst->_PutItem(InventoryProfile::CalcBagIdx(slot_id), inst); result = slot_id; } } if (result == INVALID_INDEX) { - LogError("Invalid slot_id specified ({}) with parent slot id ({})", slot_id, parentSlot); + LogError("Invalid slot_id specified ({}) with parent slot id ({})", slot_id, parent_slot); InventoryProfile::MarkDirty(inst); // Slot not found, clean up } @@ -1482,44 +1476,55 @@ int16 EQ::InventoryProfile::_HasItem(std::map& bucket, uin uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } if (inst->GetID() == item_id) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return iter->first; + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->GetID() == item_id) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } } @@ -1539,35 +1544,45 @@ int16 EQ::InventoryProfile::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint uint32 quantity_found = 0; for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } if (inst->GetID() == item_id) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return invslot::slotCursor; + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->GetID() == item_id) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } @@ -1584,34 +1599,43 @@ int16 EQ::InventoryProfile::_HasItemByUse(std::map& bucket uint32 quantity_found = 0; for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return iter->first; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } } } } @@ -1625,25 +1649,33 @@ int16 EQ::InventoryProfile::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint uint32 quantity_found = 0; for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return invslot::slotCursor; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) + if (quantity_found >= quantity) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } } } @@ -1657,44 +1689,57 @@ int16 EQ::InventoryProfile::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint int16 EQ::InventoryProfile::_HasItemByLoreGroup(std::map& bucket, uint32 loregroup) { for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - if (iter->first <= EQ::invslot::POSSESSIONS_END && iter->first >= EQ::invslot::POSSESSIONS_BEGIN) { - if ((((uint64)1 << iter->first) & m_lookup->PossessionsBitmask) == 0) + if (EQ::ValueWithin(iter->first, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if ((((uint64) 1 << iter->first) & m_lookup->PossessionsBitmask) == 0) { continue; - } - else if (iter->first <= EQ::invslot::BANK_END && iter->first >= EQ::invslot::BANK_BEGIN) { - if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) + } + } else if (EQ::ValueWithin(iter->first, EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END)) { + if (iter->first - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { continue; + } } - auto inst = iter->second; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = iter->second; + if (!inst) { + continue; + } - if (inst->GetItem()->LoreGroup == loregroup) + if (inst->GetItem()->LoreGroup == loregroup) { return iter->first; + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { auto aug_inst = inst->GetAugment(index); if (aug_inst == nullptr) { continue; } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) { return InventoryProfile::CalcSlotId(iter->first, bag_iter->first); + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = bag_inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } } @@ -1706,35 +1751,49 @@ int16 EQ::InventoryProfile::_HasItemByLoreGroup(std::map& int16 EQ::InventoryProfile::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) { for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } + EQ::ItemInstance* inst = *iter; + if (!inst) { + continue; + } - if (inst->GetItem()->LoreGroup == loregroup) + if (inst->GetItem()->LoreGroup == loregroup) { return invslot::slotCursor; + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } - if (!inst->IsClassBag()) { continue; } + if (!inst->IsClassBag()) { + continue; + } for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } + EQ::ItemInstance* bag_inst = bag_iter->second; + if (!bag_inst) { + continue; + } - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) { return InventoryProfile::CalcSlotId(invslot::slotCursor, bag_iter->first); + } for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } + EQ::ItemInstance* aug_inst = bag_inst->GetAugment(index); + if (!aug_inst) { + continue; + } - if (aug_inst->GetItem()->LoreGroup == loregroup) + if (aug_inst->GetItem()->LoreGroup == loregroup) { return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } } } @@ -1759,42 +1818,9 @@ std::vector EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id) return augments; } -std::vector EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::ItemData *item_data) -{ - std::vector free_slots{}; - for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { - if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) { - continue; - } - - EQ::ItemInstance *inv_item = GetItem(i); - - if (!inv_item) { - // Found available slot in personal inventory - free_slots.push_back(i); - } - - if (inv_item->IsClassBag() && - EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) { - - int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN); - uint8 bag_size = inv_item->GetItem()->BagSlots; - - for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) { - auto bag_item = GetItem(base_slot_id + bag_slot); - if (!bag_item) { - // Found available slot within bag - free_slots.push_back(i); - } - } - } - } - return free_slots; -} - int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data) { - for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { + for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) { continue; } diff --git a/common/inventory_profile.h b/common/inventory_profile.h index b1d1e4850e..92e9a153d2 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -178,7 +178,6 @@ namespace EQ // Locate an available inventory slot int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN); - std::vector FindAllFreeSlotsThatFitItem(const EQ::ItemData *inst); int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst); // Calculate slot_id for an item within a bag @@ -201,12 +200,6 @@ namespace EQ uint8 FindBrightestLightType(); - void dumpEntireInventory(); - void dumpWornItems(); - void dumpInventory(); - void dumpBankItems(); - void dumpSharedBankItems(); - void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, const std::string& value); void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, int value); void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, float value); @@ -224,8 +217,6 @@ namespace EQ /////////////////////////////// int GetSlotByItemInstCollection(const std::map &collection, ItemInstance *inst); - void dumpItemCollection(const std::map &collection); - void dumpBagContents(ItemInstance *inst, std::map::const_iterator *it); // Retrieves item within an inventory bucket ItemInstance* _GetItem(const std::map& bucket, int16 slot_id) const; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 94f5c70da4..ccd361cd51 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1658,11 +1658,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.abilitySlotRefresh = 0; } -#ifdef _EQDEBUG - printf("Dumping inventory on load:\n"); - m_inv.dumpEntireInventory(); -#endif - /* Reset to max so they dont drown on zone in if its underwater */ m_pp.air_remaining = 60; /* Check for PVP Zone status*/ From 7ebd9b94aa5fe17204e337cb290714a619c96992 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 25 Oct 2024 21:49:12 -0400 Subject: [PATCH 70/73] Cleanup --- common/inventory_profile.cpp | 8 +-- common/shareddb.cpp | 2 +- zone/client_process.cpp | 77 +++++++++++++++++++---------- zone/gm_commands/show/inventory.cpp | 8 +-- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index b65de87926..a6e438004f 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -1103,13 +1103,13 @@ int16 EQ::InventoryProfile::CalcSlotId(int16 bagslot_id, uint8 bagidx) if (bagslot_id == invslot::slotCursor) { slot_id = invbag::CURSOR_BAG_BEGIN + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::GENERAL_BEGIN, invslot::GENERAL_END)) { - slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invbag::GENERAL_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::GENERAL_BAGS_BEGIN + (bagslot_id - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::BANK_BEGIN, invslot::BANK_END)) { - slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invbag::BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::BANK_BAGS_BEGIN + (bagslot_id - invslot::BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::SHARED_BANK_BEGIN, invslot::SHARED_BANK_END)) { - slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invbag::SHARED_BANK_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::SHARED_BANK_BAGS_BEGIN + (bagslot_id - invslot::SHARED_BANK_BEGIN) * invbag::SLOT_COUNT + bagidx; } else if (EQ::ValueWithin(bagslot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { - slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invbag::TRADE_BAGS_BEGIN) * invbag::SLOT_COUNT + bagidx; + slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invslot::TRADE_BEGIN) * invbag::SLOT_COUNT + bagidx; } return slot_id; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 653efc35c4..20d6a055f3 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -852,7 +852,7 @@ bool SharedDatabase::GetInventory(Client *c) } int16 put_slot_id; - if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END) || slot_id == EQ::invslot::slotCursor) { + if (EQ::ValueWithin(slot_id, EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END)) { put_slot_id = inv->PushCursor(*inst); } else { put_slot_id = inv->PutItem(slot_id, *inst); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b2146dd813..d93f2ad99a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -779,47 +779,70 @@ void Client::BulkSendInventoryItems() inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) + if (ob.tellp() == last_pos) { LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + + last_pos = ob.tellp(); + } + + // General items + for (int16 slot_id = EQ::invslot::GENERAL_BEGIN; slot_id <= EQ::invslot::GENERAL_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } + + inst->Serialize(ob, slot_id); + + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } last_pos = ob.tellp(); } if (!RuleB(Inventory, LazyLoadBank)) { - // Bank items - for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { - const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + // Bank items + for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } - inst->Serialize(ob, slot_id); + inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } - last_pos = ob.tellp(); - } + last_pos = ob.tellp(); + } - // SharedBank items - for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) { - const EQ::ItemInstance* inst = m_inv[slot_id]; - if (!inst) - continue; + // SharedBank items + for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) { + const EQ::ItemInstance* inst = m_inv[slot_id]; + if (!inst) { + continue; + } - inst->Serialize(ob, slot_id); + inst->Serialize(ob, slot_id); - if (ob.tellp() == last_pos) - LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + if (ob.tellp() == last_pos) { + LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id); + } + + last_pos = ob.tellp(); + } + } + + auto outapp = new EQApplicationPacket(OP_CharInventory); - last_pos = ob.tellp(); - } - } + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); - auto outapp = new EQApplicationPacket(OP_CharInventory); - outapp->size = ob.size(); - outapp->pBuffer = ob.detach(); - QueuePacket(outapp); - safe_delete(outapp); + QueuePacket(outapp); + safe_delete(outapp); } void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { diff --git a/zone/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index 2078f2bdd6..2cffdc82f0 100644 --- a/zone/gm_commands/show/inventory.cpp +++ b/zone/gm_commands/show/inventory.cpp @@ -310,7 +310,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), item_data->ID, linker.GenerateLink(), ( @@ -339,7 +339,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} (Augment Slot {}) | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), augment_index, linker.GenerateLink(), item_data->ID, @@ -375,7 +375,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), sub_index, linker.GenerateLink(), item_data->ID, @@ -407,7 +407,7 @@ void ShowInventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}", - (EQ::invbag::CURSOR_BAG_BEGIN + limboIndex), + (14000 + limboIndex), sub_index, augment_index, linker.GenerateLink(), From 7fdb4f9eca4284beaf0fb065f2366ed0b7caa840 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Thu, 21 Nov 2024 23:19:41 -0500 Subject: [PATCH 71/73] Add slot ID conversions --- common/database/database_update_manifest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 2bb2de881d..f8639743c6 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6380,6 +6380,7 @@ CHANGE COLUMN `ornamenticon` `ornament_icon` int(11) UNSIGNED NOT NULL DEFAULT 0 CHANGE COLUMN `ornamentidfile` `ornament_idfile` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_icon`, DROP PRIMARY KEY, ADD PRIMARY KEY (`character_id`, `slot_id`) USING BTREE; + UPDATE `inventory` SET `slot_id` = ((`slot_id` - 251) + 4010) WHERE `slot_id` BETWEEN 251 AND 260; -- Bag 1 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 261) + 4210) WHERE `slot_id` BETWEEN 261 AND 270; -- Bag 2 UPDATE `inventory` SET `slot_id` = ((`slot_id` - 271) + 4410) WHERE `slot_id` BETWEEN 271 AND 280; -- Bag 3 @@ -6417,6 +6418,8 @@ UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2270; -- Bank Bags +UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags )" }, ManifestEntry{ From 4b633130a438669ea5ba3ee366e4caf4104d47ae Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:44:41 -0500 Subject: [PATCH 72/73] Update shareddb.cpp --- common/shareddb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 20d6a055f3..d0ec6025a1 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -449,7 +449,7 @@ bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) return SharedbankRepository::DeleteWhere( *this, fmt::format( - "`account_id` = {} AND `slotid` BETWEEN {} AND {}", + "`account_id` = {} AND `slot_id` BETWEEN {} AND {}", account_id, base_slot_id, base_slot_id + (EQ::invbag::SLOT_COUNT - 1) From dc622b59da85afd0d6eccb4bb11b2d195cd22172 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 14 Dec 2024 13:25:01 -0500 Subject: [PATCH 73/73] Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index f8639743c6..bb5b10b5ba 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6418,8 +6418,6 @@ UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2251) + 10610) WHERE `slot_id` UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2261) + 10810) WHERE `slot_id` BETWEEN 2261 AND 2270; -- Bank Bag 24 UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2540; -- Shared Bank Bag 1 UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2 -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2031) + 6210) WHERE `slot_id` BETWEEN 2031 AND 2270; -- Bank Bags -UPDATE `inventory` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id` BETWEEN 2531 AND 2550; -- Shared Bank Bags )" }, ManifestEntry{