From 8a7d5e72cb527e74013f4a7d87b66d55e8d74e63 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:44:16 -0600 Subject: [PATCH 01/16] [Filesystem] Path Manager Improvements (#4557) * [Filesystem] Path Manager Improvements * Update path_manager.cpp * Use native fs path building syntax --- common/eqemu_logsys.cpp | 7 +++ common/path_manager.cpp | 118 ++++++++++++++++++---------------------- 2 files changed, 59 insertions(+), 66 deletions(-) diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index c4c14f82f..a6a3e283d 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -25,6 +25,8 @@ #include "repositories/discord_webhooks_repository.h" #include "repositories/logsys_categories_repository.h" #include "termcolor/rang.hpp" +#include "path_manager.h" +#include "file.h" #include #include @@ -532,6 +534,11 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name) { EQEmuLogSys::CloseFileLogs(); + if (!File::Exists(path.GetLogPath())) { + LogInfo("Logs directory not found, creating [{}]", path.GetLogPath()); + File::Makedir(path.GetLogPath()); + } + /** * When loading settings, we must have been given a reason in category based logging to output to a file in order to even create or open one... */ diff --git a/common/path_manager.cpp b/common/path_manager.cpp index 452722c84..b767b8003 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; -} - 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(); - } + 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(); + } - // 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(); - } + // absolute + if (File::Exists(fs::path{dir}.string())) { + return fs::absolute(fs::path{dir}).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(); - } + // 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(); + } + } - // 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(); - } + // if all else fails, just set it to the config value + return dir; + }; - // 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(); - } + 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"}); - // 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(); - } + // 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} + }; - // 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(); - } + constexpr int name_width = 15; + constexpr int path_width = 0; + constexpr int break_length = 70; - // 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(); + 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); + } } - - // logging path - if (File::Exists(fs::path{m_server_path + "/" + c->LogDir}.string())) { - m_log_path = fs::relative(fs::path{m_server_path + "/" + c->LogDir}).string(); - } - - LogInfo("logs path [{}]", m_log_path); - LogInfo("lua mods path [{}]", m_lua_mods_path); - LogInfo("lua_modules path [{}]", m_lua_modules_path); - LogInfo("maps path [{}]", m_maps_path); - LogInfo("patches path [{}]", m_patch_path); - LogInfo("opcode path [{}]", m_opcode_path); - LogInfo("plugins path [{}]", m_plugins_path); - LogInfo("quests path [{}]", m_quests_path); - LogInfo("shared_memory path [{}]", m_shared_memory_path); + LogInfo("{}", Strings::Repeat("-", break_length)); } const std::string &PathManager::GetServerPath() const From 20ff3250133a281014fe7c13a9af6fe32441aa65 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:44:51 -0400 Subject: [PATCH 02/16] [Fix] Repair a LoadNPCEmote MemoryLeak (#4586) --- zone/zone.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 0280ee8f5..f02be2e2b 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1100,7 +1100,12 @@ Zone::~Zone() { if (worldserver.Connected()) { worldserver.SetZoneData(0); } + + for (auto &e: npc_emote_list) { + safe_delete(e); + } npc_emote_list.clear(); + zone_point_list.Clear(); entity_list.Clear(); parse->ReloadQuests(); @@ -1257,7 +1262,6 @@ void Zone::ReloadStaticData() { LoadVeteranRewards(); LoadAlternateCurrencies(); - npc_emote_list.clear(); LoadNPCEmotes(&npc_emote_list); //load the zone config file. @@ -2575,6 +2579,10 @@ void Zone::DoAdventureActions() void Zone::LoadNPCEmotes(std::vector* v) { + for (auto &e: *v) { + safe_delete(e); + } + v->clear(); const auto& l = NpcEmotesRepository::All(content_db); From b40e4ce7cd73e5d5d7c14ced38e3157b07acf421 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:45:09 -0400 Subject: [PATCH 03/16] [Fix] Repair a memory leak in GuildsList (#4585) There was a leak generated when sending the GuildsList Co-authored-by: Mitch Freeman --- common/patches/rof2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 38106fd2a..68aaa7cfa 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1845,11 +1845,11 @@ namespace RoF2 } } - auto outapp = new EQApplicationPacket(OP_GuildsList); - outapp->size = packet_size; - outapp->pBuffer = buffer; + safe_delete_array(in->pBuffer); - dest->FastQueuePacket(&outapp); + in->pBuffer = buffer; + in->size = packet_size; + dest->FastQueuePacket(&in); } ENCODE(OP_GuildTributeDonateItem) From 33db85f2eefe15fac8f795aff5e3d3e554bfe83d Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:45:28 -0400 Subject: [PATCH 04/16] [Fix] Repair a EQEMUConfig Memory Leak (#4584) Co-authored-by: Mitch Freeman --- common/eqemu_config.h | 2 +- zone/zone_config.h | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/common/eqemu_config.h b/common/eqemu_config.h index 7e9e3b341..0ce295ece 100644 --- a/common/eqemu_config.h +++ b/common/eqemu_config.h @@ -137,9 +137,9 @@ class EQEmuConfig { } - virtual ~EQEmuConfig() {} public: + virtual ~EQEmuConfig() {} // Produce a const singleton static const EQEmuConfig *get() diff --git a/zone/zone_config.h b/zone/zone_config.h index a8e7401b6..e527e0601 100644 --- a/zone/zone_config.h +++ b/zone/zone_config.h @@ -43,11 +43,13 @@ class ZoneConfig : public EQEmuConfig { } // Load the config - static bool LoadConfig(const std::string& path = "") { - if (_zone_config != nullptr) - delete _zone_config; - _zone_config=new ZoneConfig; - _config=_zone_config; + static bool LoadConfig(const std::string &path = "") + { + safe_delete(_zone_config); + safe_delete(_config); + + _zone_config = new ZoneConfig; + _config = _zone_config; return _config->parseFile(path); } From c82dee575a46ce55567063157d118515b194d36c Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:46:19 -0400 Subject: [PATCH 05/16] [Fix] Guild Membership Update Fix (#4581) When the guild membership was large (1k+) the client would studder for half a sec when a guild member would login or logout. This was reproduceable with a guild size of 2k members though floor would be client dependent most likely. --- zone/client_packet.cpp | 2 +- zone/client_process.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4afe67935..52211dbed 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -860,7 +860,7 @@ void Client::CompleteConnect() if (IsInAGuild()) { if (firstlogon == 1) { guild_mgr.UpdateDbMemberOnline(CharacterID(), true); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + SendGuildMembersList(); } guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c2e20fe5b..fd0028268 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -179,7 +179,7 @@ bool Client::Process() { } if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); @@ -202,7 +202,7 @@ bool Client::Process() { Save(); if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } if (GetMerc()) @@ -578,7 +578,7 @@ bool Client::Process() { } if (IsInAGuild()) { guild_mgr.UpdateDbMemberOnline(CharacterID(), false); - guild_mgr.SendToWorldSendGuildMembersList(GuildID()); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); } return false; From 4c6aaa6995b94d12b414fb90c69c07e323947af2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:46:43 -0600 Subject: [PATCH 06/16] [Logs] Improve Crash log defaults (#4579) --- common/crash.cpp | 2 ++ common/eqemu_logsys.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/common/crash.cpp b/common/crash.cpp index cb02a0bb4..3828a9af0 100644 --- a/common/crash.cpp +++ b/common/crash.cpp @@ -294,6 +294,8 @@ void print_trace() SendCrashReport(crash_report); } + LogSys.CloseFileLogs(); + exit(1); } diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index a6a3e283d..18ef3efcc 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -87,6 +87,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() * Set Defaults */ log_settings[Logs::Crash].log_to_console = static_cast(Logs::General); + log_settings[Logs::Crash].log_to_file = static_cast(Logs::General); log_settings[Logs::MySQLError].log_to_console = static_cast(Logs::General); log_settings[Logs::NPCScaling].log_to_gmsay = static_cast(Logs::General); log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); From 3155b82abbbaa731a278a7e80642b03fdaeddc75 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:46:59 -0600 Subject: [PATCH 07/16] [Command] Fix #copycharacter (#4582) * [Command] Fix #copycharacter * Update copy_character.cpp --- common/database.cpp | 50 +++++++++++++++++++++++++++++++++++- world/cli/copy_character.cpp | 4 +-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index c0193b927..a3405970d 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1860,7 +1860,35 @@ bool Database::CopyCharacter( const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1); - std::vector tables_to_zero_id = { "keyring", "data_buckets", "character_instance_safereturns" }; + // validate destination name doesn't exist already + const auto& destination_characters = CharacterDataRepository::GetWhere( + *this, + fmt::format( + "`name` = '{}' AND `deleted_at` IS NULL LIMIT 1", + Strings::Escape(destination_character_name) + ) + ); + if (!destination_characters.empty()) { + LogError("Character [{}] already exists", destination_character_name); + return false; + } + + std::vector tables_to_zero_id = { + "keyring", + "data_buckets", + "character_instance_safereturns", + "character_expedition_lockouts", + "character_instance_lockouts", + "character_parcels", + "character_tribute", + "player_titlesets", + }; + + std::vector ignore_tables = { + "guilds", + }; + + size_t total_rows_copied = 0; TransactionBegin(); @@ -1868,6 +1896,10 @@ bool Database::CopyCharacter( const std::string& table_name = t.first; const std::string& character_id_column_name = t.second; + if (Strings::Contains(ignore_tables, table_name)) { + continue; + } + auto results = QueryDatabase( fmt::format( "SHOW COLUMNS FROM {}", @@ -1918,6 +1950,10 @@ bool Database::CopyCharacter( value = std::to_string(destination_account_id); } + if (!Strings::IsNumber(value)) { + value = Strings::Escape(value); + } + new_values.emplace_back(value); } @@ -1950,6 +1986,11 @@ bool Database::CopyCharacter( ) ); + size_t rows_copied = insert_rows.size(); // Rows copied for this table + total_rows_copied += rows_copied; // Increment grand total + + LogInfo("Copying table [{}] rows [{}]", table_name, Strings::Commify(rows_copied)); + if (!insert.ErrorMessage().empty()) { TransactionRollback(); return false; @@ -1959,6 +2000,13 @@ bool Database::CopyCharacter( TransactionCommit(); + LogInfo( + "Character [{}] copied to [{}] total rows [{}]", + source_character_name, + destination_character_name, + Strings::Commify(total_rows_copied) + ); + return true; } diff --git a/world/cli/copy_character.cpp b/world/cli/copy_character.cpp index 7d915ad15..86c268217 100644 --- a/world/cli/copy_character.cpp +++ b/world/cli/copy_character.cpp @@ -12,12 +12,12 @@ void WorldserverCLI::CopyCharacter(int argc, char **argv, argh::parser &cmd, std }; std::vector options = {}; + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + if (cmd[{"-h", "--help"}]) { return; } - EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); - std::string source_character_name = cmd(2).str(); std::string destination_character_name = cmd(3).str(); std::string destination_account_name = cmd(4).str(); From fe43d26dd6979d309c2fad60c4d756bc27d5050d Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:47:22 -0400 Subject: [PATCH 08/16] [Fix] Guild creation to propagate across zones (#4575) Fix guild creation to propagate across zones --- zone/client_packet.cpp | 1 + zone/guild_mgr.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 52211dbed..4175d56ff 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -7937,6 +7937,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) SetGuildID(new_guild_id); SendGuildList(); guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName()); + guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true); guild_mgr.SendToWorldSendGuildList(); SendGuildSpawnAppearance(); diff --git a/zone/guild_mgr.h b/zone/guild_mgr.h index 3256d82d2..00ca287b9 100644 --- a/zone/guild_mgr.h +++ b/zone/guild_mgr.h @@ -83,9 +83,9 @@ public: void SendRankName(uint32 guild_id, uint32 rank, std::string rank_name); void SendAllRankNames(uint32 guild_id, uint32 char_id); BaseGuildManager::GuildInfo* GetGuildByGuildID(uint32 guild_id); + virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); protected: - virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); virtual void SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid); virtual void SendRankUpdate(uint32 CharID); virtual void SendGuildDelete(uint32 guild_id); From c44596b38a01acfb0b9389f499e274e2f516787b Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:48:00 -0600 Subject: [PATCH 09/16] [Network] Prune / disconnect TCP connections gracefully (#4574) --- common/net/tcp_connection.cpp | 2 ++ loginserver/world_server.cpp | 12 ------------ loginserver/world_server.h | 7 ------- world/login_server.cpp | 10 ---------- world/login_server.h | 1 - world/queryserv.cpp | 6 ------ world/queryserv.h | 1 - world/ucs.cpp | 12 ------------ world/ucs.h | 5 ----- world/zonelist.cpp | 8 -------- world/zonelist.h | 1 - zone/worldserver.cpp | 8 -------- zone/worldserver.h | 2 -- 13 files changed, 2 insertions(+), 73 deletions(-) diff --git a/common/net/tcp_connection.cpp b/common/net/tcp_connection.cpp index 0c2512dcc..ae1f14bad 100644 --- a/common/net/tcp_connection.cpp +++ b/common/net/tcp_connection.cpp @@ -80,6 +80,8 @@ void EQ::Net::TCPConnection::Start() { } } else if (nread == UV_EOF) { + connection->Disconnect(); + if (buf->base) { delete[] buf->base; } diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index b43651fd7..e4b7ac358 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 df5324240..45cecfebd 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -187,13 +187,6 @@ private: bool m_is_server_logged_in; bool m_is_server_trusted; - /** - * Keepalive - * @param t - */ - void OnKeepAlive(EQ::Timer *t); - std::unique_ptr m_keepalive; - static void FormatWorldServerName(char *name, int8 server_list_type); }; diff --git a/world/login_server.cpp b/world/login_server.cpp index b2017f094..b647aedf0 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 21e25870f..0a5e6a107 100644 --- a/world/login_server.h +++ b/world/login_server.h @@ -50,7 +50,6 @@ private: void ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p); void ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p); - void OnKeepAlive(EQ::Timer *t); std::unique_ptr m_keepalive; std::unique_ptr m_client; diff --git a/world/queryserv.cpp b/world/queryserv.cpp index 0611e7d50..847cb90e0 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 a02aab647..028fdcbbd 100644 --- a/world/queryserv.h +++ b/world/queryserv.h @@ -15,7 +15,6 @@ public: 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 0af363051..0cc70a0ea 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 d226e4dc9..04ec17471 100644 --- a/world/ucs.h +++ b/world/ucs.h @@ -23,11 +23,6 @@ private: inline std::string GetIP() const { return (connection && connection->Handle()) ? connection->Handle()->RemoteIP() : 0; } std::shared_ptr connection; - /** - * Keepalive - */ - std::unique_ptr m_keepalive; - void OnKeepAlive(EQ::Timer *t); }; #endif /*UCS_H_*/ diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 31427552a..c2e572a68 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 71d364c9b..e096f0363 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -72,7 +72,6 @@ public: private: void OnTick(EQ::Timer *t); - void OnKeepAlive(EQ::Timer *t); uint32 NextID; uint16 pLockedZones[MaxLockedZones]; uint32 CurGroupID; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 143b690a3..768fe4a4f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -91,8 +91,6 @@ void WorldServer::Connect() }); m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2)); - - m_keepalive = std::make_unique(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); } bool WorldServer::SendPacket(ServerPacket *pack) @@ -4690,12 +4688,6 @@ void WorldServer::RequestTellQueue(const char *who) return; } -void WorldServer::OnKeepAlive(EQ::Timer *t) -{ - ServerPacket pack(ServerOP_KeepAlive, 0); - SendPacket(&pack); -} - ZoneEventScheduler *WorldServer::GetScheduler() const { return m_zone_scheduler; diff --git a/zone/worldserver.h b/zone/worldserver.h index 49b151400..de3ac5e97 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -73,8 +73,6 @@ private: uint32 cur_groupid; uint32 last_groupid; - void OnKeepAlive(EQ::Timer *t); - std::unique_ptr m_connection; std::unique_ptr m_keepalive; From a3a498634f12ef0579be56e714d4fe3063312a8c Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Jan 2025 23:48:09 -0600 Subject: [PATCH 10/16] [Maps] Fix broken Map MMFS implementation (#4576) --- CMakeLists.txt | 3 +++ zone/map.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83dacc20f..f93cabb22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ IF(EQEMU_ADD_PROFILER) SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed,-lprofiler,--as-needed") ENDIF(EQEMU_ADD_PROFILER) +IF(USE_MAP_MMFS) + ADD_DEFINITIONS(-DUSE_MAP_MMFS) +ENDIF (USE_MAP_MMFS) IF(MSVC) ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) diff --git a/zone/map.cpp b/zone/map.cpp index 410392e8a..96feddb9c 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -320,7 +320,7 @@ bool Map::Load(const std::string &filename) } #ifdef USE_MAP_MMFS - if (v) + if (loaded_map_file) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ @@ -338,7 +338,7 @@ bool Map::Load(const std::string &filename) } #ifdef USE_MAP_MMFS - if (v) + if (loaded_map_file) return SaveMMF(filename, force_mmf_overwrite); #endif /*USE_MAP_MMFS*/ @@ -1064,7 +1064,7 @@ bool Map::LoadMMF(const std::string& map_file_name, bool force_mmf_overwrite) fclose(f); std::vector rm_buffer(rm_buffer_size); - uint32 v = InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); + uint32 v = EQ::InflateData(mmf_buffer.data(), mmf_buffer_size, rm_buffer.data(), rm_buffer_size); if (imp) { imp->rm->release(); @@ -1120,11 +1120,11 @@ bool Map::SaveMMF(const std::string& map_file_name, bool force_mmf_overwrite) } uint32 rm_buffer_size = rm_buffer.size(); - uint32 mmf_buffer_size = EstimateDeflateBuffer(rm_buffer.size()); + uint32 mmf_buffer_size = EQ::EstimateDeflateBuffer(rm_buffer.size()); std::vector mmf_buffer(mmf_buffer_size); - mmf_buffer_size = DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); + mmf_buffer_size = EQ::DeflateData(rm_buffer.data(), rm_buffer.size(), mmf_buffer.data(), mmf_buffer.size()); if (!mmf_buffer_size) { LogInfo("Failed to save Map MMF file: [{}] - null MMF buffer size", mmf_file_name.c_str()); return false; From 1ed282f6ff995c29bd915062f3ef33355c150b50 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 6 Jan 2025 00:48:39 -0500 Subject: [PATCH 11/16] [Inventory] Add GetInventorySlots() Method (#4566) * [Inventory] Add GetInventorySlots() Method * Update client.cpp * Push --- common/inventory_profile.cpp | 10 +- common/inventory_profile.h | 4 +- zone/client.cpp | 221 +++++++++++++--------------- zone/client.h | 3 +- zone/corpse.cpp | 6 +- zone/corpse.h | 2 +- zone/embparser_api.cpp | 2 +- zone/gm_commands/emptyinventory.cpp | 40 ++--- zone/loot.cpp | 4 +- zone/lua_bot.cpp | 7 +- zone/lua_bot.h | 4 +- zone/lua_client.cpp | 28 +++- zone/lua_client.h | 7 +- zone/lua_corpse.cpp | 4 +- zone/lua_corpse.h | 2 +- zone/lua_inventory.cpp | 8 +- zone/lua_inventory.h | 4 +- zone/lua_npc.cpp | 4 +- zone/lua_npc.h | 2 +- zone/npc.h | 2 +- zone/perl_bot.cpp | 6 +- zone/perl_client.cpp | 19 ++- zone/perl_inventory.cpp | 4 +- zone/perl_npc.cpp | 2 +- zone/perl_player_corpse.cpp | 2 +- zone/questmgr.cpp | 2 +- zone/questmgr.h | 2 +- 27 files changed, 204 insertions(+), 197 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index df81e516c..595b9f025 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -612,9 +612,9 @@ bool EQ::InventoryProfile::HasAugmentEquippedByID(uint32 item_id) return has_equipped; } -int EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) +uint32 EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; ItemInstance* item = nullptr; for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { @@ -643,9 +643,9 @@ bool EQ::InventoryProfile::HasItemEquippedByID(uint32 item_id) return has_equipped; } -int EQ::InventoryProfile::CountItemEquippedByID(uint32 item_id) +uint32 EQ::InventoryProfile::CountItemEquippedByID(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; ItemInstance* item = nullptr; for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { @@ -1807,4 +1807,4 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it } } return 0; -} \ No newline at end of file +} diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 100d5ebd8..4761e1b97 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -147,13 +147,13 @@ namespace EQ bool HasItemEquippedByID(uint32 item_id); // Check how many of a specific item the player has equipped by Item ID - int CountItemEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); // Check if player has a specific augment equipped by Item ID bool HasAugmentEquippedByID(uint32 item_id); // Check how many of a specific augment the player has equipped by Item ID - int CountAugmentEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); // Get a list of augments from a specific slot ID std::vector GetAugmentIDsBySlotID(int16 slot_id); diff --git a/zone/client.cpp b/zone/client.cpp index 920553f5c..c98946846 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10461,27 +10461,15 @@ void Client::SendToInstance(std::string instance_type, std::string zone_short_na MovePC(zone_id, instance_id, x, y, z, heading); } -int Client::CountItem(uint32 item_id) +uint32 Client::CountItem(uint32 item_id) { - int quantity = 0; + uint32 quantity = 0; EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - quantity += (item->IsStackable() ? item->GetCharges() : 1); - } + + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + quantity += (item->IsStackable() ? item->GetCharges() : 1); } } @@ -10498,30 +10486,26 @@ void Client::ResetItemCooldown(uint32 item_id) int recast_type = item_d->RecastType; bool found_item = false; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item) { - item_d = item->GetItem(); - if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) { - item->SetRecastTimestamp(0); - DeleteItemRecastTimer(item_d->ID); - SendItemPacket(slot_id, item, ItemPacketCharmUpdate); - found_item = true; - } + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item) { + item_d = item->GetItem(); + if ( + item_d && + item->GetID() == item_id || + ( + item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && + item_d->RecastType == recast_type + ) + ) { + item->SetRecastTimestamp(0); + DeleteItemRecastTimer(item_d->ID); + SendItemPacket(slot_id, item, ItemPacketCharmUpdate); + found_item = true; } } } + if (!found_item) { DeleteItemRecastTimer(item_id); //We didn't find the item but we still want to remove the timer } @@ -10556,25 +10540,20 @@ void Client::SetItemCooldown(uint32 item_id, bool use_saved_timer, uint32 in_sec final_time = total_time - current_time; } - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = GetInv().GetItem(slot_id); - if (item) { - item_d = item->GetItem(); - if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) { - item->SetRecastTimestamp(total_time); - SendItemPacket(slot_id, item, ItemPacketCharmUpdate); - } + for (const int16& slot_id : GetInventorySlots()) { + item = GetInv().GetItem(slot_id); + if (item) { + item_d = item->GetItem(); + if ( + item_d && + item->GetID() == item_id || + ( + item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && + item_d->RecastType == recast_type + ) + ) { + item->SetRecastTimestamp(total_time); + SendItemPacket(slot_id, item, ItemPacketCharmUpdate); } } } @@ -10617,38 +10596,26 @@ uint32 Client::GetItemCooldown(uint32 item_id) void Client::RemoveItem(uint32 item_id, uint32 quantity) { + uint32 removed_count = 0; EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - int16 removed_count = 0; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - if (removed_count == quantity) { - break; - } - item = GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - int16 charges = item->IsStackable() ? item->GetCharges() : 0; - int16 stack_size = std::max(charges, static_cast(1)); - if ((removed_count + stack_size) <= quantity) { - removed_count += stack_size; - DeleteItemInInventory(slot_id, charges, true); - } else { - int16 amount_left = (quantity - removed_count); - if (amount_left > 0 && stack_size >= amount_left) { - removed_count += amount_left; - DeleteItemInInventory(slot_id, amount_left, true); - } + for (const int16& slot_id : GetInventorySlots()) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + uint32 charges = item->IsStackable() ? item->GetCharges() : 0; + uint32 stack_size = std::max(charges, static_cast(1)); + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, charges, true); + } else { + uint32 amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); } } } @@ -12820,37 +12787,28 @@ uint16 Client::GetSkill(EQ::skills::SkillType skill_id) const void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity) { EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - int16 removed_count = 0; - const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - if (removed_count == quantity) { - break; - } - item = GetInv().GetItem(slot_id); - if (item && item->GetSerialNumber() == serial_number) { - int16 charges = item->IsStackable() ? item->GetCharges() : 0; - int16 stack_size = std::max(charges, static_cast(1)); - if ((removed_count + stack_size) <= quantity) { - removed_count += stack_size; - DeleteItemInInventory(slot_id, charges, true); - } else { - int16 amount_left = (quantity - removed_count); - if (amount_left > 0 && stack_size >= amount_left) { - removed_count += amount_left; - DeleteItemInInventory(slot_id, amount_left, true); - } + uint32 removed_count = 0; + + const auto& slot_ids = GetInventorySlots(); + + for (const int16& slot_id : slot_ids) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetSerialNumber() == serial_number) { + uint32 charges = item->IsStackable() ? item->GetCharges() : 0; + uint32 stack_size = std::max(charges, static_cast(1)); + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, charges, true); + } else { + uint32 amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); } } } @@ -13075,3 +13033,28 @@ void Client::ClientToNpcAggroProcess() LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); } } + +const std::vector& Client::GetInventorySlots() +{ + static const std::vector> slots = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END }, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + + static std::vector slot_ids; + + if (slot_ids.empty()) { + for (const auto& [begin, end] : slots) { + for (int16 slot_id = begin; slot_id <= end; ++slot_id) { + slot_ids.emplace_back(slot_id); + } + } + } + + return slot_ids; +} diff --git a/zone/client.h b/zone/client.h index 5acc388b3..03202af2f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -467,6 +467,7 @@ public: inline ExtendedProfile_Struct& GetEPP() { return m_epp; } inline EQ::InventoryProfile& GetInv() { return m_inv; } inline const EQ::InventoryProfile& GetInv() const { return m_inv; } + const std::vector& GetInventorySlots(); inline PetInfo* GetPetInfo(int pet_info_type) { return pet_info_type == PetInfoType::Suspended ? &m_suspendedminion : &m_petinfo; } inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; } inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; } @@ -1093,7 +1094,7 @@ public: bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false); void SendCursorBuffer(); void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); - int CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); void ResetItemCooldown(uint32 item_id); void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1); uint32 GetItemCooldown(uint32 item_id); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e54dd2e63..4a0d8de09 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1871,9 +1871,10 @@ bool Corpse::HasItem(uint32 item_id) return false; } -uint16 Corpse::CountItem(uint32 item_id) +uint32 Corpse::CountItem(uint32 item_id) { - uint16 item_count = 0; + uint32 item_count = 0; + if (!database.GetItem(item_id)) { return item_count; } @@ -1893,6 +1894,7 @@ uint16 Corpse::CountItem(uint32 item_id) item_count += i->charges > 0 ? i->charges : 1; } } + return item_count; } diff --git a/zone/corpse.h b/zone/corpse.h index 28fc8c91a..c1925c325 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -196,7 +196,7 @@ public: /* Corpse: Loot */ void QueryLoot(Client *to); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); std::vector GetLootList(); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index d18796a4e..c451db90b 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1446,7 +1446,7 @@ int Perl__collectitems(uint32_t item_id, bool remove_item) return quest_manager.collectitems(item_id, remove_item); } -int Perl__countitem(uint32_t item_id) +uint32 Perl__countitem(uint32_t item_id) { return quest_manager.countitem(item_id); } diff --git a/zone/gm_commands/emptyinventory.cpp b/zone/gm_commands/emptyinventory.cpp index 582512289..e37167db6 100644 --- a/zone/gm_commands/emptyinventory.cpp +++ b/zone/gm_commands/emptyinventory.cpp @@ -2,31 +2,21 @@ void command_emptyinventory(Client *c, const Seperator *sep) { - auto target = c; + Client* t = c; if (c->GetGM() && c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); + t = c->GetTarget()->CastToClient(); } EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - int removed_count = 0; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = target->GetInv().GetItem(slot_id); - if (item) { - int stack_size = std::max(static_cast(item->GetCharges()), 1); - removed_count += stack_size; - target->DeleteItemInInventory(slot_id, 0, true); - } + uint32 removed_count = 0; + + for (const int16& slot_id : t->GetInventorySlots()) { + item = t->GetInv().GetItem(slot_id); + + if (item) { + uint32 stack_size = std::max(static_cast(item->GetCharges()), static_cast(1)); + removed_count += stack_size; + t->DeleteItemInInventory(slot_id, 0, true); } } @@ -35,8 +25,8 @@ void command_emptyinventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "{} {} no items to delete.", - c->GetTargetDescription(target, TargetDescriptionType::UCYou), - c == target ? "have" : "has" + c->GetTargetDescription(t, TargetDescriptionType::UCYou), + c == t ? "have" : "has" ).c_str() ); return; @@ -46,9 +36,9 @@ void command_emptyinventory(Client *c, const Seperator *sep) Chat::White, fmt::format( "Inventory cleared for {}, {} item{} deleted.", - c->GetTargetDescription(target), + c->GetTargetDescription(t), removed_count, removed_count != 1 ? "s" : "" ).c_str() ); -} \ No newline at end of file +} diff --git a/zone/loot.cpp b/zone/loot.cpp index 6fcf22cca..a330914e2 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 2cf0e7db3..5b37a1344 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 b07d14abc..6825d2d29 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -127,8 +127,8 @@ public: 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 f8d5ca64d..13982fcc0 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2287,7 +2287,7 @@ void Lua_Client::SendToInstance(std::string instance_type, std::string zone_shor self->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); } -int Lua_Client::CountItem(uint32 item_id) { +uint32 Lua_Client::CountItem(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItem(item_id); } @@ -2480,7 +2480,7 @@ void Lua_Client::AddItem(luabind::object item_table) { ); } -int Lua_Client::CountAugmentEquippedByID(uint32 item_id) { +uint32 Lua_Client::CountAugmentEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -2490,7 +2490,7 @@ bool Lua_Client::HasAugmentEquippedByID(uint32 item_id) { return self->GetInv().HasAugmentEquippedByID(item_id); } -int Lua_Client::CountItemEquippedByID(uint32 item_id) { +uint32 Lua_Client::CountItemEquippedByID(uint32 item_id) { Lua_Safe_Call_Int(); return self->GetInv().CountItemEquippedByID(item_id); } @@ -3436,6 +3436,21 @@ void Lua_Client::AreaTaunt(float range, int bonus_hate) entity_list.AETaunt(self, range, bonus_hate); } +luabind::object Lua_Client::GetInventorySlots(lua_State* L) { + auto lua_table = luabind::newtable(L); + + if (d_) { + auto self = reinterpret_cast(d_); + int index = 1; + for (const int16& slot_id : self->GetInventorySlots()) { + lua_table[index] = slot_id; + index++; + } + } + + return lua_table; +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3514,9 +3529,9 @@ luabind::scope lua_register_client() { .def("ClearXTargets", (void(Lua_Client::*)(void))&Lua_Client::ClearXTargets) .def("ClearZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::ClearZoneFlag) .def("Connected", (bool(Lua_Client::*)(void))&Lua_Client::Connected) - .def("CountAugmentEquippedByID", (int(Lua_Client::*)(uint32))&Lua_Client::CountAugmentEquippedByID) - .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) - .def("CountItemEquippedByID", (int(Lua_Client::*)(uint32))&Lua_Client::CountItemEquippedByID) + .def("CountAugmentEquippedByID", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountAugmentEquippedByID) + .def("CountItem", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountItem) + .def("CountItemEquippedByID", (uint32(Lua_Client::*)(uint32))&Lua_Client::CountItemEquippedByID) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(luabind::object))&Lua_Client::CreateExpedition) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32))&Lua_Client::CreateExpedition) .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition) @@ -3650,6 +3665,7 @@ luabind::scope lua_register_client() { .def("GetInstrumentMod", (int(Lua_Client::*)(int))&Lua_Client::GetInstrumentMod) .def("GetIntoxication", (int(Lua_Client::*)(void))&Lua_Client::GetIntoxication) .def("GetInventory", (Lua_Inventory(Lua_Client::*)(void))&Lua_Client::GetInventory) + .def("GetInventorySlots", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetInventorySlots) .def("GetInvulnerableEnvironmentDamage", (bool(Lua_Client::*)(void))&Lua_Client::GetInvulnerableEnvironmentDamage) .def("GetItemIDAt", (int(Lua_Client::*)(int))&Lua_Client::GetItemIDAt) .def("GetItemCooldown", (uint32(Lua_Client::*)(uint32))&Lua_Client::GetItemCooldown) diff --git a/zone/lua_client.h b/zone/lua_client.h index faff8b157..fb012b3bc 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -441,14 +441,14 @@ public: void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two, uint32 sound_controls); - int CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); void RemoveItem(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity); void SetGMStatus(int new_status); int16 GetGMStatus(); void AddItem(luabind::object item_table); - int CountAugmentEquippedByID(uint32 item_id); - int CountItemEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); bool HasAugmentEquippedByID(uint32 item_id); bool HasItemEquippedByID(uint32 item_id); int GetHealAmount(); @@ -509,6 +509,7 @@ public: void AreaTaunt(); void AreaTaunt(float range); void AreaTaunt(float range, int bonus_hate); + luabind::object GetInventorySlots(lua_State* L); void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 321e4e543..2db6bad07 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 9ad63e979..58bfb7d05 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -62,7 +62,7 @@ public: uint32 GetPlatinum(); void AddLooter(Lua_Mob who); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); Lua_Corpse_Loot_List GetLootList(lua_State* L); diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index 48987de68..3d464a915 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 472447e62..a9c5c8d5e 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -43,8 +43,8 @@ public: bool DeleteItem(int slot_id); bool DeleteItem(int slot_id, int quantity); bool CheckNoDrop(int slot_id); - int CountAugmentEquippedByID(uint32 item_id); - int CountItemEquippedByID(uint32 item_id); + uint32 CountAugmentEquippedByID(uint32 item_id); + uint32 CountItemEquippedByID(uint32 item_id); Lua_ItemInst PopItem(int slot_id); bool HasAugmentEquippedByID(uint32 item_id); bool HasItemEquippedByID(uint32 item_id); diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index b17804edc..aa0973292 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 ec916593a..180bddad0 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -146,7 +146,7 @@ public: void ChangeLastName(std::string last_name); void ClearLastName(); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetLootItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); float GetHealScale(); diff --git a/zone/npc.h b/zone/npc.h index a20d975b1..9b17afaba 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -222,7 +222,7 @@ public: void RemoveLootCash(); void QueryLoot(Client *to, bool is_pet_query = false); bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); + uint32 CountItem(uint32 item_id); uint32 GetLootItemIDBySlot(uint16 loot_slot); uint16 GetFirstLootSlotByItemID(uint32 item_id); std::vector GetLootList(); diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 86f104565..bd0747e94 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 8bb39afda..c41b39ba5 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2209,7 +2209,7 @@ void Perl_Client_SendToInstance(Client* self, std::string instance_type, std::st self->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); } -int Perl_Client_CountItem(Client* self, uint32 item_id) +uint32 Perl_Client_CountItem(Client* self, uint32 item_id) { return self->CountItem(item_id); } @@ -2388,7 +2388,7 @@ void Perl_Client_AddItem(Client* self, perl::reference table_ref) augment_four, augment_five, augment_six, attuned, slot_id); } -int Perl_Client_CountAugmentEquippedByID(Client* self, uint32 item_id) +uint32 Perl_Client_CountAugmentEquippedByID(Client* self, uint32 item_id) { return self->GetInv().CountAugmentEquippedByID(item_id); } @@ -2398,7 +2398,7 @@ bool Perl_Client_HasAugmentEquippedByID(Client* self, uint32 item_id) return self->GetInv().HasAugmentEquippedByID(item_id); } -int Perl_Client_CountItemEquippedByID(Client* self, uint32 item_id) +uint32 Perl_Client_CountItemEquippedByID(Client* self, uint32 item_id) { return self->GetInv().CountItemEquippedByID(item_id); } @@ -3212,6 +3212,18 @@ Merc* Perl_Client_GetMerc(Client* self) return self->GetMerc(); } +perl::array Perl_Client_GetInventorySlots(Client* self) +{ + perl::array result; + const auto& v = self->GetInventorySlots(); + + for (int i = 0; i < v.size(); ++i) { + result.push_back(v[i]); + } + + return result; +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3426,6 +3438,7 @@ void perl_register_client() package.add("GetInstanceID", &Perl_Client_GetInstanceID); package.add("GetInstrumentMod", &Perl_Client_GetInstrumentMod); package.add("GetInventory", &Perl_Client_GetInventory); + package.add("GetInventorySlots", &Perl_Client_GetInventorySlots); package.add("GetInvulnerableEnvironmentDamage", &Perl_Client_GetInvulnerableEnvironmentDamage); package.add("GetItemAt", &Perl_Client_GetItemAt); package.add("GetItemCooldown", &Perl_Client_GetItemCooldown); diff --git a/zone/perl_inventory.cpp b/zone/perl_inventory.cpp index ba083e936..a002a24a7 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 b47b8c0e8..a44bd146e 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 60de30201..c034e0312 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -161,7 +161,7 @@ bool Perl_Corpse_HasItem(Corpse* self, uint32_t item_id) // @categories Script U return self->HasItem(item_id); } -int Perl_Corpse_CountItem(Corpse* self, uint32_t item_id) // @categories Script Utility +uint32 Perl_Corpse_CountItem(Corpse* self, uint32_t item_id) // @categories Script Utility { return self->CountItem(item_id); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ad29a76a9..cd44abf8b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3304,7 +3304,7 @@ int QuestManager::collectitems(uint32 item_id, bool remove) return quantity; } -int QuestManager::countitem(uint32 item_id) { +uint32 QuestManager::countitem(uint32 item_id) { QuestManagerCurrentQuestVars(); if (!initiator) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 1f872aaf5..c73596f4e 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -249,7 +249,7 @@ public: int getlevel(uint8 type); int collectitems(uint32 item_id, bool remove); int collectitems_processSlot(int16 slot_id, uint32 item_id, bool remove); - int countitem(uint32 item_id); + uint32 countitem(uint32 item_id); void removeitem(uint32 item_id, uint32 quantity = 1); std::string getitemcomment(uint32 item_id); std::string getitemlore(uint32 item_id); From d95b64e0b8c0050607ee8d84e0e02e44dca8bd45 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 6 Jan 2025 01:18:25 -0500 Subject: [PATCH 12/16] [Cleanup] Fix GM Flag Spell Restriction Bypasses (#4571) * [Cleanup] Fix GM Flag Spell Restriction Bypasses * Update spells.cpp --- zone/spells.cpp | 185 ++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 101 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 15d1e5574..4ca95255c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -645,6 +645,11 @@ bool Mob::DoCastingChecksOnCaster(int32 spell_id, CastingSlot slot) { return true; } +struct SpellCheck { + std::function condition; // The condition to check + std::function action; // The action if the condition fails +}; + bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) { /* @@ -652,114 +657,92 @@ bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) - levitate zone restriction (client blocks) [cancel before begin cast message] - can not cast outdoor [cancels after spell finishes channeling] - If the spell is a casted spell, check on CastSpell and ignore on SpellFinished. - If the spell is a initiated from SpellFinished, then check at start of SpellFinished. + If the spell is a cast spell, check on CastSpell and ignore on SpellFinished. + If the spell is initiated from SpellFinished, then check at start of SpellFinished. */ - bool bypass_casting_restrictions = false; + bool bypass_casting_restrictions = !IsClient(); + glm::vec3 position = glm::vec3(GetPosition()); - if (!IsClient()) { - bypass_casting_restrictions = true; - } - - if (IsClient() && CastToClient()->GetGM()) { - bypass_casting_restrictions = true; - Message( - Chat::White, - fmt::format( - "Your GM flag allows you to bypass zone casting restrictions and cast {} in this zone.", - Saylink::Silent( - fmt::format( - "#castspell {}", - spell_id - ), - GetSpellName(spell_id) - ) - ).c_str() - ); - } - - /* - Zone ares that prevent blocked spells from being cast. - If on cast iniated then check any mob casting, if on spellfinished only check if is from client. - */ - if ((check_on_casting && !bypass_casting_restrictions) || (!check_on_casting && IsClient())) { - if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - if (IsClient()) { - if (!CastToClient()->GetGM()) { - const char *msg = zone->GetSpellBlockedMessage(spell_id, glm::vec3(GetPosition())); - if (msg) { - Message(Chat::Red, msg); - return false; - } - else { - Message(Chat::Red, "You can't cast this spell here."); - return false; - } - LogSpells("Spell casting canceled [{}] : can not cast in this zone location blocked spell.", spell_id); - } - else { - Message( - Chat::White, - fmt::format( - "Your GM flag allows you to bypass zone blocked spells and cast {} in this zone.", - Saylink::Silent( - fmt::format( - "#castspell {}", - spell_id - ), - GetSpellName(spell_id) - ) - ).c_str() - ); - LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); - } - } - return false; + auto gm_bypass_message = [&](const std::string& restriction) { + if (CastToClient()->GetGM()) { + Message( + Chat::White, + fmt::format( + "Your GM flag allows you to bypass {} and cast {}.", + restriction, + Saylink::Silent( + fmt::format("#castspell {}", spell_id), + GetSpellName(spell_id) + ) + ).c_str() + ); + return true; } - } - /* - Zones where you can not use levitate spells. - */ - if (!bypass_casting_restrictions && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { //check on spellfinished. - Message(Chat::Red, "You have entered an area where levitation effects do not function."); - LogSpells("Spell casting canceled [{}] : can not cast levitation in this zone.", spell_id); return false; - } - /* - Zones where you can not use detrimental spells. - */ - if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - Message(Chat::Red, "You cannot cast detrimental spells here."); - return false; - } - /* - Zones where you can not cast a spell that is for daytime or nighttime only - */ - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - MessageString(Chat::Red, CAST_DAYTIME); - return false; - } + }; - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - MessageString(Chat::Red, CAST_NIGHTTIME); - return false; - } - - if (check_on_casting) { - /* - Zones where you can not cast out door only spells. This is only checked when casting is completed. - */ - if (!bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - if (IsClient()) { - if (!CastToClient()->GetGM()) { - MessageString(Chat::Red, CAST_OUTDOORS); - LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); - return false; - } else { - Message(Chat::White, "Your GM flag allows you to cast outdoor spells when indoors."); - } + std::vector spell_checks = { + // Blocked spells + { + [&]() { return !bypass_casting_restrictions && zone->IsSpellBlocked(spell_id, position); }, + [&]() { + if (gm_bypass_message("zone blocked spells")) { return true; } + const char* msg = zone->GetSpellBlockedMessage(spell_id, position); + Message(Chat::Red, msg ? msg : "You can't cast this spell here."); + return false; } + }, + // Levitation restriction + { + [&]() { return !bypass_casting_restrictions && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate); }, + [&]() { + if (gm_bypass_message("zone levitation restrictions")) { return true; } + Message(Chat::Red, "You have entered an area where levitation effects do not function."); + return false; + } + }, + // Detrimental spells restriction + { + [&]() { return !bypass_casting_restrictions && IsDetrimentalSpell(spell_id) && !zone->CanDoCombat(); }, + [&]() { + if (gm_bypass_message("no combat zone restrictions")) { return true; } + Message(Chat::Red, "You cannot cast detrimental spells here."); + return false; + } + }, + // Daytime-only spells + { + [&]() { return !bypass_casting_restrictions && spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime(); }, + [&]() { + if (gm_bypass_message("spell daytime restrictions")) { return true; } + MessageString(Chat::Red, CAST_DAYTIME); + return false; + } + }, + // Nighttime-only spells + { + [&]() { return !bypass_casting_restrictions && spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime(); }, + [&]() { + if (gm_bypass_message("spell nighttime restrictions")) return true; + MessageString(Chat::Red, CAST_NIGHTTIME); + return false; + } + }, + // Outdoor-only spells + { + [&]() { return check_on_casting && !bypass_casting_restrictions && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor(); }, + [&]() { + if (gm_bypass_message("zone outdoor restrictions")) return true; + MessageString(Chat::Red, CAST_OUTDOORS); + return false; + } + } + }; + + for (const auto& check : spell_checks) { + if (check.condition() && !check.action()) { + return false; } } From 490cffb5ea9aaf675c931dbd06422df6b1c6a959 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 6 Jan 2025 00:27:11 -0600 Subject: [PATCH 13/16] [Release] 22.61.0 (#4587) --- CHANGELOG.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ common/version.h | 2 +- package.json | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfa1f1bc..a7ea71c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,68 @@ +## [22.61.0] 1/6/2025 + +### Bots + +* Fix AA ranks to account for level ([#4567](https://github.com/EQEmu/Server/pull/4567)) @nytmyr 2024-12-07 + +### Code + +* Convert Event Parses to Single Line ([#4569](https://github.com/EQEmu/Server/pull/4569)) @Kinglykrab 2024-12-12 +* Fix GM Flag Spell Restriction Bypasses ([#4571](https://github.com/EQEmu/Server/pull/4571)) @Kinglykrab 2025-01-06 +* Remove Unused Group Methods ([#4559](https://github.com/EQEmu/Server/pull/4559)) @Kinglykrab 2024-12-12 + +### Commands + +* Add #find bot Subcommand ([#4563](https://github.com/EQEmu/Server/pull/4563)) @Kinglykrab 2024-12-12 +* Add #find ldon_theme Subcommand ([#4564](https://github.com/EQEmu/Server/pull/4564)) @Kinglykrab 2024-12-12 +* Fix #copycharacter ([#4582](https://github.com/EQEmu/Server/pull/4582)) @Akkadius 2025-01-06 + +### Databuckets + +* Improved Reliability and Performance of Databuckets ([#4562](https://github.com/EQEmu/Server/pull/4562)) @Akkadius 2024-12-12 + +### Feature + +* Enable bazaar window 'Find Trader' functionality ([#4560](https://github.com/EQEmu/Server/pull/4560)) @neckkola 2024-12-12 + +### Filesystem + +* Path Manager Improvements ([#4557](https://github.com/EQEmu/Server/pull/4557)) @Akkadius 2025-01-06 + +### Fixes + +* Allow Items in ROF2 to Stack to 32,767 ([#4556](https://github.com/EQEmu/Server/pull/4556)) @Kinglykrab 2024-12-12 +* Fix EVENT_COMBAT on NPC Death ([#4558](https://github.com/EQEmu/Server/pull/4558)) @Kinglykrab 2024-11-28 +* Guild Membership Update Fix ([#4581](https://github.com/EQEmu/Server/pull/4581)) @neckkola 2025-01-06 +* Guild creation to propagate across zones ([#4575](https://github.com/EQEmu/Server/pull/4575)) @neckkola 2025-01-06 +* Repair a EQEMUConfig Memory Leak ([#4584](https://github.com/EQEmu/Server/pull/4584)) @neckkola 2025-01-06 +* Repair a LoadNPCEmote MemoryLeak ([#4586](https://github.com/EQEmu/Server/pull/4586)) @neckkola 2025-01-06 +* Repair a memory leak in GuildsList ([#4585](https://github.com/EQEmu/Server/pull/4585)) @neckkola 2025-01-06 +* Resolve a client crash when logging in or zoning ([#4572](https://github.com/EQEmu/Server/pull/4572)) @neckkola 2024-12-14 + +### Groups + +* Fix AmIMainAssist incorrectly checking for MainTankName ([#4565](https://github.com/EQEmu/Server/pull/4565)) @nytmyr 2024-12-04 + +### Inventory + +* Add GetInventorySlots() Method ([#4566](https://github.com/EQEmu/Server/pull/4566)) @Kinglykrab 2025-01-06 + +### Logs + +* Improve Crash log defaults ([#4579](https://github.com/EQEmu/Server/pull/4579)) @Akkadius 2025-01-06 + +### Maps + +* Fix broken Map MMFS implementation ([#4576](https://github.com/EQEmu/Server/pull/4576)) @Akkadius 2025-01-06 + +### Network + +* Prune / disconnect TCP connections gracefully ([#4574](https://github.com/EQEmu/Server/pull/4574)) @Akkadius 2025-01-06 + +### Rules + +* Add rules for requiring custom files from client ([#4561](https://github.com/EQEmu/Server/pull/4561)) @knervous 2024-12-12 + ## [22.60.0] 11/25/2024 ### Bazaar diff --git a/common/version.h b/common/version.h index 232d3afc5..919b8f64c 100644 --- a/common/version.h +++ b/common/version.h @@ -25,7 +25,7 @@ // Build variables // these get injected during the build pipeline -#define CURRENT_VERSION "22.60.0-dev" // always append -dev to the current version for custom-builds +#define CURRENT_VERSION "22.61.0-dev" // always append -dev to the current version for custom-builds #define LOGIN_VERSION "0.8.0" #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ diff --git a/package.json b/package.json index 26dfe3d29..68a2da55e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eqemu-server", - "version": "22.60.0", + "version": "22.61.0", "repository": { "type": "git", "url": "https://github.com/EQEmu/Server.git" From d89f9bdcc7af7da1579b428174a1171142658d03 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Tue, 7 Jan 2025 01:01:49 -0400 Subject: [PATCH 14/16] [Fix] Repair an incorrect safe_delete call memory leak. (#4588) --- common/patches/rof2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 68aaa7cfa..ef27f06c2 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -487,7 +487,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_stat); //itemstat } - safe_delete(in->pBuffer); + safe_delete_array(in->pBuffer); in->size = p_size; in->pBuffer = (uchar *) buffer.get(); dest->QueuePacket(in); @@ -508,7 +508,7 @@ namespace RoF2 eq->num_of_traders = emu->traders; eq->num_of_items = emu->items; - safe_delete(in->pBuffer); + safe_delete_array(in->pBuffer); in->SetOpcode(OP_TraderShop); in->size = sizeof(structs::BazaarWelcome_Struct); in->pBuffer = (uchar *) buffer.get(); From 03b30d5c7af0aecfab5459d40c6777b6b62fabf3 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 7 Jan 2025 13:46:32 -0600 Subject: [PATCH 15/16] [Database] Change npc_types walkspeed to be of type float (#4589) * [Database] Change npc_types walkspeed to be of type float * Update database_update_manifest.cpp --- common/database/database_update_manifest.cpp | 11 +++++++++++ common/version.h | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 61186a205..fde8820f0 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5782,6 +5782,17 @@ CREATE INDEX idx_bot_expires ON data_buckets (bot_id, expires); ALTER TABLE `trader` ADD COLUMN `char_zone_instance_id` INT NULL DEFAULT '0' AFTER `char_zone_id`; )" + }, + ManifestEntry{ + .version = 9287, + .description = "2024_11_26_bazaar_find_trader.sql", + .check = "SHOW COLUMNS FROM `npc_types` LIKE 'walkspeed'", + .condition = "missing", + .match = "float", + .sql = R"( +ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0; +)", + .content_schema_update = true } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/version.h b/common/version.h index 919b8f64c..192a20614 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9286 +#define CURRENT_BINARY_DATABASE_VERSION 9287 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif From 533dc997fd8fc7e71b848add173be61d9d251911 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:50:34 -0400 Subject: [PATCH 16/16] [Fix] Repair a memory leak in #summonitem (#4591) --- zone/gm_commands/summonitem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/gm_commands/summonitem.cpp b/zone/gm_commands/summonitem.cpp index ddee8ab07..b72292a97 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); }