aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Chat/Hyperlinks.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2021-10-31 23:07:56 +0100
committerShauren <shauren.trinity@gmail.com>2021-10-31 23:07:56 +0100
commit8423382fb034d3f1734590f7f246b73f3aa646aa (patch)
tree7ac27a5e8fc53dae8a44a3efcfaca4395f73fe9d /src/server/game/Chat/Hyperlinks.cpp
parent70a62c39785e65711c5068289e2eaba7fc0880c3 (diff)
Core/Chat: Implement hyperlink validation for new link types
Diffstat (limited to 'src/server/game/Chat/Hyperlinks.cpp')
-rw-r--r--src/server/game/Chat/Hyperlinks.cpp435
1 files changed, 397 insertions, 38 deletions
diff --git a/src/server/game/Chat/Hyperlinks.cpp b/src/server/game/Chat/Hyperlinks.cpp
index f66df9b4348..08d1bd37ea3 100644
--- a/src/server/game/Chat/Hyperlinks.cpp
+++ b/src/server/game/Chat/Hyperlinks.cpp
@@ -25,6 +25,7 @@
#include "SharedDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
+#include "StringFormat.h"
#include "World.h"
#include "advstd.h"
@@ -99,6 +100,49 @@ static bool equal_with_len(char const* str1, char const* str2, size_t len)
return !len && !*str1;
}
+static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len)
+{
+ if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId))
+ {
+ CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId);
+ if (!locale)
+ return equal_with_len(creatureTemplate->Name.c_str(), pos, len);
+
+ for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ std::string const& name = (i == DEFAULT_LOCALE) ? creatureTemplate->Name : locale->Name[i];
+ if (name.empty())
+ continue;
+ if (equal_with_len(name.c_str(), pos, len))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <>
+struct LinkValidator<LinkTags::spell>
+{
+ static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len)
+ {
+ return IsTextValid(data.Spell, pos, len);
+ }
+
+ static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*info->SpellName)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(SpellLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_SPELL;
+ }
+};
+
template <>
struct LinkValidator<LinkTags::achievement>
{
@@ -119,6 +163,186 @@ struct LinkValidator<LinkTags::achievement>
};
template <>
+struct LinkValidator<LinkTags::apower>
+{
+ static bool IsTextValid(ArtifactPowerLinkData const& data, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(data.ArtifactPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(ArtifactPowerLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ARTIFACT_POWER;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::azessence>
+{
+ static bool IsTextValid(AzeriteEssenceLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Essence->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(AzeriteEssenceLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Rank + 1];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::battlepet>
+{
+ static bool IsTextValid(BattlePetLinkData const& data, char const* pos, size_t len)
+ {
+ return IsCreatureNameValid(data.Species->CreatureID, pos, len);
+ }
+
+ static bool IsColorValid(BattlePetLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Quality];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::conduit>
+{
+ static bool IsTextValid(SoulbindConduitRankEntry const* rank, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(rank->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(SoulbindConduitRankEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_SPELL;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::currency>
+{
+ static bool IsTextValid(CurrencyLinkData const& data, char const* pos, size_t len)
+ {
+ LocalizedString const* name = data.Container ? &data.Container->ContainerName : &data.Currency->Name;
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*name)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(CurrencyLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[(data.Container ? data.Container->ContainerQuality : data.Currency->Quality)];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::enchant>
+{
+ static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ {
+ SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
+ if (bounds.first == bounds.second)
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+
+ SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID
+ ? bounds.first->second->SkillupSkillLineID
+ : bounds.first->second->SkillLine);
+ if (!skill)
+ return false;
+
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ char const* skillName = skill->DisplayName[i];
+ size_t skillLen = strlen(skillName);
+ if (len > skillLen + 2 && // or of form [Skill Name: Spell Name]
+ !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) &&
+ equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2)))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ENCHANT;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrfollower>
+{
+ static bool IsTextValid(GarrisonFollowerLinkData const& data, char const* pos, size_t len)
+ {
+ return IsCreatureNameValid(data.Follower->HordeCreatureID, pos, len)
+ || IsCreatureNameValid(data.Follower->AllianceCreatureID, pos, len);
+ }
+
+ static bool IsColorValid(GarrisonFollowerLinkData const& data, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[data.Quality];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrfollowerability>
+{
+ static bool IsTextValid(GarrAbilityEntry const* ability, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(ability->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(GarrAbilityEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_GARR_ABILITY;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::garrmission>
+{
+ static bool IsTextValid(GarrisonMissionLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Mission->Name[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(GarrisonMissionLinkData const&, HyperlinkColor c)
+ {
+ return c == QuestDifficultyColors[2];
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::instancelock>
+{
+ static bool IsTextValid(InstanceLockLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(data.Map->MapName[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(InstanceLockLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_INSTANCE_LOCK;
+ }
+};
+
+template <>
struct LinkValidator<LinkTags::item>
{
static bool IsTextValid(ItemLinkData const& data, char const* pos, size_t len)
@@ -127,17 +351,22 @@ struct LinkValidator<LinkTags::item>
if (!(data.Item->GetFlags3() & ITEM_FLAG3_HIDE_NAME_SUFFIX) && data.Suffix)
suffixStrings = &data.Suffix->Description;
+ return IsTextValid(data.Item, suffixStrings, pos, len);
+ }
+
+ static bool IsTextValid(ItemTemplate const* itemTemplate, LocalizedString const* suffixStrings, char const* pos, size_t len)
+ {
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
- std::string name = data.Item->GetName(i);
+ std::string name = itemTemplate->GetName(i);
if (name.empty())
continue;
if (suffixStrings)
{
if (len > name.length() + 1 &&
- (strncmp(name.c_str(), pos, name.length()) == 0) &&
- (*(pos + name.length()) == ' ') &&
- equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1))
+ (strncmp(name.c_str(), pos, name.length()) == 0) &&
+ (*(pos + name.length()) == ' ') &&
+ equal_with_len((*suffixStrings)[i], pos + name.length() + 1, len - name.length() - 1))
return true;
}
else if (equal_with_len(name.c_str(), pos, len))
@@ -153,6 +382,52 @@ struct LinkValidator<LinkTags::item>
};
template <>
+struct LinkValidator<LinkTags::journal>
+{
+ static bool IsTextValid(JournalLinkData const& data, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len((*data.ExpectedText)[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(JournalLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_JOURNAL;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::keystone>
+{
+ static bool IsTextValid(KeystoneLinkData const& data, char const* pos, size_t len)
+ {
+ // Skip "Keystone" prefix - not loading GlobalStrings.db2
+ char const* validateStartPos = strstr(pos, ": ");
+ if (!validateStartPos)
+ return false;
+
+ // skip ": " too
+ validateStartPos += 2;
+ size_t validateLen = len - (validateStartPos - pos);
+
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ std::string expectedText = Trinity::StringFormat("%s (%u)", data.Map->Name[i], data.Level);
+ if (equal_with_len(expectedText.c_str(), validateStartPos, validateLen))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(KeystoneLinkData const&, HyperlinkColor c)
+ {
+ return c == ItemQualityColors[ITEM_QUALITY_EPIC];
+ }
+};
+
+template <>
struct LinkValidator<LinkTags::quest>
{
static bool IsTextValid(QuestLinkData const& data, char const* pos, size_t len)
@@ -183,66 +458,57 @@ struct LinkValidator<LinkTags::quest>
};
template <>
-struct LinkValidator<LinkTags::spell>
+struct LinkValidator<LinkTags::mawpower>
{
- static bool IsTextValid(SpellLinkData const& data, char const* pos, size_t len)
+ static bool IsTextValid(MawPowerEntry const* mawPower, char const* pos, size_t len)
{
- return IsTextValid(data.Spell, pos, len);
- }
-
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
- {
- for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- if (equal_with_len((*info->SpellName)[i], pos, len))
- return true;
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
- static bool IsColorValid(SpellLinkData const&, HyperlinkColor c)
+ static bool IsColorValid(MawPowerEntry const*, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_SPELL;
}
};
template <>
-struct LinkValidator<LinkTags::enchant>
+struct LinkValidator<LinkTags::outfit>
{
- static bool IsTextValid(SpellInfo const* info, char const* pos, size_t len)
+ static bool IsTextValid(std::string const&, char const*, size_t)
{
- SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(info->Id);
- if (bounds.first == bounds.second)
- return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
+ return true;
+ }
- SkillLineEntry const* skill = sSkillLineStore.LookupEntry(bounds.first->second->SkillupSkillLineID
- ? bounds.first->second->SkillupSkillLineID
- : bounds.first->second->SkillLine);
- if (!skill)
- return false;
+ static bool IsColorValid(std::string const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
- for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
- {
- char const* skillName = skill->DisplayName[i];
- size_t skillLen = strlen(skillName);
- if (len > skillLen + 2 && // or of form [Skill Name: Spell Name]
- !strncmp(pos, skillName, skillLen) && !strncmp(pos + skillLen, ": ", 2) &&
- equal_with_len((*info->SpellName)[i], pos + (skillLen + 2), len - (skillLen + 2)))
- return true;
- }
+template <>
+struct LinkValidator<LinkTags::pvptal>
+{
+ static bool IsTextValid(PvpTalentEntry const* mawPower, char const* pos, size_t len)
+ {
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
+ return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
- static bool IsColorValid(SpellInfo const*, HyperlinkColor c)
+ static bool IsColorValid(PvpTalentEntry const*, HyperlinkColor c)
{
- return c == CHAT_LINK_COLOR_ENCHANT;
+ return c == CHAT_LINK_COLOR_TALENT;
}
};
template <>
struct LinkValidator<LinkTags::talent>
{
- static bool IsTextValid(TalentEntry const* talent, char const* pos, size_t len)
+ static bool IsTextValid(TalentEntry const* mawPower, char const* pos, size_t len)
{
- if (SpellInfo const* info = sSpellMgr->GetSpellInfo(talent->SpellID, DIFFICULTY_NONE))
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(mawPower->SpellID, DIFFICULTY_NONE))
return LinkValidator<LinkTags::spell>::IsTextValid(info, pos, len);
return false;
}
@@ -267,6 +533,81 @@ struct LinkValidator<LinkTags::trade>
}
};
+template <>
+struct LinkValidator<LinkTags::transmogappearance>
+{
+ static bool IsTextValid(ItemModifiedAppearanceEntry const* enchantment, char const* pos, size_t len)
+ {
+ if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(enchantment->ItemID))
+ return LinkValidator<LinkTags::item>::IsTextValid(itemTemplate, nullptr, pos, len);
+ return false;
+ }
+
+ static bool IsColorValid(ItemModifiedAppearanceEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::transmogillusion>
+{
+ static bool IsTextValid(SpellItemEnchantmentEntry const* enchantment, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(enchantment->Name[i], pos, len))
+ return true;
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ if (equal_with_len(enchantment->HordeName[i], pos, len))
+ return true;
+ return false;
+ }
+
+ static bool IsColorValid(SpellItemEnchantmentEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::transmogset>
+{
+ static bool IsTextValid(TransmogSetEntry const* set, char const* pos, size_t len)
+ {
+ for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
+ {
+ if (ItemNameDescriptionEntry const* itemNameDescription = sItemNameDescriptionStore.LookupEntry(set->ItemNameDescriptionID))
+ {
+ std::string expectedText = Trinity::StringFormat("%s (%s)", set->Name[i], itemNameDescription->Description[i]);
+ if (equal_with_len(expectedText.c_str(), pos, len))
+ return true;
+ }
+ else if (equal_with_len(set->Name[i], pos, len))
+ return true;
+ }
+ return false;
+ }
+
+ static bool IsColorValid(TransmogSetEntry const*, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_TRANSMOG;
+ }
+};
+
+template <>
+struct LinkValidator<LinkTags::worldmap>
+{
+ static bool IsTextValid(WorldMapLinkData const&, char const*, size_t)
+ {
+ return true;
+ }
+
+ static bool IsColorValid(WorldMapLinkData const&, HyperlinkColor c)
+ {
+ return c == CHAT_LINK_COLOR_ACHIEVEMENT;
+ }
+};
+
#define TryValidateAs(tagname) \
{ \
ASSERT(!strcmp(LinkTags::tagname::tag(), #tagname)); \
@@ -288,17 +629,31 @@ struct LinkValidator<LinkTags::trade>
static bool ValidateLinkInfo(HyperlinkInfo const& info)
{
TryValidateAs(achievement);
+ TryValidateAs(apower);
+ TryValidateAs(azessence);
TryValidateAs(area);
TryValidateAs(areatrigger);
+ TryValidateAs(battlepet);
+ TryValidateAs(conduit);
TryValidateAs(creature);
TryValidateAs(creature_entry);
+ TryValidateAs(currency);
TryValidateAs(enchant);
TryValidateAs(gameevent);
TryValidateAs(gameobject);
TryValidateAs(gameobject_entry);
+ TryValidateAs(garrfollower);
+ TryValidateAs(garrfollowerability);
+ TryValidateAs(garrmission);
+ TryValidateAs(instancelock);
TryValidateAs(item);
TryValidateAs(itemset);
+ TryValidateAs(journal);
+ TryValidateAs(keystone);
+ TryValidateAs(mawpower);
+ TryValidateAs(outfit);
TryValidateAs(player);
+ TryValidateAs(pvptal);
TryValidateAs(quest);
TryValidateAs(skill);
TryValidateAs(spell);
@@ -307,6 +662,10 @@ static bool ValidateLinkInfo(HyperlinkInfo const& info)
TryValidateAs(tele);
TryValidateAs(title);
TryValidateAs(trade);
+ TryValidateAs(transmogappearance);
+ TryValidateAs(transmogillusion);
+ TryValidateAs(transmogset);
+ TryValidateAs(worldmap);
return false;
}