Core/Chat: Implement hyperlink validation for new link types

This commit is contained in:
Shauren
2021-10-31 23:07:56 +01:00
parent 70a62c3978
commit 8423382fb0
6 changed files with 936 additions and 72 deletions

View File

@@ -36,6 +36,20 @@ char const* Trinity::ChatCommands::ArgInfo<AchievementEntry const*>::TryConsume(
return args;
}
struct CurrencyTypesVisitor
{
using value_type = CurrencyTypesEntry const*;
value_type operator()(Hyperlink<currency> currency) const { return currency->Currency; }
value_type operator()(uint32 currencyId) const { return sCurrencyTypesStore.LookupEntry(currencyId); }
};
char const* Trinity::ChatCommands::ArgInfo<CurrencyTypesEntry const*>::TryConsume(CurrencyTypesEntry const*& data, char const* args)
{
Variant <Hyperlink<currency>, uint32> val;
if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
data = boost::apply_visitor(CurrencyTypesVisitor(), val);
return args;
}
struct GameTeleVisitor
{
using value_type = GameTele const*;

View File

@@ -116,6 +116,13 @@ struct TC_GAME_API ArgInfo<AchievementEntry const*>
static char const* TryConsume(AchievementEntry const*&, char const*);
};
// CurrencyTypesEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<CurrencyTypesEntry const*>
{
static char const* TryConsume(CurrencyTypesEntry const*&, char const*);
};
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>

View File

@@ -74,13 +74,130 @@ bool Trinity::Hyperlinks::LinkTags::achievement::StoreTo(AchievementLinkData& va
t.TryConsumeTo(val.Criteria[1]) && t.TryConsumeTo(val.Criteria[2]) && t.TryConsumeTo(val.Criteria[3]) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::apower::StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 artifactPowerId;
if (!(t.TryConsumeTo(artifactPowerId) && t.TryConsumeTo(val.PurchasedRank) && t.TryConsumeTo(val.CurrentRankWithBonus) && t.IsEmpty()))
return false;
if (!sArtifactPowerStore.LookupEntry(artifactPowerId))
return false;
val.ArtifactPower = sDB2Manager.GetArtifactPowerRank(artifactPowerId, std::max<uint8>(val.CurrentRankWithBonus, 1));
if (val.ArtifactPower)
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::azessence::StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 azeriteEssenceId;
if (!t.TryConsumeTo(azeriteEssenceId))
return false;
return (val.Essence = sAzeriteEssenceStore.LookupEntry(azeriteEssenceId)) && t.TryConsumeTo(val.Rank)
&& sDB2Manager.GetAzeriteEssencePower(azeriteEssenceId, val.Rank) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::battlepet::StoreTo(BattlePetLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 battlePetSpeciesId;
if (!t.TryConsumeTo(battlePetSpeciesId))
return false;
return (val.Species = sBattlePetSpeciesStore.LookupEntry(battlePetSpeciesId)) && t.TryConsumeTo(val.Level)
&& t.TryConsumeTo(val.Quality) && val.Quality < MAX_ITEM_QUALITY
&& t.TryConsumeTo(val.MaxHealth) && t.TryConsumeTo(val.Power) && t.TryConsumeTo(val.Speed)
&& t.TryConsumeTo(val.PetGuid) && val.PetGuid.GetHigh() == HighGuid::BattlePet && t.TryConsumeTo(val.DisplayId);
}
bool Trinity::Hyperlinks::LinkTags::conduit::StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 soulbindConduitId, rank;
if (!(t.TryConsumeTo(soulbindConduitId) && t.TryConsumeTo(rank) && t.IsEmpty()))
return false;
return !!(val = sDB2Manager.GetSoulbindConduitRank(soulbindConduitId, rank));
}
bool Trinity::Hyperlinks::LinkTags::currency::StoreTo(CurrencyLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 currencyId;
if (!t.TryConsumeTo(currencyId))
return false;
val.Currency = sCurrencyTypesStore.LookupEntry(currencyId);
if (!val.Currency || !t.TryConsumeTo(val.Quantity) || !t.IsEmpty())
return false;
val.Container = sDB2Manager.GetCurrencyContainerForCurrencyQuantity(currencyId, val.Quantity);
return true;
}
bool Trinity::Hyperlinks::LinkTags::enchant::StoreTo(SpellInfo const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 spellId;
if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
return false;
return (val = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE)) && val->HasAttribute(SPELL_ATTR0_TRADESPELL);
return !!(val = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE)) && val->HasAttribute(SPELL_ATTR0_TRADESPELL);
}
bool Trinity::Hyperlinks::LinkTags::garrfollower::StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 garrFollowerId;
if (!t.TryConsumeTo(garrFollowerId))
return false;
val.Follower = sGarrFollowerStore.LookupEntry(garrFollowerId);
if (!val.Follower || !t.TryConsumeTo(val.Quality) || val.Quality >= MAX_ITEM_QUALITY || !t.TryConsumeTo(val.Level) || !t.TryConsumeTo(val.ItemLevel)
|| !t.TryConsumeTo(val.Abilities[0]) || !t.TryConsumeTo(val.Abilities[1]) || !t.TryConsumeTo(val.Abilities[2]) || !t.TryConsumeTo(val.Abilities[3])
|| !t.TryConsumeTo(val.Traits[0]) || !t.TryConsumeTo(val.Traits[1]) || !t.TryConsumeTo(val.Traits[2]) || !t.TryConsumeTo(val.Traits[3])
|| !t.TryConsumeTo(val.Specialization))
return false;
for (uint32 ability : val.Abilities)
if (ability && !sGarrAbilityStore.LookupEntry(ability))
return false;
for (uint32 trait : val.Traits)
if (trait && !sGarrAbilityStore.LookupEntry(trait))
return false;
if (val.Specialization && !sGarrAbilityStore.LookupEntry(val.Specialization))
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::garrfollowerability::StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 garrAbilityId;
if (!t.TryConsumeTo(garrAbilityId))
return false;
return !!(val = sGarrAbilityStore.LookupEntry(garrAbilityId)) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::garrmission::StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 garrMissionId;
if (!t.TryConsumeTo(garrMissionId))
return false;
return !!(val.Mission = sGarrMissionStore.LookupEntry(garrMissionId)) && t.TryConsumeTo(val.DbID) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::instancelock::StoreTo(InstanceLockLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
if (!t.TryConsumeTo(val.Owner))
return false;
uint32 mapId;
if (!t.TryConsumeTo(mapId))
return false;
return !!(val.Map = sMapStore.LookupEntry(mapId))
&& t.TryConsumeTo(val.Difficulty) && sDB2Manager.GetMapDifficultyData(mapId, Difficulty(val.Difficulty))
&& t.TryConsumeTo(val.CompletedEncountersMask) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const* pos, size_t len)
@@ -143,6 +260,92 @@ bool Trinity::Hyperlinks::LinkTags::item::StoreTo(ItemLinkData& val, char const*
return t.TryConsumeTo(val.Creator) && t.TryConsumeTo(val.UseEnchantId) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::journal::StoreTo(JournalLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 id;
if (!t.TryConsumeTo(val.Type) || !t.TryConsumeTo(id) || !t.TryConsumeTo(val.Difficulty) || !t.IsEmpty())
return false;
switch (JournalLinkData::Types(val.Type))
{
case JournalLinkData::Types::Instance:
{
JournalInstanceEntry const* instance = sJournalInstanceStore.LookupEntry(id);
if (!instance)
return false;
val.ExpectedText = &instance->Name;
break;
}
case JournalLinkData::Types::Encounter:
{
JournalEncounterEntry const* encounter = sJournalEncounterStore.LookupEntry(id);
if (!encounter)
return false;
val.ExpectedText = &encounter->Name;
break;
}
case JournalLinkData::Types::EncounterSection:
{
JournalEncounterSectionEntry const* encounterSection = sJournalEncounterSectionStore.LookupEntry(id);
if (!encounterSection)
return false;
val.ExpectedText = &encounterSection->Title;
break;
}
case JournalLinkData::Types::Tier:
{
JournalTierEntry const* tier = sDB2Manager.GetJournalTier(id);
if (!tier)
return false;
val.ExpectedText = &tier->Name;
break;
}
default:
return false;
}
return true;
}
bool Trinity::Hyperlinks::LinkTags::keystone::StoreTo(KeystoneLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 mapChallengeModeId;
if (!t.TryConsumeTo(val.ItemId) || !t.TryConsumeTo(mapChallengeModeId) || !t.TryConsumeTo(val.Level)
|| !t.TryConsumeTo(val.Affix[0]) || !t.TryConsumeTo(val.Affix[1]) || !t.TryConsumeTo(val.Affix[2]) || !t.TryConsumeTo(val.Affix[3])
|| !t.IsEmpty())
return false;
val.Map = sMapChallengeModeStore.LookupEntry(mapChallengeModeId);
if (!val.Map)
return false;
ItemTemplate const* item = sObjectMgr->GetItemTemplate(val.ItemId);
if (!item || item->GetClass() != ITEM_CLASS_REAGENT || item->GetSubClass() != ITEM_SUBCLASS_KEYSTONE)
return false;
for (uint32 keystoneAffix : val.Affix)
if (keystoneAffix && !sKeystoneAffixStore.LookupEntry(keystoneAffix))
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::mawpower::StoreTo(MawPowerEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 mawPowerId;
if (!t.TryConsumeTo(mawPowerId))
return false;
return !!(val = sMawPowerStore.LookupEntry(mawPowerId)) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::pvptal::StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 pvpTalentId;
if (!(t.TryConsumeTo(pvpTalentId) && t.IsEmpty()))
return false;
if (!(val = sPvpTalentStore.LookupEntry(pvpTalentId)))
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::quest::StoreTo(QuestLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
@@ -185,3 +388,48 @@ bool Trinity::Hyperlinks::LinkTags::trade::StoreTo(TradeskillLinkData& val, char
return false;
return true;
}
bool Trinity::Hyperlinks::LinkTags::transmogappearance::StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 itemModifiedAppearanceId;
if (!t.TryConsumeTo(itemModifiedAppearanceId))
return false;
return !!(val = sItemModifiedAppearanceStore.LookupEntry(itemModifiedAppearanceId)) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::transmogillusion::StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 spellItemEnchantmentId;
if (!t.TryConsumeTo(spellItemEnchantmentId))
return false;
return !!(val = sSpellItemEnchantmentStore.LookupEntry(spellItemEnchantmentId))
&& sDB2Manager.GetTransmogIllusionForEnchantment(spellItemEnchantmentId) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::transmogset::StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 transmogSetId;
if (!t.TryConsumeTo(transmogSetId))
return false;
return !!(val = sTransmogSetStore.LookupEntry(transmogSetId)) && t.IsEmpty();
}
bool Trinity::Hyperlinks::LinkTags::worldmap::StoreTo(WorldMapLinkData& val, char const* pos, size_t len)
{
HyperlinkDataTokenizer t(pos, len);
uint32 uiMapId;
if (!t.TryConsumeTo(uiMapId))
return false;
val.UiMap = sUiMapStore.LookupEntry(uiMapId);
if (!val.UiMap || !t.TryConsumeTo(val.X) || !t.TryConsumeTo(val.Y))
return false;
if (t.IsEmpty())
return true;
val.Z.emplace();
if (!t.TryConsumeTo(*val.Z))
return false;
return t.IsEmpty();
}

View File

@@ -25,6 +25,7 @@
#include "SharedDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "StringFormat.h"
#include "World.h"
#include "advstd.h"
@@ -99,88 +100,26 @@ static bool equal_with_len(char const* str1, char const* str2, size_t len)
return !len && !*str1;
}
template <>
struct LinkValidator<LinkTags::achievement>
static bool IsCreatureNameValid(uint32 creatureId, char const* pos, size_t len)
{
static bool IsTextValid(AchievementLinkData const& data, char const* pos, size_t len)
if (CreatureTemplate const* creatureTemplate = sObjectMgr->GetCreatureTemplate(creatureId))
{
if (!len)
return false;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
if (equal_with_len(data.Achievement->Title[i], pos, len))
return true;
return false;
}
static bool IsColorValid(AchievementLinkData const&, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_ACHIEVEMENT;
}
};
template <>
struct LinkValidator<LinkTags::item>
{
static bool IsTextValid(ItemLinkData const& data, char const* pos, size_t len)
{
LocalizedString const* suffixStrings = nullptr;
if (!(data.Item->GetFlags3() & ITEM_FLAG3_HIDE_NAME_SUFFIX) && data.Suffix)
suffixStrings = &data.Suffix->Description;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
{
std::string name = data.Item->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))
return true;
}
else if (equal_with_len(name.c_str(), pos, len))
return true;
}
return false;
}
static bool IsColorValid(ItemLinkData const& data, HyperlinkColor c)
{
return c == ItemQualityColors[data.Quality];
}
};
template <>
struct LinkValidator<LinkTags::quest>
{
static bool IsTextValid(QuestLinkData const& data, char const* pos, size_t len)
{
QuestTemplateLocale const* locale = sObjectMgr->GetQuestLocale(data.Quest->GetQuestId());
CreatureLocale const* locale = sObjectMgr->GetCreatureLocale(creatureId);
if (!locale)
return equal_with_len(data.Quest->GetLogTitle().c_str(), pos, len);
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) ? data.Quest->GetLogTitle() : locale->LogTitle[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;
}
static bool IsColorValid(QuestLinkData const&, HyperlinkColor c)
{
for (uint8 i = 0; i < MAX_QUEST_DIFFICULTY; ++i)
if (c == QuestDifficultyColors[i])
return true;
return false;
}
};
return false;
}
template <>
struct LinkValidator<LinkTags::spell>
@@ -204,6 +143,106 @@ struct LinkValidator<LinkTags::spell>
}
};
template <>
struct LinkValidator<LinkTags::achievement>
{
static bool IsTextValid(AchievementLinkData const& data, char const* pos, size_t len)
{
if (!len)
return false;
for (LocaleConstant i = LOCALE_enUS; i < TOTAL_LOCALES; i = LocaleConstant(i + 1))
if (equal_with_len(data.Achievement->Title[i], pos, len))
return true;
return false;
}
static bool IsColorValid(AchievementLinkData const&, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_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>
{
@@ -237,12 +276,239 @@ struct LinkValidator<LinkTags::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)
{
LocalizedString const* suffixStrings = nullptr;
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 = 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))
return true;
}
else if (equal_with_len(name.c_str(), pos, len))
return true;
}
return false;
}
static bool IsColorValid(ItemLinkData const& data, HyperlinkColor c)
{
return c == ItemQualityColors[data.Quality];
}
};
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)
{
QuestTemplateLocale const* locale = sObjectMgr->GetQuestLocale(data.Quest->GetQuestId());
if (!locale)
return equal_with_len(data.Quest->GetLogTitle().c_str(), pos, len);
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
{
std::string const& name = (i == DEFAULT_LOCALE) ? data.Quest->GetLogTitle() : locale->LogTitle[i];
if (name.empty())
continue;
if (equal_with_len(name.c_str(), pos, len))
return true;
}
return false;
}
static bool IsColorValid(QuestLinkData const&, HyperlinkColor c)
{
for (uint8 i = 0; i < MAX_QUEST_DIFFICULTY; ++i)
if (c == QuestDifficultyColors[i])
return true;
return false;
}
};
template <>
struct LinkValidator<LinkTags::mawpower>
{
static bool IsTextValid(MawPowerEntry 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(MawPowerEntry const*, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_SPELL;
}
};
template <>
struct LinkValidator<LinkTags::outfit>
{
static bool IsTextValid(std::string const&, char const*, size_t)
{
return true;
}
static bool IsColorValid(std::string const&, HyperlinkColor c)
{
return c == CHAT_LINK_COLOR_TRANSMOG;
}
};
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(PvpTalentEntry const*, HyperlinkColor c)
{
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;
}

View File

@@ -20,17 +20,36 @@
#include "advstd.h"
#include "ObjectGuid.h"
#include "Optional.h"
#include <string>
#include <utility>
struct AchievementEntry;
struct ArtifactPowerRankEntry;
struct AzeriteEssenceEntry;
struct BattlePetSpeciesEntry;
struct CurrencyContainerEntry;
struct CurrencyTypesEntry;
struct GarrAbilityEntry;
struct GarrFollowerEntry;
struct GarrMissionEntry;
struct GlyphPropertiesEntry;
struct ItemModifiedAppearanceEntry;
struct ItemNameDescriptionEntry;
struct ItemTemplate;
struct LocalizedString;
struct MapEntry;
struct MapChallengeModeEntry;
struct MawPowerEntry;
struct PvpTalentEntry;
class Quest;
struct SkillLineEntry;
struct SoulbindConduitRankEntry;
class SpellInfo;
struct SpellItemEnchantmentEntry;
struct TalentEntry;
struct TransmogSetEntry;
struct UiMapEntry;
namespace Trinity
{
@@ -48,6 +67,64 @@ struct AchievementLinkData
uint32 Criteria[4];
};
struct ArtifactPowerLinkData
{
ArtifactPowerRankEntry const* ArtifactPower;
uint8 PurchasedRank;
uint8 CurrentRankWithBonus;
};
struct AzeriteEssenceLinkData
{
AzeriteEssenceEntry const* Essence;
uint8 Rank;
};
struct BattlePetLinkData
{
BattlePetSpeciesEntry const* Species;
uint8 Level;
uint8 Quality;
uint32 MaxHealth;
uint32 Power;
uint32 Speed;
ObjectGuid PetGuid;
uint32 DisplayId;
};
struct CurrencyLinkData
{
CurrencyTypesEntry const* Currency;
int32 Quantity;
CurrencyContainerEntry const* Container;
};
struct GarrisonFollowerLinkData
{
GarrFollowerEntry const* Follower;
uint32 Quality;
uint32 Level;
uint32 ItemLevel;
uint32 Abilities[4];
uint32 Traits[4];
uint32 Specialization;
};
struct GarrisonMissionLinkData
{
GarrMissionEntry const* Mission;
uint64 DbID;
};
struct InstanceLockLinkData
{
ObjectGuid Owner;
MapEntry const* Map;
uint32 Difficulty;
uint32 CompletedEncountersMask;
};
struct ItemLinkData
{
ItemTemplate const* Item;
@@ -73,6 +150,29 @@ struct ItemLinkData
ItemNameDescriptionEntry const* Suffix;
};
struct JournalLinkData
{
enum class Types : uint8
{
Instance = 0,
Encounter = 1,
EncounterSection = 2,
Tier = 3
};
uint8 Type;
LocalizedString const* ExpectedText;
uint32 Difficulty;
};
struct KeystoneLinkData
{
uint32 ItemId;
MapChallengeModeEntry const* Map;
uint32 Level;
uint32 Affix[4];
};
struct QuestLinkData
{
::Quest const* Quest;
@@ -92,6 +192,14 @@ struct TradeskillLinkData
SkillLineEntry const* Skill;
};
struct WorldMapLinkData
{
UiMapEntry const* UiMap;
uint32 X;
uint32 Y;
Optional<uint32> Z;
};
namespace LinkTags {
/************************** LINK TAGS ***************************************************\
@@ -138,6 +246,7 @@ namespace LinkTags {
};
#define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = type; static constexpr char const* tag() { return #ltag; } }
// custom formats
make_base_tag(area, uint32);
make_base_tag(areatrigger, uint32);
make_base_tag(creature, ObjectGuid::LowType);
@@ -151,6 +260,9 @@ namespace LinkTags {
make_base_tag(taxinode, uint32);
make_base_tag(tele, uint32);
make_base_tag(title, uint32);
// client format
make_base_tag(outfit, std::string const&); // some sort of weird base91 derived encoding
#undef make_base_tag
struct TC_GAME_API achievement
@@ -160,6 +272,41 @@ namespace LinkTags {
static bool StoreTo(AchievementLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API apower
{
using value_type = ArtifactPowerLinkData const&;
static constexpr char const* tag() { return "apower"; }
static bool StoreTo(ArtifactPowerLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API azessence
{
using value_type = AzeriteEssenceLinkData const&;
static constexpr char const* tag() { return "azessence"; }
static bool StoreTo(AzeriteEssenceLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API battlepet
{
using value_type = BattlePetLinkData const&;
static constexpr char const* tag() { return "battlepet"; }
static bool StoreTo(BattlePetLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API conduit
{
using value_type = SoulbindConduitRankEntry const*&;
static constexpr char const* tag() { return "conduit"; }
static bool StoreTo(SoulbindConduitRankEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API currency
{
using value_type = CurrencyLinkData const&;
static constexpr char const* tag() { return "currency"; }
static bool StoreTo(CurrencyLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API enchant
{
using value_type = SpellInfo const*;
@@ -167,6 +314,34 @@ namespace LinkTags {
static bool StoreTo(SpellInfo const*& val, char const* pos, size_t len);
};
struct TC_GAME_API garrfollower
{
using value_type = GarrisonFollowerLinkData const&;
static constexpr char const* tag() { return "garrfollower"; }
static bool StoreTo(GarrisonFollowerLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API garrfollowerability
{
using value_type = GarrAbilityEntry const*&;
static constexpr char const* tag() { return "garrfollowerability"; }
static bool StoreTo(GarrAbilityEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API garrmission
{
using value_type = GarrisonMissionLinkData const&;
static constexpr char const* tag() { return "garrmission"; }
static bool StoreTo(GarrisonMissionLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API instancelock
{
using value_type = InstanceLockLinkData const&;
static constexpr char const* tag() { return "instancelock"; }
static bool StoreTo(InstanceLockLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API item
{
using value_type = ItemLinkData const&;
@@ -174,6 +349,34 @@ namespace LinkTags {
static bool StoreTo(ItemLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API journal
{
using value_type = JournalLinkData const&;
static constexpr char const* tag() { return "journal"; }
static bool StoreTo(JournalLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API keystone
{
using value_type = KeystoneLinkData const&;
static constexpr char const* tag() { return "keystone"; }
static bool StoreTo(KeystoneLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API mawpower
{
using value_type = MawPowerEntry const*&;
static constexpr char const* tag() { return "mawpower"; }
static bool StoreTo(MawPowerEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API pvptal
{
using value_type = PvpTalentEntry const*&;
static constexpr char const* tag() { return "pvptal"; }
static bool StoreTo(PvpTalentEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API quest
{
using value_type = QuestLinkData const&;
@@ -201,6 +404,34 @@ namespace LinkTags {
static constexpr char const* tag() { return "trade"; }
static bool StoreTo(TradeskillLinkData& val, char const* pos, size_t len);
};
struct TC_GAME_API transmogappearance
{
using value_type = ItemModifiedAppearanceEntry const*&;
static constexpr char const* tag() { return "transmogappearance"; }
static bool StoreTo(ItemModifiedAppearanceEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API transmogillusion
{
using value_type = SpellItemEnchantmentEntry const*&;
static constexpr char const* tag() { return "transmogillusion"; }
static bool StoreTo(SpellItemEnchantmentEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API transmogset
{
using value_type = TransmogSetEntry const*&;
static constexpr char const* tag() { return "transmogset"; }
static bool StoreTo(TransmogSetEntry const*& val, char const* pos, size_t len);
};
struct TC_GAME_API worldmap
{
using value_type = WorldMapLinkData const&;
static constexpr char const* tag() { return "worldmap"; }
static bool StoreTo(WorldMapLinkData& val, char const* pos, size_t len);
};
}
struct HyperlinkColor

View File

@@ -5649,6 +5649,11 @@ enum ChatLinkColors : uint32
CHAT_LINK_COLOR_SPELL = 0xff71d5ff, // bright blue
CHAT_LINK_COLOR_ENCHANT = 0xffffd000, // orange
CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00,
CHAT_LINK_COLOR_ARTIFACT_POWER = 0xff71d5ff,
CHAT_LINK_COLOR_GARR_ABILITY = 0xff4e96f7,
CHAT_LINK_COLOR_INSTANCE_LOCK = 0xffff8000,
CHAT_LINK_COLOR_JOURNAL = 0xff66bbff,
CHAT_LINK_COLOR_TRANSMOG = 0xffff80ff,
};
// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask