diff --git a/common/patches/IBuff.h b/common/patches/IBuff.h index 42ebec462..176d0981c 100644 --- a/common/patches/IBuff.h +++ b/common/patches/IBuff.h @@ -4,9 +4,9 @@ #pragma once +#include "client_version.h" #include "common/emu_opcodes.h" -#include #include #include "common/types.h" @@ -24,11 +24,14 @@ public: IBuff() = default; virtual ~IBuff() = default; - virtual EQApplicationPacket* MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, bool clear_buffs) const = 0; + virtual std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + bool clear_buffs) const = 0; - virtual EQApplicationPacket* BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const = 0; - virtual EQApplicationPacket* RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const = 0; - virtual void SetRefreshType(EQApplicationPacket* packet, Mob* source, Client* target) const = 0; + virtual std::unique_ptr BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, + bool fade) const = 0; + virtual std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, + bool buff_timers_suspended, const std::vector& slots) const = 0; + virtual void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const = 0; }; } // namespace Buff 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 581ab155d..ca58711fc 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 @@ -34,64 +36,64 @@ struct ClientComponents { switch (version) { case Version::TOB: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; case Version::RoF2: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; case Version::RoF: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; case Version::UF: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; case Version::SoD: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; case Version::SoF: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); + break; + case Version::Titanium: + buffComponent = std::make_unique(); + messageComponent = std::make_unique(); break; default: - buffComponent = std::make_shared(); - messageComponent = std::make_shared(); break; } } const Version version; - std::shared_ptr buffComponent; - std::shared_ptr messageComponent; + std::unique_ptr buffComponent; + 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; - }(); +// 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), + } +}; - return patches.at(version); +const std::unique_ptr& GetBuffComponent(Version version) +{ + return s_patches.at(static_cast(version)).buffComponent; } -const std::shared_ptr& GetBuffComponent(Version version) +const std::unique_ptr& GetMessageComponent(Version version) { - return GetComponents(version).buffComponent; -} - -const std::shared_ptr& GetMessageComponent(Version version) -{ - return GetComponents(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 092df844c..45b4d88e4 100644 --- a/common/patches/client_version.h +++ b/common/patches/client_version.h @@ -11,5 +11,6 @@ namespace Buff { class IBuff; } namespace Message { class IMessage; } // store all static functions for the different patches here -const std::shared_ptr& GetBuffComponent(EQ::versions::ClientVersion version); -const std::shared_ptr& GetMessageComponent(EQ::versions::ClientVersion version); +// store all static functions for the different patches here, this can return nullptr for unsupported patches +const std::unique_ptr& GetBuffComponent(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 2b7c96e9a..3c9bfb1eb 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -3924,12 +3924,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; @@ -3941,7 +3940,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); @@ -3963,15 +3962,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; @@ -3980,10 +3980,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; @@ -4018,7 +4019,8 @@ void Titanium::ResolveArguments(uint32_t id, std::array& args) c } // namespace Message namespace Buff { -EQApplicationPacket* Titanium::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, bool clear_buffs) const +std::unique_ptr Titanium::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + bool clear_buffs) const { uint32 count = 0; uint32 buff_count; @@ -4040,15 +4042,10 @@ EQApplicationPacket* Titanium::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bo } } - EQApplicationPacket* outapp = nullptr; - //Create it for a targeting window, else create it for a create buff packet. - if(for_target) { - outapp = new EQApplicationPacket(OP_RefreshTargetBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); - } - else { - outapp = new EQApplicationPacket(OP_RefreshBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); - } + auto outapp = std::make_unique(for_target ? OP_RefreshTargetBuffs : OP_RefreshBuffs, + sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; buff->entity_id = mob->GetID(); buff->count = count; @@ -4078,17 +4075,18 @@ EQApplicationPacket* Titanium::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bo return outapp; } -EQApplicationPacket* Titanium::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const +std::unique_ptr Titanium::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, + bool fade) const { return nullptr; } -EQApplicationPacket* Titanium::RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, +std::unique_ptr Titanium::RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const { return nullptr; } -void Titanium::SetRefreshType(EQApplicationPacket* packet, Mob* source, Client* target) const {} +void Titanium::SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const {} } // namespace Buff diff --git a/common/patches/titanium.h b/common/patches/titanium.h index 4ed42e8df..33c48ccce 100644 --- a/common/patches/titanium.h +++ b/common/patches/titanium.h @@ -60,11 +60,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: @@ -82,11 +85,14 @@ public: Titanium() = default; ~Titanium() override = default; - EQApplicationPacket* MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, bool clear_buffs) const override; + std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + bool clear_buffs) const override; - EQApplicationPacket* BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; - EQApplicationPacket* RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const override; - void SetRefreshType(EQApplicationPacket* packet, Mob* source, Client* target) const override; + std::unique_ptr + BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; + std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, + bool buff_timers_suspended, const std::vector& slots) const override; + void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const override; }; } // namespace Buff diff --git a/common/patches/tob.cpp b/common/patches/tob.cpp index 34393c761..79853f4cb 100644 --- a/common/patches/tob.cpp +++ b/common/patches/tob.cpp @@ -5550,7 +5550,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) { @@ -5576,15 +5577,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; @@ -5594,10 +5596,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); @@ -5610,12 +5613,12 @@ EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uin } // namespace Message namespace Buff { +std::unique_ptr TOB::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + bool clear_buffs) const { return nullptr; } -EQApplicationPacket* TOB::MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, bool clear_buffs) const { return nullptr; } - -EQApplicationPacket* TOB::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const +std::unique_ptr TOB::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const { - auto packet = new EQApplicationPacket(OP_BuffDefinition, sizeof(::TOB::structs::EQAffectPacket_Struct)); + auto packet = std::make_unique(OP_BuffDefinition, sizeof(::TOB::structs::EQAffectPacket_Struct)); auto affect = reinterpret_cast<::TOB::structs::EQAffectPacket_Struct*>(packet->pBuffer); // base packet @@ -5665,7 +5668,8 @@ EQApplicationPacket* TOB::BuffDefinition(Mob* mob, const Buffs_Struct& buff, int return packet; } -EQApplicationPacket* TOB::RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const +std::unique_ptr TOB::RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, + bool buff_timers_suspended, const std::vector& slots) const { Buffs_Struct* buffs = mob->GetBuffs(); @@ -5704,11 +5708,11 @@ EQApplicationPacket* TOB::RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer buffer.WriteUInt8(opcode == OP_RefreshPetBuffs ? 2 : 0); buffer.WriteUInt8(buff_timers_suspended ? 1 : 0); // bBuffTimersOnHold - return new EQApplicationPacket(opcode, std::move(buffer)); + return std::make_unique(opcode, std::move(buffer)); } // 0 = self buff window, 1 = self target window, 2 = pet buff or target window, 4 = group, 5 = PC, 7 = NPC -void TOB::SetRefreshType(EQApplicationPacket* packet, Mob* source, Client* target) const +void TOB::SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const { unsigned char* type = &packet->pBuffer[packet->size - 2]; diff --git a/common/patches/tob.h b/common/patches/tob.h index cb4f6f3f0..d27a5c0ff 100644 --- a/common/patches/tob.h +++ b/common/patches/tob.h @@ -42,10 +42,13 @@ public: TOB() = default; ~TOB() override = default; - [[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; @@ -62,11 +65,14 @@ public: TOB() = default; ~TOB() override = default; - EQApplicationPacket* MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, bool clear_buffs) const override; + std::unique_ptr MakeLegacyBuffsPacket(Mob* mob, int32_t timer, bool for_target, + bool clear_buffs) const override; - EQApplicationPacket* BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; - EQApplicationPacket* RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, bool buff_timers_suspended, const std::vector& slots) const override; - void SetRefreshType(EQApplicationPacket* packet, Mob* source, Client* target) const override; + std::unique_ptr + BuffDefinition(Mob* mob, const Buffs_Struct& buff, int slot, bool fade) const override; + std::unique_ptr RefreshBuffs(EmuOpcode opcode, Mob* mob, int32_t timer, bool remove, + bool buff_timers_suspended, const std::vector& slots) const override; + void SetRefreshType(const std::unique_ptr& packet, Mob* source, Client* target) const override; }; } // namespace Buff diff --git a/zone/client_version.h b/zone/client_version.h index ef40ceec0..974ab1693 100644 --- a/zone/client_version.h +++ b/zone/client_version.h @@ -17,42 +17,39 @@ namespace ClientPatch { using ClientList = std::unordered_map; +template using ComponentGetter = std::function; 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); - EQApplicationPacket* app = std::invoke(fun, obj, std::forward(args)...); - if (app != nullptr) { - c->QueuePacket(app); - delete app; + 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, - 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; + return [=](Fun fun, const ComponentGetter& component, Args&&... args) + requires std::is_member_function_pointer_v + { + 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, _] = build_packets.try_emplace( - ent->ClientVersion(), - std::invoke(fun, component_getter(ent), std::forward(args)...)); + 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->second != nullptr) - ent->QueuePacket(packet->second, ackreq, Client::CLIENT_CONNECTED); + if (packet) + ent->QueuePacket(packet.get(), ackreq, Client::CLIENT_CONNECTED); } } - - for (auto [_, packet] : build_packets) - if (packet != nullptr) - delete packet; }; } @@ -63,15 +60,14 @@ 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"); - + return [=](Fun fun, const 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::unordered_map build_packets; + std::array, EQ::versions::ClientVersionCount> build_packets; for (auto& [_, mob] : sender->GetCloseMobList(distance)) { if (mob && mob->IsClient()) { @@ -80,40 +76,42 @@ 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 = build_packets.at(static_cast(client->ClientVersion())); + if (!packet) + if (auto comp = component(client); comp != nullptr) + packet = std::invoke(fun, comp, std::forward(args)...); - if (packet->second != nullptr) - client->QueuePacket(packet->second, is_ack_required, Client::CLIENT_CONNECTED); + if (packet) + client->QueuePacket(packet.get(), is_ack_required, Client::CLIENT_CONNECTED); } } } - - for (auto [_, packet] : build_packets) - if (packet != nullptr) - delete packet;; } }; } template static void FastQueuePacket(Client* c, Fun fun, Obj* obj, Args&&... args) + requires std::is_member_function_pointer_v { - static_assert(std::is_member_function_pointer_v); - EQApplicationPacket* app = std::invoke(fun, obj, std::forward(args)...); - if (app != nullptr) { - c->FastQueuePacket(&app); // FastQueuePacket inherits the lifetime management of the packet, do not delete or it will be double free + if (obj != nullptr) { + std::unique_ptr app = std::invoke(fun, obj, std::forward(args)...); + if (app) { + // FastQueuePacket specifically takes lifetime management of packet, so release here + EQApplicationPacket* packet = app.release(); + c->FastQueuePacket(&packet); + } } } static auto QueueClientsByTarget(Mob* sender, bool iSendToSender, Mob* SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits, bool inspect_buffs, bool clear_target_window) { - 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"); + return [=](Fun fun, const ComponentGetter component, Args&&... args) + requires std::is_member_function_pointer_v + { std::unordered_map build_packets; std::unordered_map client_list = entity_list.GetClientList(); @@ -167,7 +165,7 @@ static auto QueueClientsByTarget(Mob* sender, bool iSendToSender, Mob* SkipThisM } if (Send && (c->ClientVersionBit() & ClientVersionBits)) { - EQApplicationPacket* app = std::invoke(fun, component_getter(c), std::forward(args)...); + EQApplicationPacket* app = std::invoke(fun, component(c), std::forward(args)...); } } } @@ -180,6 +178,7 @@ static auto QueueClientsByTarget(Mob* sender, bool iSendToSender, Mob* SkipThisM // 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(); }; @@ -202,9 +201,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);