From fba1c068471036364871c5ab5097637d3f37b0be Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 01:03:48 -0600 Subject: [PATCH 1/7] changed pointer into unique pointer and addressed review concerns --- common/patches/IMessage.h | 10 +++--- common/patches/client_version.cpp | 18 +++++------ common/patches/client_version.h | 2 +- common/patches/titanium.cpp | 19 +++++------ common/patches/titanium.h | 11 ++++--- common/patches/tob.cpp | 15 +++++---- common/patches/tob.h | 9 ++++-- zone/client_version.h | 53 ++++++++++++++++--------------- 8 files changed, 75 insertions(+), 62 deletions(-) diff --git a/common/patches/IMessage.h b/common/patches/IMessage.h index 57ab52286..80bd15501 100644 --- a/common/patches/IMessage.h +++ b/common/patches/IMessage.h @@ -37,12 +37,14 @@ public: virtual ~IMessage() = default; // these two are the basic string message packets - [[nodiscard]] virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0; - [[nodiscard]] virtual EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const = 0; + virtual std::unique_ptr Simple(uint32_t color, uint32_t id) const = 0; + virtual std::unique_ptr Formatted(uint32_t color, uint32_t id, + const std::array& args) const = 0; // These aren't technically messages, but they use the same format and are similar enough to include here - virtual EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const = 0; - virtual EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, + virtual std::unique_ptr InterruptSpell(uint32_t message, uint32_t spawn_id, + const char* spell_link) const = 0; + virtual std::unique_ptr InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) const = 0; }; diff --git a/common/patches/client_version.cpp b/common/patches/client_version.cpp index 20a506899..a4faebf59 100644 --- a/common/patches/client_version.cpp +++ b/common/patches/client_version.cpp @@ -34,31 +34,31 @@ struct ClientComponents { switch (version) { case Version::TOB: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; case Version::RoF2: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; case Version::RoF: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; case Version::UF: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; case Version::SoD: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; case Version::SoF: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; default: - messageComponent = std::make_shared(); + messageComponent = std::make_unique(); break; } } const Version version; - std::shared_ptr messageComponent; + std::unique_ptr messageComponent; }; static const ClientComponents& GetComponents(Version version) @@ -78,7 +78,7 @@ static const ClientComponents& GetComponents(Version version) return patches.at(version); } -const std::shared_ptr& GetMessageComponent(Version version) +const std::unique_ptr& GetMessageComponent(Version version) { return GetComponents(version).messageComponent; } diff --git a/common/patches/client_version.h b/common/patches/client_version.h index 5c1ede856..054cd2a7d 100644 --- a/common/patches/client_version.h +++ b/common/patches/client_version.h @@ -10,4 +10,4 @@ namespace Message { class IMessage; } // store all static functions for the different patches here -const std::shared_ptr& GetMessageComponent(EQ::versions::ClientVersion version); +const std::unique_ptr& GetMessageComponent(EQ::versions::ClientVersion version); diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 49ead949e..731cc1488 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -3922,12 +3922,11 @@ namespace Titanium } /*Titanium*/ namespace Message { - -EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const +std::unique_ptr Titanium::Simple(uint32_t color, uint32_t id) const { uint32_t string_id = ResolveID(id); if (string_id > 0) { - auto outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); + auto outapp = std::make_unique(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); auto* sms = reinterpret_cast(outapp->pBuffer); sms->string_id = string_id; sms->color = color; @@ -3939,7 +3938,7 @@ EQApplicationPacket* Titanium::Simple(uint32_t color, uint32_t id) const return nullptr; } -EQApplicationPacket* Titanium::Formatted( +std::unique_ptr Titanium::Formatted( uint32_t color, uint32_t id, const std::array& args) const { uint32_t string_id = ResolveID(id); @@ -3961,15 +3960,16 @@ EQApplicationPacket* Titanium::Formatted( buf.WriteUInt8(0); - return new EQApplicationPacket(OP_FormattedMessage, std::move(buf)); + return std::make_unique(OP_FormattedMessage, std::move(buf)); } return nullptr; } -EQApplicationPacket* Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const +std::unique_ptr Titanium::InterruptSpell(uint32_t message, uint32_t spawn_id, + const char* spell_link) const { - auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct)); + auto outapp = std::make_unique(OP_InterruptCast, sizeof(InterruptCast_Struct)); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); ic->spawnid = spawn_id; @@ -3978,10 +3978,11 @@ EQApplicationPacket* Titanium::InterruptSpell(uint32_t message, uint32_t spawn_i return outapp; } -EQApplicationPacket* Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, +std::unique_ptr Titanium::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, + const char* name, const char* spell_link) const { - auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1); + auto outapp = std::make_unique(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); ic->spawnid = spawn_id; diff --git a/common/patches/titanium.h b/common/patches/titanium.h index 1d96475a2..e31c4ef63 100644 --- a/common/patches/titanium.h +++ b/common/patches/titanium.h @@ -59,11 +59,14 @@ public: Titanium() = default; ~Titanium() override = default; - [[nodiscard]] EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override; - [[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const override; + std::unique_ptr Simple(uint32_t color, uint32_t id) const override; + std::unique_ptr Formatted(uint32_t color, uint32_t id, + const std::array& args) const override; - EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override; - EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, + std::unique_ptr InterruptSpell(uint32_t message, uint32_t spawn_id, + const char* spell_link) const override; + std::unique_ptr InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, + const char* name, const char* spell_link) const override; protected: diff --git a/common/patches/tob.cpp b/common/patches/tob.cpp index 2b781032c..41b014448 100644 --- a/common/patches/tob.cpp +++ b/common/patches/tob.cpp @@ -5634,7 +5634,8 @@ void TOB::ResolveArguments(uint32_t id, std::array& args) const } } -EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const std::array& args) const +std::unique_ptr TOB::Formatted(uint32_t color, uint32_t id, + const std::array& args) const { uint32_t string_id = ResolveID(id); if (string_id > 0) { @@ -5660,15 +5661,16 @@ EQApplicationPacket* TOB::Formatted(uint32_t color, uint32_t id, const std::arra buffer.WriteUInt32(0); } - return new EQApplicationPacket(OP_FormattedMessage, std::move(buffer)); + return std::make_unique(OP_FormattedMessage, std::move(buffer)); } return nullptr; } -EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const +std::unique_ptr TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, + const char* spell_link) const { - auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1); + auto outapp = std::make_unique(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); ic->spawnid = spawn_id; @@ -5678,10 +5680,11 @@ EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, co return outapp; } -EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, +std::unique_ptr TOB::InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, + const char* name, const char* spell_link) const { - auto outapp = new EQApplicationPacket(OP_InterruptCast, + auto outapp = std::make_unique(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + strlen(spell_link) + 2); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); diff --git a/common/patches/tob.h b/common/patches/tob.h index c3cc9e2c9..39eb708cf 100644 --- a/common/patches/tob.h +++ b/common/patches/tob.h @@ -42,10 +42,13 @@ public: TOB() {} ~TOB() override {} - [[nodiscard]] EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const std::array& args) const override; + std::unique_ptr Formatted(uint32_t color, uint32_t id, + const std::array& args) const override; - EQApplicationPacket* InterruptSpell(uint32_t message, uint32_t spawn_id, const char* spell_link) const override; - EQApplicationPacket* InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, const char* name, const char* spell_link) const override; + std::unique_ptr InterruptSpell(uint32_t message, uint32_t spawn_id, + const char* spell_link) const override; + std::unique_ptr InterruptSpellOther(Mob* sender, uint32_t message, uint32_t spawn_id, + const char* name, const char* spell_link) const override; protected: [[nodiscard]] uint32_t ResolveID(uint32_t id) const override; diff --git a/zone/client_version.h b/zone/client_version.h index 595f9bad9..66b6d77f7 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -21,11 +21,9 @@ template static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) { static_assert(std::is_member_function_pointer_v); - EQApplicationPacket* app = std::invoke(fun, obj, std::forward(args)...); - if (app != nullptr) { - c->QueuePacket(app); - delete app; - } + std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); + if (app) + c->QueuePacket(app.get()); } // packet generator queue functions @@ -35,23 +33,24 @@ static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = std::function component_getter, Args&&... args) { static_assert(std::is_member_function_pointer_v && "Function is required to be a member function"); - std::unordered_map build_packets; + std::vector>> build_packets; std::unordered_map client_list = entity_list.GetClientList(); for (auto [_, ent] : client_list) { if (!ignore_sender || ent != sender) { - auto [packet, _] = build_packets.try_emplace( - ent->ClientVersion(), - std::invoke(fun, component_getter(ent), std::forward(args)...)); + auto packet_it = std::find_if(build_packets.begin(), build_packets.end(), + [version = ent->GetClientVersion()](const auto& build_packet) { + return build_packet.first == version; + }); - if (packet->second != nullptr) - ent->QueuePacket(packet->second, ackreq, Client::CLIENT_CONNECTED); + if (packet_it == build_packets.end()) + packet_it = build_packets.emplace(build_packets.end(), ent->ClientVersion(), + std::invoke(fun, component_getter(ent), std::forward(args)...)); + + if (packet_it->second != nullptr) + ent->QueuePacket(packet_it->second.get(), ackreq, Client::CLIENT_CONNECTED); } } - - for (auto [_, packet] : build_packets) - if (packet != nullptr) - delete packet; }; } @@ -70,7 +69,7 @@ static auto QueueCloseClients( QueueClients(sender, ignore_sender, is_ack_required)(fun, component_getter, std::forward(args)...); } else { float distance_squared = distance * distance; - std::unordered_map build_packets; + std::vector>> build_packets; for (auto& [_, mob] : sender->GetCloseMobList(distance)) { if (mob && mob->IsClient()) { @@ -79,20 +78,22 @@ static auto QueueCloseClients( && client != skipped_mob && DistanceSquared(client->GetPosition(), sender->GetPosition()) < distance_squared && client->Connected() - && client->ShouldGetPacket(sender, filter)) { - auto [packet, _] = build_packets.try_emplace( - client->ClientVersion(), - std::invoke(fun, component_getter(client), std::forward(args)...)); + && client->ShouldGetPacket(sender, filter)) + { + auto packet_it = std::find_if(build_packets.begin(), build_packets.end(), + [version = client->GetClientVersion()](const auto& build_packet) { + return build_packet.first == version; + }); - if (packet->second != nullptr) - client->QueuePacket(packet->second, is_ack_required, Client::CLIENT_CONNECTED); + if (packet_it == build_packets.end()) + packet_it = build_packets.emplace(build_packets.end(), client->ClientVersion(), + std::invoke(fun, component_getter(client), std::forward(args)...)); + + if (packet_it->second != nullptr) + client->QueuePacket(packet_it->second.get(), is_ack_required, Client::CLIENT_CONNECTED); } } } - - for (auto [_, packet] : build_packets) - if (packet != nullptr) - delete packet;; } }; } From f47ce5b620babe4b1079ebb1332b516d3b409773 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 15:01:45 -0600 Subject: [PATCH 2/7] cleaned up and used require syntax for constraints --- zone/client_version.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/client_version.h b/zone/client_version.h index 66b6d77f7..5e4e9ce10 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -18,9 +18,9 @@ namespace ClientPatch { using ClientList = std::unordered_map; template +requires std::is_member_function_pointer_v static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) { - static_assert(std::is_member_function_pointer_v); std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); if (app) c->QueuePacket(app.get()); @@ -30,9 +30,9 @@ static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true) { return [=](Fun fun, - std::function component_getter, Args&&... args) { - static_assert(std::is_member_function_pointer_v && "Function is required to be a member function"); - + std::function component_getter, Args&&... args) + requires std::is_member_function_pointer_v + { std::vector>> build_packets; std::unordered_map client_list = entity_list.GetClientList(); @@ -62,9 +62,9 @@ static auto QueueCloseClients( if (distance <= 0) distance = static_cast(zone->GetClientUpdateRange()); return [=](Fun fun, - std::function component_getter, Args&&... args) { - static_assert(std::is_member_function_pointer_v, "Function is required to be a member function"); - + std::function component_getter, Args&&... args) + requires std::is_member_function_pointer_v + { if (sender == nullptr) { QueueClients(sender, ignore_sender, is_ack_required)(fun, component_getter, std::forward(args)...); } else { From ce3d73dc439e861658ad4561116e88796d8278c4 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 15:09:04 -0600 Subject: [PATCH 3/7] More cleanup --- zone/client_version.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/zone/client_version.h b/zone/client_version.h index 5e4e9ce10..640720fb2 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -16,9 +16,10 @@ namespace ClientPatch { using ClientList = std::unordered_map; +template using ComponentGetter = std::function; template -requires std::is_member_function_pointer_v + requires std::is_member_function_pointer_v static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) { std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); @@ -29,9 +30,8 @@ static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) // packet generator queue functions static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true) { - return [=](Fun fun, - std::function component_getter, Args&&... args) - requires std::is_member_function_pointer_v + return [=](Fun fun, ComponentGetter component, Args&&... args) + requires std::is_member_function_pointer_v { std::vector>> build_packets; std::unordered_map client_list = entity_list.GetClientList(); @@ -45,7 +45,7 @@ static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = if (packet_it == build_packets.end()) packet_it = build_packets.emplace(build_packets.end(), ent->ClientVersion(), - std::invoke(fun, component_getter(ent), std::forward(args)...)); + std::invoke(fun, component(ent), std::forward(args)...)); if (packet_it->second != nullptr) ent->QueuePacket(packet_it->second.get(), ackreq, Client::CLIENT_CONNECTED); @@ -61,12 +61,11 @@ static auto QueueCloseClients( { if (distance <= 0) distance = static_cast(zone->GetClientUpdateRange()); - return [=](Fun fun, - std::function component_getter, Args&&... args) - requires std::is_member_function_pointer_v + return [=](Fun fun, ComponentGetter component, Args&&... args) + requires std::is_member_function_pointer_v { if (sender == nullptr) { - QueueClients(sender, ignore_sender, is_ack_required)(fun, component_getter, std::forward(args)...); + QueueClients(sender, ignore_sender, is_ack_required)(fun, component, std::forward(args)...); } else { float distance_squared = distance * distance; std::vector>> build_packets; @@ -87,7 +86,7 @@ static auto QueueCloseClients( if (packet_it == build_packets.end()) packet_it = build_packets.emplace(build_packets.end(), client->ClientVersion(), - std::invoke(fun, component_getter(client), std::forward(args)...)); + std::invoke(fun, component(client), std::forward(args)...)); if (packet_it->second != nullptr) client->QueuePacket(packet_it->second.get(), is_ack_required, Client::CLIENT_CONNECTED); @@ -124,9 +123,9 @@ static auto CloseMessageString( Mob* skipped_mob = nullptr, bool is_ack_required = true, eqFilterType filter = FilterNone) { - return [=](uint32_t type, uint32_t id, Args&&... args) { - static_assert(sizeof...(Args) <= 9, "Too many arguments"); - + return [=](uint32_t type, uint32_t id, Args&&... args) + requires (sizeof...(Args) <= 9) + { auto queue_close_clients = ClientPatch::QueueCloseClients(sender, ignore_sender, distance, skipped_mob, is_ack_required, filter); From 0a6dd09f2cadb2242c72f09a605156d13e68bab0 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 15:51:15 -0600 Subject: [PATCH 4/7] Removed unnecessary function for static component map --- common/patches/client_version.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/common/patches/client_version.cpp b/common/patches/client_version.cpp index a4faebf59..94ac4b483 100644 --- a/common/patches/client_version.cpp +++ b/common/patches/client_version.cpp @@ -61,24 +61,19 @@ struct ClientComponents std::unique_ptr messageComponent; }; -static const ClientComponents& GetComponents(Version version) -{ - static const std::unordered_map patches = [] { - std::unordered_map p; - p.emplace(Version::Titanium, Version::Titanium); - p.emplace(Version::SoF, Version::SoF); - p.emplace(Version::SoD, Version::SoD); - p.emplace(Version::UF, Version::UF); - p.emplace(Version::RoF, Version::RoF); - p.emplace(Version::RoF2, Version::RoF2); - p.emplace(Version::TOB, Version::TOB); - return p; - }(); - - return patches.at(version); -} +static const std::unordered_map s_patches = [] { + std::unordered_map p; + p.emplace(Version::Titanium, Version::Titanium); + p.emplace(Version::SoF, Version::SoF); + p.emplace(Version::SoD, Version::SoD); + p.emplace(Version::UF, Version::UF); + p.emplace(Version::RoF, Version::RoF); + p.emplace(Version::RoF2, Version::RoF2); + p.emplace(Version::TOB, Version::TOB); + return p; +}(); const std::unique_ptr& GetMessageComponent(Version version) { - return GetComponents(version).messageComponent; + return s_patches.at(version).messageComponent; } From 40870924e900580034d057008d61894f263bfa13 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 16:54:30 -0600 Subject: [PATCH 5/7] Updated patch map to an array and added null safety checks for the conversion --- common/patches/client_version.cpp | 31 ++++++++++++++++++------------- common/patches/client_version.h | 2 +- zone/client_version.h | 24 +++++++++++++++--------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/common/patches/client_version.cpp b/common/patches/client_version.cpp index 94ac4b483..667c525b5 100644 --- a/common/patches/client_version.cpp +++ b/common/patches/client_version.cpp @@ -51,9 +51,11 @@ struct ClientComponents case Version::SoF: messageComponent = std::make_unique(); break; - default: + case Version::Titanium: messageComponent = std::make_unique(); break; + default: + break; } } @@ -61,19 +63,22 @@ struct ClientComponents std::unique_ptr messageComponent; }; -static const std::unordered_map s_patches = [] { - std::unordered_map p; - p.emplace(Version::Titanium, Version::Titanium); - p.emplace(Version::SoF, Version::SoF); - p.emplace(Version::SoD, Version::SoD); - p.emplace(Version::UF, Version::UF); - p.emplace(Version::RoF, Version::RoF); - p.emplace(Version::RoF2, Version::RoF2); - p.emplace(Version::TOB, Version::TOB); - return p; -}(); +// this array must be in the same order as the Version enum because it converts Version to index directly +static const std::array s_patches = { + { + ClientComponents(Version::Unknown), // empty + ClientComponents(Version::Client62), // empty + ClientComponents(Version::Titanium), + ClientComponents(Version::SoF), + ClientComponents(Version::SoD), + ClientComponents(Version::UF), + ClientComponents(Version::RoF), + ClientComponents(Version::RoF2), + ClientComponents(Version::TOB), + } +}; const std::unique_ptr& GetMessageComponent(Version version) { - return s_patches.at(version).messageComponent; + return s_patches.at(static_cast(version)).messageComponent; } diff --git a/common/patches/client_version.h b/common/patches/client_version.h index 054cd2a7d..a0ebf9c3a 100644 --- a/common/patches/client_version.h +++ b/common/patches/client_version.h @@ -9,5 +9,5 @@ namespace Message { class IMessage; } -// store all static functions for the different patches here +// store all static functions for the different patches here, this can return nullptr for unsupported patches const std::unique_ptr& GetMessageComponent(EQ::versions::ClientVersion version); diff --git a/zone/client_version.h b/zone/client_version.h index 640720fb2..6c710658c 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -22,15 +22,17 @@ template requires std::is_member_function_pointer_v static void QueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) { - std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); - if (app) - c->QueuePacket(app.get()); + if (obj != nullptr) { + std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); + if (app) + c->QueuePacket(app.get()); + } } // packet generator queue functions static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = true) { - return [=](Fun fun, ComponentGetter component, Args&&... args) + return [=](Fun fun, const ComponentGetter& component, Args&&... args) requires std::is_member_function_pointer_v { std::vector>> build_packets; @@ -44,8 +46,9 @@ static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = }); if (packet_it == build_packets.end()) - packet_it = build_packets.emplace(build_packets.end(), ent->ClientVersion(), - std::invoke(fun, component(ent), std::forward(args)...)); + if (Obj* comp = component(ent); comp != nullptr) + packet_it = build_packets.emplace(build_packets.end(), ent->ClientVersion(), + std::invoke(fun, comp, std::forward(args)...)); if (packet_it->second != nullptr) ent->QueuePacket(packet_it->second.get(), ackreq, Client::CLIENT_CONNECTED); @@ -61,7 +64,7 @@ static auto QueueCloseClients( { if (distance <= 0) distance = static_cast(zone->GetClientUpdateRange()); - return [=](Fun fun, ComponentGetter component, Args&&... args) + return [=](Fun fun, const ComponentGetter& component, Args&&... args) requires std::is_member_function_pointer_v { if (sender == nullptr) { @@ -85,8 +88,9 @@ static auto QueueCloseClients( }); if (packet_it == build_packets.end()) - packet_it = build_packets.emplace(build_packets.end(), client->ClientVersion(), - std::invoke(fun, component(client), std::forward(args)...)); + if (auto comp = component(client); comp != nullptr) + packet_it = build_packets.emplace(build_packets.end(), client->ClientVersion(), + std::invoke(fun, comp, std::forward(args)...)); if (packet_it->second != nullptr) client->QueuePacket(packet_it->second.get(), is_ack_required, Client::CLIENT_CONNECTED); @@ -101,6 +105,8 @@ static auto QueueCloseClients( // Helpers for the Message interface to send message packets namespace Message { + +// this can return nullptr when the component doesn't exist for the version static std::function GetComponent = [](const Client* c) -> IMessage* { return GetMessageComponent(c->GetClientVersion()).get(); }; From cebafe39714527b6c51ca47861530120ad57f8b8 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 21:46:17 -0600 Subject: [PATCH 6/7] Added missing include --- common/patches/client_version.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/patches/client_version.cpp b/common/patches/client_version.cpp index 667c525b5..0c00906f5 100644 --- a/common/patches/client_version.cpp +++ b/common/patches/client_version.cpp @@ -26,6 +26,8 @@ #include "common/patches/rof2.h" #include "common/patches/tob.h" +#include + using Version = EQ::versions::ClientVersion; struct ClientComponents From 1e1701efb7b6ae961a03f83a19391561ca36d9c9 Mon Sep 17 00:00:00 2001 From: dannuic Date: Sat, 25 Apr 2026 23:25:43 -0600 Subject: [PATCH 7/7] Converted memoization to static array --- zone/client_version.h | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/zone/client_version.h b/zone/client_version.h index 6c710658c..80032c452 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -35,23 +35,18 @@ static auto QueueClients(Mob* sender, bool ignore_sender = false, bool ackreq = return [=](Fun fun, const ComponentGetter& component, Args&&... args) requires std::is_member_function_pointer_v { - std::vector>> build_packets; + std::array, EQ::versions::ClientVersionCount> build_packets; std::unordered_map client_list = entity_list.GetClientList(); for (auto [_, ent] : client_list) { if (!ignore_sender || ent != sender) { - auto packet_it = std::find_if(build_packets.begin(), build_packets.end(), - [version = ent->GetClientVersion()](const auto& build_packet) { - return build_packet.first == version; - }); + auto& packet = build_packets.at(static_cast(ent->ClientVersion())); + if (!packet) + if (auto comp = component(ent); comp != nullptr) + packet = std::invoke(fun, comp, std::forward(args)...); - if (packet_it == build_packets.end()) - if (Obj* comp = component(ent); comp != nullptr) - packet_it = build_packets.emplace(build_packets.end(), ent->ClientVersion(), - std::invoke(fun, comp, std::forward(args)...)); - - if (packet_it->second != nullptr) - ent->QueuePacket(packet_it->second.get(), ackreq, Client::CLIENT_CONNECTED); + if (packet) + ent->QueuePacket(packet.get(), ackreq, Client::CLIENT_CONNECTED); } } }; @@ -71,7 +66,7 @@ static auto QueueCloseClients( QueueClients(sender, ignore_sender, is_ack_required)(fun, component, std::forward(args)...); } else { float distance_squared = distance * distance; - std::vector>> build_packets; + std::array, EQ::versions::ClientVersionCount> build_packets; for (auto& [_, mob] : sender->GetCloseMobList(distance)) { if (mob && mob->IsClient()) { @@ -82,18 +77,13 @@ static auto QueueCloseClients( && client->Connected() && client->ShouldGetPacket(sender, filter)) { - auto packet_it = std::find_if(build_packets.begin(), build_packets.end(), - [version = client->GetClientVersion()](const auto& build_packet) { - return build_packet.first == version; - }); - - if (packet_it == build_packets.end()) + auto& packet = build_packets.at(static_cast(client->ClientVersion())); + if (!packet) if (auto comp = component(client); comp != nullptr) - packet_it = build_packets.emplace(build_packets.end(), client->ClientVersion(), - std::invoke(fun, comp, std::forward(args)...)); + packet = std::invoke(fun, comp, std::forward(args)...); - if (packet_it->second != nullptr) - client->QueuePacket(packet_it->second.get(), is_ack_required, Client::CLIENT_CONNECTED); + if (packet) + client->QueuePacket(packet.get(), is_ack_required, Client::CLIENT_CONNECTED); } } }