diff --git a/common/emu_versions.h b/common/emu_versions.h index 798503317..c732e994b 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -61,8 +61,8 @@ namespace EQ maskAllClients = 0xFFFFFFFF }; - constexpr ClientVersion LastClientVersion = ClientVersion::TOB; - constexpr size_t ClientVersionCount = (static_cast(LastClientVersion) + 1); + inline constexpr ClientVersion LastClientVersion = ClientVersion::TOB; + inline constexpr size_t ClientVersionCount = (static_cast(LastClientVersion) + 1); bool IsValidClientVersion(ClientVersion client_version); ClientVersion ValidateClientVersion(ClientVersion client_version); diff --git a/common/links.cpp b/common/links.cpp index b8fa758af..6508fa44a 100644 --- a/common/links.cpp +++ b/common/links.cpp @@ -4,7 +4,28 @@ #include "links.h" -std::string Links::FormatSpellLink(uint32_t SpellID, const std::string& SpellName) +#include "spdat.h" + +void Links::FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item) { - return fmt::format("{}63^{}^0^'{}{}", ITEM_TAG_CHAR, SpellID, SpellName.c_str(), ITEM_TAG_CHAR); + // TODO: Reverse 0x14064B220 to get definition of this function +} + +void Links::FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID, + const char* spellNameOverride) +{ + snprintf(Buffer, BufferSize, "%c%d3^%d^0^'%s%c", ITEM_TAG_CHAR, ETAG_SPELL, SpellID, + spellNameOverride && spellNameOverride[0] ? spellNameOverride : GetSpellName(SpellID), ITEM_TAG_CHAR); +} + +void Links::FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword, std::string_view text) +{ + if (text.empty()) { + snprintf(Buffer, BufferSize, "%c%d%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE, + static_cast(keyword.length()), keyword.data(), ITEM_TAG_CHAR); + } else { + snprintf(Buffer, BufferSize, "%c%d%.*s:%.*s%c", ITEM_TAG_CHAR, ETAG_DIALOG_RESPONSE, + static_cast(keyword.length()), keyword.data(), + static_cast(text.length()), text.data(), ITEM_TAG_CHAR); + } } diff --git a/common/links.h b/common/links.h index 49d2e8fab..d01940cba 100644 --- a/common/links.h +++ b/common/links.h @@ -3,9 +3,59 @@ // #pragma once +#include "item_instance.h" + +namespace EQ { class ItemInstance; } namespace Links { - constexpr char ITEM_TAG_CHAR = '\x12'; - std::string FormatSpellLink(uint32_t SpellID, const std::string& SpellName); +// Max Link Size in bytes +constexpr size_t MAX_LINK_SIZE = 512; + +// Universal link tag character +constexpr char ITEM_TAG_CHAR = '\x12'; + +// Enumeration of different types of item tags +enum ETagCodes +{ + ETAG_ITEM = 0, + ETAG_PLAYER, + ETAG_SPAM, + ETAG_ACHIEVEMENT, + ETAG_DIALOG_RESPONSE, + ETAG_COMMAND, + ETAG_SPELL, + ETAG_FACTION, + ETAG_COMMAND2, + ETAG_UNKNOWN9, + + ETAG_COUNT, + ETAG_FIRST = ETAG_ITEM, + ETAG_LAST = ETAG_UNKNOWN9, + + ETAG_INVALID = -1, +}; + +//---------------------------------------------------------------------------- +// Link Formatting -- Pulled from MQ code + +// Create an achievement link for the given achievement. +// TODO: implement this when achievements are added, leave the signature here for reference. Code in eqlib's ItemLinks.cpp +// void FormatAchievementLink(char* Buffer, size_t BufferSize, const Achievement* achievement, +// std::string_view playerName); + +// Create an item link from the given item. +void FormatItemLink(char* Buffer, size_t BufferSize, const EQ::ItemInstance* item); + +// Create a spell link for the given spell, with optional spell name override. Spells on items often have +// spell name overrides that changes the display name of the spell. +void FormatSpellLink(char* Buffer, size_t BufferSize, uint32_t SpellID, + const char* spellNameOverride = nullptr); + +// Format text into a clickable dialog link. The keyword is the text that will be displayed in the chat window, +// and the text is the text that will be sent to the server when the link is clicked. If no text is provided, +// then the keyword will be used as the text. +void FormatDialogLink(char* Buffer, size_t BufferSize, std::string_view keyword, + std::string_view text = {}); + } diff --git a/zone/client.cpp b/zone/client.cpp index 3306c9a1f..9ae791995 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3895,14 +3895,14 @@ bool Client::ShouldGetPacket(Mob *sender, eqFilterType filter) if (sender == this) return true; - auto g = GetGroup(); + Group* g = GetGroup(); if (g && g->IsGroupMember(sender)) return true; - auto r = GetRaid(); + Raid* r = GetRaid(); if (r && sender->IsClient()) { - auto rgid1 = r->GetGroup(this); - auto rgid2 = r->GetGroup(sender->CastToClient()); + uint32 rgid1 = r->GetGroup(this); + uint32 rgid2 = r->GetGroup(sender->CastToClient()); if (rgid1 != RAID_GROUPLESS && rgid1 == rgid2) return true; } else { diff --git a/zone/patch/client_version.h b/zone/patch/client_version.h index ee274c630..23e493959 100644 --- a/zone/patch/client_version.h +++ b/zone/patch/client_version.h @@ -37,7 +37,7 @@ public: return [=](Fun fun, Args&&... args) { static_assert(std::is_member_function_pointer_v); std::unordered_map build_packets; - auto client_list = entity_list.GetClientList(); + std::unordered_map client_list = entity_list.GetClientList(); for (auto [_, ent] : client_list) { if (!ignore_sender || ent != sender) { diff --git a/zone/patch/components/message/IMessage.h b/zone/patch/components/message/IMessage.h index f82f7ee59..153544e95 100644 --- a/zone/patch/components/message/IMessage.h +++ b/zone/patch/components/message/IMessage.h @@ -29,8 +29,8 @@ namespace ZoneClient::Message { class IMessage { public: - constexpr IMessage() {} - constexpr virtual ~IMessage() {} + IMessage() {} + virtual ~IMessage() {} // these two are the basic string message packets virtual EQApplicationPacket* Simple(uint32_t color, uint32_t id) const = 0; diff --git a/zone/patch/components/message/rof.h b/zone/patch/components/message/rof.h index 5ba087845..487df6586 100644 --- a/zone/patch/components/message/rof.h +++ b/zone/patch/components/message/rof.h @@ -23,7 +23,7 @@ namespace ZoneClient::Message { class RoF : public UF { public: - constexpr RoF() {} - constexpr ~RoF() override {} + RoF() {} + ~RoF() override {} }; } // namespace Zone::Message diff --git a/zone/patch/components/message/rof2.h b/zone/patch/components/message/rof2.h index 8ab9c1720..1318c007a 100644 --- a/zone/patch/components/message/rof2.h +++ b/zone/patch/components/message/rof2.h @@ -23,7 +23,7 @@ namespace ZoneClient::Message { class RoF2 : public RoF { public: - constexpr RoF2() {} - constexpr ~RoF2() override {} + RoF2() {} + ~RoF2() override {} }; } // namespace Zone::Message diff --git a/zone/patch/components/message/sod.h b/zone/patch/components/message/sod.h index eb23c10b5..16b14b10b 100644 --- a/zone/patch/components/message/sod.h +++ b/zone/patch/components/message/sod.h @@ -23,7 +23,7 @@ namespace ZoneClient::Message { class SoD : public SoF { public: - constexpr SoD() {} - constexpr ~SoD() override {} + SoD() {} + ~SoD() override {} }; } // namespace Zone::Message diff --git a/zone/patch/components/message/sof.h b/zone/patch/components/message/sof.h index 274498545..3b8c1d1ad 100644 --- a/zone/patch/components/message/sof.h +++ b/zone/patch/components/message/sof.h @@ -23,7 +23,7 @@ namespace ZoneClient::Message { class SoF : public Titanium { public: - constexpr SoF() {} - constexpr ~SoF() override {} + SoF() {} + ~SoF() override {} }; } // namespace Zone::Message diff --git a/zone/patch/components/message/titanium.cpp b/zone/patch/components/message/titanium.cpp index 63633c36a..73ab4e85c 100644 --- a/zone/patch/components/message/titanium.cpp +++ b/zone/patch/components/message/titanium.cpp @@ -86,7 +86,7 @@ EQApplicationPacket* Titanium::InterruptSpellOther( Mob* sender, uint32_t message, uint32_t spawn_id, uint32_t spell_id, const char* spell_name_override) const { - auto name = sender->GetCleanName(); + const char* name = sender->GetCleanName(); auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(name) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); diff --git a/zone/patch/components/message/titanium.h b/zone/patch/components/message/titanium.h index 6b98c44fa..4c89ad10c 100644 --- a/zone/patch/components/message/titanium.h +++ b/zone/patch/components/message/titanium.h @@ -23,8 +23,8 @@ namespace ZoneClient::Message { class Titanium : public IMessage { public: - constexpr Titanium() {} - constexpr ~Titanium() override {} + Titanium() {} + ~Titanium() override {} EQApplicationPacket* Simple(uint32_t color, uint32_t id) const override; diff --git a/zone/patch/components/message/tob.cpp b/zone/patch/components/message/tob.cpp index 8a23f2b15..c107918d5 100644 --- a/zone/patch/components/message/tob.cpp +++ b/zone/patch/components/message/tob.cpp @@ -82,7 +82,7 @@ static void ServerToTOBConvertLinks(std::string& message_out, const std::string& return; } - auto segments = Strings::Split(message_in, '\x12'); + std::vector segments = Strings::Split(message_in, '\x12'); for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { if (segment_iter & 1) { auto etag = std::stoi(segments[segment_iter].substr(0, 1)); @@ -90,43 +90,43 @@ static void ServerToTOBConvertLinks(std::string& message_out, const std::string& switch (etag) { case 0: { size_t index = 1; - auto item_id = segments[segment_iter].substr(index, 5); + std::string item_id = segments[segment_iter].substr(index, 5); index += 5; - auto aug1 = segments[segment_iter].substr(index, 5); + std::string aug1 = segments[segment_iter].substr(index, 5); index += 5; - auto aug2 = segments[segment_iter].substr(index, 5); + std::string aug2 = segments[segment_iter].substr(index, 5); index += 5; - auto aug3 = segments[segment_iter].substr(index, 5); + std::string aug3 = segments[segment_iter].substr(index, 5); index += 5; - auto aug4 = segments[segment_iter].substr(index, 5); + std::string aug4 = segments[segment_iter].substr(index, 5); index += 5; - auto aug5 = segments[segment_iter].substr(index, 5); + std::string aug5 = segments[segment_iter].substr(index, 5); index += 5; - auto aug6 = segments[segment_iter].substr(index, 5); + std::string aug6 = segments[segment_iter].substr(index, 5); index += 5; - auto is_evolving = segments[segment_iter].substr(index, 1); + std::string is_evolving = segments[segment_iter].substr(index, 1); index += 1; - auto evolutionGroup = segments[segment_iter].substr(index, 4); + std::string evolutionGroup = segments[segment_iter].substr(index, 4); index += 4; - auto evolutionLevel = segments[segment_iter].substr(index, 2); + std::string evolutionLevel = segments[segment_iter].substr(index, 2); index += 2; - auto ornamentationIconID = segments[segment_iter].substr(index, 5); + std::string ornamentationIconID = segments[segment_iter].substr(index, 5); index += 5; - auto itemHash = segments[segment_iter].substr(index, 8); + std::string itemHash = segments[segment_iter].substr(index, 8); index += 8; - auto text = segments[segment_iter].substr(index); + std::string text = segments[segment_iter].substr(index); message_out.push_back('\x12'); message_out.push_back('0'); //etag item @@ -202,13 +202,14 @@ EQApplicationPacket* TOB::InterruptSpell(uint32_t message, uint32_t spawn_id, ui ? GetSpellName(spell_id) : spell_name_override; - std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id, spell_name_override); - auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + spell_link.size() + 1); + auto outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(spell_link) + 1); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); ic->spawnid = spawn_id; - fmt::format_to_n(ic->message, spell_link.size(), "{}", spell_link); + fmt::format_to_n(ic->message, strlen(spell_link) + 1, "{}\0", spell_link); outapp->priority = 5; return outapp; @@ -221,15 +222,16 @@ EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uin ? GetSpellName(spell_id) : spell_name_override; - std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); - auto name = sender->GetCleanName(); + const char* name = sender->GetCleanName(); auto outapp = new EQApplicationPacket(OP_InterruptCast, - sizeof(InterruptCast_Struct) + strlen(name) + spell_link.size() + 2); + sizeof(InterruptCast_Struct) + strlen(name) + strlen(spell_link) + 2); auto ic = reinterpret_cast(outapp->pBuffer); ic->messageid = ResolveID(message); ic->spawnid = spawn_id; - fmt::format_to_n(ic->message, strlen(name) + spell_link.size() + 2, "{}\0{}\0", name, spell_link); + fmt::format_to_n(ic->message, strlen(name) + strlen(spell_link) + 2, "{}\0{}\0", name, spell_link); return outapp; } @@ -237,16 +239,20 @@ EQApplicationPacket* TOB::InterruptSpellOther(Mob* sender, uint32_t message, uin EQApplicationPacket* TOB::Fizzle(uint32_t type, uint32_t message, uint32_t spell_id) const { std::string spell_name(GetSpellName(spell_id)); - std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); - return Formatted(type, message, spell_link.c_str()); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); + + return Formatted(type, message, spell_link); } EQApplicationPacket* TOB::FizzleOther(uint32_t type, uint32_t message, uint32_t spell_id, const char* caster) const { std::string spell_name(GetSpellName(spell_id)); - std::string spell_link = Links::FormatSpellLink(spell_id, spell_name); - return Formatted(type, message, caster, spell_link.c_str()); + char spell_link[Links::MAX_LINK_SIZE]; + Links::FormatSpellLink(spell_link, Links::MAX_LINK_SIZE, spell_id); + + return Formatted(type, message, caster, spell_link); } } // namespace Zone::Message diff --git a/zone/patch/components/message/tob.h b/zone/patch/components/message/tob.h index ceb4a3594..50d5d5222 100644 --- a/zone/patch/components/message/tob.h +++ b/zone/patch/components/message/tob.h @@ -23,8 +23,8 @@ namespace ZoneClient::Message { class TOB : public RoF2 { public: - constexpr TOB() {} - constexpr ~TOB() override {} + TOB() {} + ~TOB() override {} EQApplicationPacket* Formatted(uint32_t color, uint32_t id, const char* a1 = nullptr, const char* a2 = nullptr, const char* a3 = nullptr, diff --git a/zone/patch/components/message/uf.h b/zone/patch/components/message/uf.h index 731ca006d..f19677ae0 100644 --- a/zone/patch/components/message/uf.h +++ b/zone/patch/components/message/uf.h @@ -23,7 +23,7 @@ namespace ZoneClient::Message { class UF : public SoD { public: - constexpr UF() {} - constexpr ~UF() override {} + UF() {} + ~UF() override {} }; } // namespace Zone::Message