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/CHANGELOG.md b/CHANGELOG.md index 14e5cd22c2..a7ea71c2fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,102 @@ +## [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 + +* 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/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/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..c8105eb83a 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 @@ -97,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 @@ -172,6 +174,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 +243,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 +359,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 +428,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 +561,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/bazaar.cpp b/common/bazaar.cpp index 7891828213..cce3fcdd3b 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,26 +27,49 @@ Bazaar::GetSearchResults( search.trader_entity_id, search.trader_id, search.search_scope, - char_zone_id + char_zone_id, + char_zone_instance_id ); + bool convert = false; std::string search_criteria_trader("TRUE "); 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)); + 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)); } @@ -62,7 +86,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 +146,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 [{}]", @@ -347,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/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/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/database.cpp b/common/database.cpp index c0193b9277..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; @@ -1860,7 +1867,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 +1903,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 +1957,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 +1993,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 +2007,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/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/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 636899094e..bb5b10b5ba 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5770,6 +5770,665 @@ 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`; +)" + }, + 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 + }, + 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 + }, + 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 + }, + 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, + }, + 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 + }, + 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; +)", + }, + 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 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/database_schema.h b/common/database_schema.h index e0b87b94d3..9f23001b18 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"}, @@ -63,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"}, @@ -124,6 +126,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 +215,7 @@ namespace DatabaseSchema { "ground_spawns", "horses", "items", + "items_evolving_details", "ldon_trap_entries", "ldon_trap_templates", "lootdrop", 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..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 @@ -352,9 +354,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 +750,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/emu_oplist.h b/common/emu_oplist.h index aa8190720d..eefcdfee87 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), @@ -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), @@ -162,6 +163,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), @@ -283,12 +285,15 @@ 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), N(OP_ItemName), N(OP_ItemPacket), N(OP_ItemPreview), +N(OP_ItemPreviewRequest), N(OP_ItemRecastDelay), N(OP_ItemVerifyReply), N(OP_ItemVerifyRequest), @@ -400,6 +405,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 +521,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_constants.h b/common/eq_constants.h index 44beffd25b..e048a9321e 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, @@ -1132,4 +1114,5 @@ enum ExpSource namespace DoorType { constexpr uint32 BuyerStall = 155; } + #endif /*COMMON_EQ_CONSTANTS_H*/ 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/eq_packet_structs.h b/common/eq_packet_structs.h index 322bdf6392..f6e2b9ed1b 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]; @@ -5818,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 { @@ -6347,6 +6363,7 @@ enum BazaarTraderBarterActions { TraderAck2 = 22, AddTraderToBazaarWindow = 24, RemoveTraderFromBazaarWindow = 25, + FirstOpenSearch = 26, ClickTrader = 28, DeliveryCostUpdate = 29 }; @@ -6386,6 +6403,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 +6425,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), @@ -6436,6 +6455,90 @@ 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; +}; + +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_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(); 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/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index c4c14f82f5..18ef3efcc9 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 @@ -85,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); @@ -532,6 +535,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/eqemu_logsys.h b/common/eqemu_logsys.h index 8bf474f349..0697739599 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -142,6 +142,9 @@ namespace Logs { EqTime, Corpses, XTargets, + EvolveItem, + PositionUpdate, + KSM, MaxCategoryID /* Don't Remove this */ }; @@ -242,7 +245,10 @@ namespace Logs { "Zoning", "EqTime", "Corpses", - "XTargets" + "XTargets", + "EvolveItem", + "PositionUpdate", + "KSM" // Kernel Samepage Merging }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 10c9cd98a2..4f1eadde7f 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__);\ @@ -844,6 +854,26 @@ 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 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 {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __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 df81e516c4..a6e438004f 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,24 +335,32 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } - if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { + + source_item_instance->SetEvolveEquipped(false); + 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; } + if (source_item_instance->IsEvolving() > 0) { + source_item_instance->SetEvolveEquipped(true); + } } } @@ -370,24 +369,32 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } - if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { + + destination_item_instance->SetEvolveEquipped(false); + 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; } + if (destination_item_instance->IsEvolving()) { + destination_item_instance->SetEvolveEquipped(true); + } } } @@ -439,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 @@ -453,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)); } } @@ -494,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++) { - 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 +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); + for (uint8 bag_slot = invbag::SLOT_BEGIN; bag_slot < bag_slots; bag_slot++) { + inv_item = GetItem(base_slot_id + bag_slot); - uint8 BagSize = InvItem->GetItem()->BagSlots; - - for (uint8 BagSlot = invbag::SLOT_BEGIN; BagSlot < BagSize; BagSlot++) { - - InvItem = GetItem(BaseSlotID + BagSlot); - - 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 +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 @@ -612,9 +609,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 +640,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) { @@ -670,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; @@ -717,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; @@ -763,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; @@ -824,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) { @@ -832,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 @@ -861,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...) @@ -885,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); + } } } } @@ -924,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); + } } } } @@ -943,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); + } } } } @@ -993,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 @@ -1001,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; } @@ -1021,25 +1092,23 @@ 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 || 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) { + } else if (EQ::ValueWithin(bagslot_id, invslot::GENERAL_BEGIN, 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) { + } else if (EQ::ValueWithin(bagslot_id, invslot::BANK_BEGIN, 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) { + } else if (EQ::ValueWithin(bagslot_id, invslot::SHARED_BANK_BEGIN, 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) { + } else if (EQ::ValueWithin(bagslot_id, invslot::TRADE_BEGIN, invslot::TRADE_END)) { slot_id = invbag::TRADE_BAGS_BEGIN + (bagslot_id - invslot::TRADE_BEGIN) * invbag::SLOT_COUNT + bagidx; } @@ -1049,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 } @@ -1104,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; } @@ -1150,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; @@ -1167,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; } @@ -1183,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; } @@ -1236,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; @@ -1333,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 @@ -1393,68 +1405,65 @@ 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); 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) { + 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 } @@ -1467,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; + } } } } @@ -1524,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; + } } } @@ -1569,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); + } } } } @@ -1610,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); + } } } @@ -1642,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; + } } } } @@ -1691,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; + } } } @@ -1744,10 +1818,9 @@ std::vector EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id) return augments; } -std::vector EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::ItemData *item_data) +int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data) { - std::vector free_slots{}; - 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; } @@ -1756,7 +1829,7 @@ std::vector EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::I if (!inv_item) { // Found available slot in personal inventory - free_slots.push_back(i); + return i; } if (inv_item->IsClassBag() && @@ -1769,42 +1842,189 @@ std::vector EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::I auto bag_item = GetItem(base_slot_id + bag_slot); if (!bag_item) { // Found available slot within bag - free_slots.push_back(i); + return base_slot_id + bag_slot; } } } } - return free_slots; + return 0; } -int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data) +//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) { - for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { - if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) { + 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; } - EQ::ItemInstance *inv_item = GetItem(i); + 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 (!inv_item) { - // Found available slot in personal inventory - return i; + + if (value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += value->GetCharges() <= 0 ? 1 : value->GetCharges(); + if (quantity_found >= quantity) { + return key; + } } - if (inv_item->IsClassBag() && - EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) { + 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; + } + } - int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN); - uint8 bag_size = inv_item->GetItem()->BagSlots; + if (!value->IsClassBag()) { + continue; + } - 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 - return base_slot_id + bag_slot; + 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 0; -} \ No newline at end of file + + 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 100d5ebd8d..92e9a153d2 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(); } @@ -147,13 +149,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); @@ -176,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 @@ -199,26 +200,23 @@ 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); 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 /////////////////////////////// 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; @@ -233,6 +231,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/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/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) 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/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/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 9024f93b93..3e05972e65 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); @@ -487,10 +489,11 @@ 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(); + in->pBuffer = (uchar*)buffer; dest->QueuePacket(in); + safe_delete(in); break; } @@ -500,21 +503,22 @@ 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; 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(); + in->pBuffer = (uchar *)buffer; LogTrading("(RoF2) WelcomeMessage action [{}]", action); dest->QueuePacket(in); + safe_delete(in); break; } @@ -583,19 +587,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; } @@ -890,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: { @@ -961,6 +969,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity); dest->QueuePacket(outapp.get()); + safe_delete(inapp); break; } default: { @@ -1341,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); @@ -1843,11 +1904,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) @@ -2044,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); @@ -6218,6 +6306,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); @@ -6358,9 +6451,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{}; @@ -6382,6 +6484,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 @@ -6395,18 +6502,15 @@ namespace RoF2 ob.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader)); if (item->EvolvingItem > 0) { - RoF2::structs::EvolvingItem evotop; + RoF2::structs::EvolvingItem_Struct 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; + 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)); + ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem_Struct)); } /** @@ -6780,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_limits.h b/common/patches/rof2_limits.h index 7e7774de65..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); @@ -272,6 +304,7 @@ namespace RoF2 const size_t SAY_LINK_BODY_SIZE = 56; const uint32 MAX_GUILD_ID = 50000; + const uint32 MAX_BAZAAR_TRADERS = 600; } /*constants*/ @@ -291,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, @@ -314,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/rof2_ops.h b/common/patches/rof2_ops.h index 08d8b99857..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 @@ -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) @@ -91,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/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index fa43b65b3c..4a8d277ebe 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 { @@ -4745,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 @@ -5427,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/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/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/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 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/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_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/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/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_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/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/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/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 0e23c0b85f..1b42931098 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -111,12 +111,13 @@ 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; uint8_t idle_when_empty; uint32_t seconds_before_idle; + int32_t shard_at_player_count; }; static std::string PrimaryKey() @@ -219,12 +220,13 @@ 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", "idle_when_empty", "seconds_before_idle", + "shard_at_player_count", }; } @@ -323,12 +325,13 @@ 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", "idle_when_empty", "seconds_before_idle", + "shard_at_player_count", }; } @@ -461,12 +464,13 @@ 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; e.idle_when_empty = 1; e.seconds_before_idle = 60; + e.shard_at_player_count = 0; return e; } @@ -595,12 +599,13 @@ 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; 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; } @@ -725,12 +730,13 @@ 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)); 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( @@ -844,12 +850,13 @@ 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)); 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( @@ -971,12 +978,13 @@ 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)); 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) + ")"); } @@ -1102,12 +1110,13 @@ 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; 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); } @@ -1224,12 +1233,13 @@ 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; 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); } @@ -1396,12 +1406,13 @@ 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)); 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( @@ -1516,12 +1527,13 @@ 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)); 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/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/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/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/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/repositories/trader_repository.h b/common/repositories/trader_repository.h index 6c6ab35d2b..a8e3c13c17 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -12,10 +12,12 @@ class TraderRepository : public BaseTraderRepository { public: + static constexpr uint32 TRADER_CONVERT_ID = 4000000000; struct DistinctTraders_Struct { uint32 trader_id; uint32 zone_id; + uint32 zone_instance_id; uint32 entity_id; std::string trader_name; }; @@ -35,18 +37,28 @@ 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) + 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; - auto results = db.QueryDatabase( - "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_entity_id, c.name " + 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 " + "WHERE t.char_zone_instance_id = {} " + "ORDER BY t.char_zone_instance_id ASC " + "LIMIT {}", + char_zone_instance_id, + max_results) ); distinct_traders.reserve(results.RowCount()); @@ -54,10 +66,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); @@ -221,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 b2de14baa7..af9e6215d7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -340,6 +340,10 @@ 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_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) @@ -369,6 +373,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) @@ -681,6 +686,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) @@ -825,6 +831,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) @@ -1033,6 +1040,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/servertalk.h b/common/servertalk.h index 46a3e08bc9..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, @@ -1945,6 +1943,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/common/shareddb.cpp b/common/shareddb.cpp index ac7a3a173a..d0ec6025a1 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -37,17 +37,20 @@ #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" #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 +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 + ) + ); - 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(); - // Save bag contents, if slot supports bag contents - if (inst->IsClassBag() && EQ::InventoryProfile::SupportsContainers(slot_id)) + 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)) { // 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; + } + + if (!EQ::InventoryProfile::SupportsContainers(slot_id)) { + return true; + } + + const int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(slot_id, EQ::invbag::SLOT_BEGIN); - // @merth: need to delete augments here - return true; + return SharedbankRepository::DeleteWhere( + *this, + fmt::format( + "`account_id` = {} AND `slot_id` BETWEEN {} AND {}", + account_id, + base_slot_id, + base_slot_id + (EQ::invbag::SLOT_COUNT - 1) + ) + ); } @@ -550,169 +579,159 @@ 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 + ) + ); - 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); } } 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("`character_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; } - 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_BEGIN, EQ::invslot::POSSESSIONS_END)) { // 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_BEGIN, EQ::invslot::BANK_END)) { // 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 +740,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 +751,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 +764,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 +778,83 @@ 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) { - 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 + 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; + } ); - put_slot_id = inv->PushCursor(*inst); + + 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); + } } - else { + + int16 put_slot_id; + 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); } @@ -811,7 +866,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,13 +875,13 @@ 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, char_id, - EQ::versions::MobVersionName(inv->InventoryVersion()), - (inv->GMInventory() ? "true" : "false") + EQ::versions::MobVersionName(inv.InventoryVersion()), + (inv.GMInventory() ? "true" : "false") ); } @@ -837,95 +892,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) EQ::ItemInstance::ClearGUIDMap(); // Retrieve shared inventory - 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); + return GetSharedBank(char_id, &inv, true); } std::map SharedDatabase::GetItemRecastTimestamps(uint32 char_id) @@ -1279,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/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/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) } } 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 c615c7c854..b86cb155fc 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.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__ @@ -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 9296 #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/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/package.json b/package.json index f2590babbe..68a2da55e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eqemu-server", - "version": "22.59.1", + "version": "22.61.0", "repository": { "type": "git", "url": "https://github.com/EQEmu/Server.git" 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 0995b83bab..a4e6d68ed9 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 @@ -566,7 +567,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 +729,18 @@ OP_InitialHPUpdate=0x0000 #aura related OP_UpdateAura=0x1456 OP_RemoveTrap=0x71da + +OP_PickZoneWindow=0x72d8 +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/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= 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/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/world/cli/copy_character.cpp b/world/cli/copy_character.cpp index 7d915ad15e..ebe8e1831b 100644 --- a/world/cli/copy_character.cpp +++ b/world/cli/copy_character.cpp @@ -11,7 +11,7 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std "destination_account_name" }; std::vector options = {}; - + if (cmd[{"-h", "--help"}]) { return; } 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(); } 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/world/client.cpp b/world/client.cpp index a6f5cfb5f4..e75d8824d0 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(); @@ -2369,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/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/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/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/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); } 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/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/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 6ffe44099e..3de17a13ce 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( "{} {}", @@ -2818,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 && @@ -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 7871f9ac9f..b0f32b406f 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; @@ -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())) { @@ -4192,14 +4193,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 +4258,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 @@ -7403,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 d0a8d97b0b..30e3be5d69 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" @@ -157,7 +158,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), @@ -329,7 +330,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; @@ -402,6 +403,7 @@ Client::~Client() { mMovementManager->RemoveClient(this); + DataBucket::DeleteCachedBuckets(DataBucketLoadType::Account, AccountID()); DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID()); if (RuleB(Bots, Enabled)) { @@ -1469,7 +1471,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 +1524,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 +1541,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 +1553,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 +1565,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 +1577,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 +1625,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; } @@ -1827,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()); @@ -2208,7 +2210,7 @@ void Client::Stand() { } void Client::Sit() { - SetAppearance(eaSitting, false); + SetAppearance(eaSitting, false); } void Client::ChangeLastName(std::string last_name) { @@ -4391,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, @@ -5627,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); } @@ -5676,15 +5755,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 +5774,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 +5793,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 +5810,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 +6306,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; } @@ -8366,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 @@ -9124,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 ) @@ -9136,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); @@ -9157,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 ) @@ -9169,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); @@ -9840,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; } @@ -9855,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); } @@ -10461,27 +10540,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 +10565,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 +10619,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 +10675,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 +12866,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); } } } @@ -12981,50 +13018,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); @@ -13075,3 +13139,169 @@ 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; +} + +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++; + } +} + +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(); +} + +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); + } + } + } +} + +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 5acc388b3e..d7a61f6622 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) @@ -294,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); @@ -306,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; } @@ -467,6 +473,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; } @@ -691,6 +698,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; } @@ -707,13 +716,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); @@ -1093,7 +1102,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); @@ -1127,6 +1136,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; } @@ -1809,6 +1822,25 @@ 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); + + // 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); @@ -1945,6 +1977,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; @@ -1967,6 +2000,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); @@ -2064,12 +2099,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(); @@ -2235,6 +2271,8 @@ class Client : public Mob public: 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_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 14ee222f34..ccd361cd51 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" @@ -80,7 +81,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 @@ -161,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; @@ -208,6 +209,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; @@ -283,6 +285,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; @@ -339,6 +342,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; @@ -524,7 +528,6 @@ int Client::HandlePacket(const EQApplicationPacket *app) // Finish client connecting state void Client::CompleteConnect() { - UpdateWho(); client_state = CLIENT_CONNECTED; SendAllPackets(); @@ -819,6 +822,10 @@ void Client::CompleteConnect() CharacterID() ) ); + + if (IsPetNameChangeAllowed()) { + InvokeChangePetName(false); + } } if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) { @@ -861,7 +868,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)); @@ -913,10 +920,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()) { @@ -946,6 +949,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()) { @@ -969,6 +977,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()); @@ -1308,7 +1326,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 */ @@ -1413,6 +1431,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 @@ -1639,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*/ @@ -1862,8 +1876,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(); @@ -2023,38 +2038,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" : "" ); } @@ -2198,19 +2213,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 << "|"; @@ -3251,30 +3266,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 +3305,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 +3381,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 +3453,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); @@ -3985,6 +3936,10 @@ void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) SendBazaarWelcome(); break; } + case FirstOpenSearch: { + SendBulkBazaarTraders(); + break; + } default: { LogError("Malformed BazaarSearch_Struct packet received, ignoring\n"); } @@ -4479,14 +4434,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 +4466,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)) { @@ -4628,6 +4569,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)) @@ -5026,7 +4988,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { CheckScanCloseMobsMovingTimer(); } - CheckSendBulkClientPositionUpdate(); + CheckSendBulkNpcPositions(); int32 new_animation = ppu->animation; @@ -8015,6 +7977,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(); @@ -9370,6 +9333,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; @@ -9591,14 +9578,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 +9642,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)) { @@ -10769,8 +10742,7 @@ void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) { - if (!CharacterID()) - { + if (!CharacterID()) { return; } @@ -10779,60 +10751,42 @@ 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; + 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) @@ -11930,6 +11884,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) { @@ -15603,6 +15568,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) { @@ -17238,3 +17223,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 57a4bfcf48..587e14ac7b 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); @@ -188,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); @@ -242,6 +244,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/client_process.cpp b/zone/client_process.cpp index c2e20fe5b6..d93f2ad99a 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()) @@ -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; @@ -770,65 +770,83 @@ 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); - 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(); } - 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; + // 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); + 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; + 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; + } - 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(); + } - auto outapp = new EQApplicationPacket(OP_CharInventory); - outapp->size = ob.size(); - outapp->pBuffer = ob.detach(); - QueuePacket(outapp); - safe_delete(outapp); -} + // 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; + } -void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { - const EQ::ItemData* handy_item = nullptr; + inst->Serialize(ob, slot_id); - 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; + 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); + + outapp->size = ob.size(); + outapp->pBuffer = ob.detach(); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { + const EQ::ItemData* handy_item = nullptr; const EQ::ItemData *item = nullptr; auto merchant_list = zone->merchanttable[merchant_id]; auto npc = entity_list.GetMobByNpcTypeID(npcid); @@ -840,6 +858,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/command.cpp b/zone/command.cpp index 513cd02180..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) || @@ -243,6 +244,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) ) { @@ -825,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" @@ -936,4 +939,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..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); @@ -195,6 +197,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/corpse.cpp b/zone/corpse.cpp index e54dd2e635..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) { @@ -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/data_bucket.cpp b/zone/data_bucket.cpp index d022183bc8..23d4eb9d6f 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) { @@ -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; } @@ -58,14 +62,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 +78,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 @@ -105,29 +99,35 @@ 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 ); - 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(); + } - LogDataBuckets("Returning key [{}] value [{}] from cache", ce.e.key_, ce.e.value); - return ce.e; + // 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", e.key_, e.value); + return e; + } } } @@ -144,29 +144,29 @@ 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, + .account_id = k.account_id, + .character_id = k.character_id, + .npc_id = k.npc_id, + .bot_id = k.bot_id } ); 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, @@ -178,111 +178,96 @@ 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(); + const uint32 id = mob->GetMobTypeIdentifier(); if (!id) { return false; } 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}); } - else if (mob->IsNPC()) { - t = DataBucketLoadType::NPC; - } - - BulkLoadEntities(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 [{}] 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, + g_data_bucket_cache.size() + ); + } return DataBucketsRepository::DeleteWhere( database, @@ -297,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 ); @@ -315,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 ); @@ -340,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)); } @@ -366,28 +360,30 @@ 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 ); } -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::Account) { + has_cache = e.account_id == ids[0]; } - else if (t == DataBucketLoadType::NPC) { - has_cache = ce.e.npc_id == ids[0]; + else if (t == DataBucketLoadType::Client) { + has_cache = e.character_id == ids[0]; } } @@ -406,8 +402,8 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector(t)); @@ -442,12 +438,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) +bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &entry) { - 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 +491,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 +501,12 @@ 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.account_id == e.account_id && + 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 +524,49 @@ 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; + case DataBucketLoadType::Account: + return e.account_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.account_id > 0 || key.bot_id > 0) { + return true; + } + + return false; +} diff --git a/zone/data_bucket.h b/zone/data_bucket.h index 4ae270e445..1bd2166307 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,48 +8,28 @@ #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; 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, - NPC, MaxType }; static const std::string Name[Type::MaxType] = { "Bot", + "Account", "Client", - "NPC", }; } @@ -68,8 +44,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 +54,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/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/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; } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index d18796a4e7..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; @@ -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); } @@ -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 4a9a232cad..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; @@ -1817,25 +1808,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); } } @@ -2890,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; } @@ -2943,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) { @@ -2953,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. @@ -2969,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 @@ -2981,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() ); } @@ -5771,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/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/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/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/gm_commands/find.cpp b/zone/gm_commands/find.cpp index c2e1ee5593..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" @@ -11,6 +12,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 +49,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", @@ -65,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 ) ); 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/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/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/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index c2d22b2d96..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 {} | {} ({}){}", - (8000 + 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 {}) | {} ({}){}", - (8000 + 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 {} | {} ({}){}", - (8000 + 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 {}) | {} ({}){}", - (8000 + limboIndex), + (14000 + limboIndex), sub_index, augment_index, linker.GenerateLink(), 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); } 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/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) 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); 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); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 30f4c31daf..afd366c234 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(); @@ -1909,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)){ @@ -1924,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)){ @@ -1943,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) { @@ -3641,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 @@ -3744,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/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..83e2a3fb3e 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,76 @@ 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; +} + +void Lua_Client::ShowZoneShardMenu() +{ + Lua_Safe_Call_Void(); + self->ShowZoneShardMenu(); +} + +void Lua_Client::GrantPetNameChange() +{ + Lua_Safe_Call_Void(); + self->GrantPetNameChange(); +} + +void Lua_Client::SetAAEXPPercentage(uint8 percentage) +{ + Lua_Safe_Call_Void(); + 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<>()) @@ -3504,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) @@ -3514,9 +3585,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) @@ -3524,6 +3595,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) @@ -3602,6 +3674,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) @@ -3650,6 +3725,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) @@ -3867,6 +3943,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) @@ -3892,6 +3969,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) @@ -3954,6 +4033,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 faff8b1572..ed02e65927 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,16 @@ 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 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); @@ -586,6 +596,8 @@ class Lua_Client : public Lua_Mob void DialogueWindow(std::string markdown); 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/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_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/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_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/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/lua_packet.cpp b/zone/lua_packet.cpp index c6a1ab6e20..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)), @@ -915,7 +916,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/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/map.cpp b/zone/map.cpp index 410392e8a7..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 @@ -320,7 +321,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 +339,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*/ @@ -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; @@ -1064,7 +1066,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 +1122,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; diff --git a/zone/mob.cpp b/zone/mob.cpp index a06dbc7432..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) { @@ -572,6 +573,8 @@ Mob::~Mob() m_close_mobs.clear(); + ClearDataBucketCache(); + LeaveHealRotationTargetPool(); } @@ -2048,19 +2051,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 +2071,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 +2091,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 +2112,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; @@ -5325,14 +5328,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; @@ -8613,3 +8609,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..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; @@ -1491,6 +1493,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/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"; }); } } 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 f1260cd9c9..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; @@ -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..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(); @@ -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; } 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(); 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..3cf1ad0d75 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 @@ -2209,7 +2214,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 +2393,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 +2403,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 +3217,58 @@ 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_Client_GrantPetNameChange(Client* self) +{ + self->GrantPetNameChange(); +} + +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); @@ -3283,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); @@ -3303,6 +3361,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); @@ -3340,6 +3399,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); @@ -3426,6 +3488,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); @@ -3639,11 +3702,14 @@ 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); 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); @@ -3729,6 +3795,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/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/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/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. diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 68bd859af1..cd44abf8b0 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); } @@ -1326,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) { @@ -3343,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); 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 diff --git a/zone/spells.cpp b/zone/spells.cpp index 7b644cd3ac..723c6833ef 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)) { @@ -652,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) { /* @@ -659,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; - } + bool bypass_casting_restrictions = !IsClient(); + glm::vec3 position = glm::vec3(GetPosition()); - 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() - ); - } - - /* - 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; } } @@ -1804,22 +1780,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 +3934,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 +3944,7 @@ bool Mob::SpellOnTarget( target_id ); }, - 0, - &args + 0, &args ); if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { @@ -7413,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/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 d3896fb091..bdbb6326c9 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; @@ -777,6 +780,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. @@ -1091,15 +1096,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); @@ -1794,7 +1800,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; @@ -2913,10 +2919,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); @@ -2937,9 +2944,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); @@ -3085,14 +3094,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); @@ -3212,7 +3222,45 @@ void Client::SendBulkBazaarTraders() return; } - auto results = TraderRepository::GetDistinctTraders(database); + 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); + auto p_size = 4 + 12 * results.count + results.name_length; auto buffer = std::make_unique(p_size); memset(buffer.get(), 0, p_size); @@ -3221,7 +3269,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()); @@ -3233,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 acfb686df6..995887f17f 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) @@ -3644,11 +3642,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; @@ -3919,30 +3912,61 @@ 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) { + 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; + 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: { out->action = 0; + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); } } - out->entity_id = in->entity_id; - out->zone_id = in->zone_id; - out->trader_id = in->trader_id; - strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); - 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); } @@ -4693,12 +4717,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; diff --git a/zone/zone.cpp b/zone/zone.cpp index 0280ee8f52..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); } @@ -1100,7 +1099,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 +1261,6 @@ void Zone::ReloadStaticData() { LoadVeteranRewards(); LoadAlternateCurrencies(); - npc_emote_list.clear(); LoadNPCEmotes(&npc_emote_list); //load the zone config file. @@ -1370,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); @@ -1534,10 +1537,6 @@ bool Zone::Process() { if (adv_data && !did_adventure_actions) { DoAdventureActions(); } - - if (GetNpcPositionUpdateDistance() == 0) { - CalculateNpcUpdateDistanceSpread(); - } } if (hot_reload_timer.Check() && IsQuestHotReloadQueued()) { @@ -2575,6 +2574,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); @@ -2727,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; 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); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 3f0ac20e8c..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 @@ -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); 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; 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(),