From 6ffe41dd59199ff243f8a4ba2e61500374157e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Borz=C3=AC?= Date: Wed, 11 Mar 2026 00:50:32 +0100 Subject: [PATCH] refactor(Core/Misc): port gossip validation, StringFormat APIs, and spell attribute naming from TC (#24789) Co-authored-by: Rochet2 Co-authored-by: Shauren Co-authored-by: tobmaps --- src/common/Utilities/StringFormat.h | 51 +++++++++++++++++++ src/server/game/AI/CoreAI/PetAI.cpp | 2 +- src/server/game/Entities/Unit/Unit.cpp | 2 +- src/server/game/Handlers/MiscHandler.cpp | 6 +++ src/server/game/Spells/Auras/SpellAuras.h | 6 +-- .../game/Spells/SpellInfoCorrections.cpp | 2 +- src/server/shared/SharedDefines.h | 2 +- src/server/shared/enuminfo_SharedDefines.cpp | 6 +-- 8 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/common/Utilities/StringFormat.h b/src/common/Utilities/StringFormat.h index 8befb6ffb..05e89ad06 100644 --- a/src/common/Utilities/StringFormat.h +++ b/src/common/Utilities/StringFormat.h @@ -29,6 +29,16 @@ namespace Acore template using FormatString = fmt::format_string; + using FormatStringView = fmt::string_view; + + using FormatArgs = fmt::format_args; + + template + constexpr auto MakeFormatArgs(Args&&... args) + { + return fmt::make_format_args(args...); + } + /// Default AC string format function. template inline std::string StringFormat(FormatString fmt, Args&&... args) @@ -43,6 +53,47 @@ namespace Acore } } + /// Format directly to an output iterator. + template + inline OutputIt StringFormatTo(OutputIt out, FormatString fmt, Args&&... args) + { + try + { + return fmt::format_to(out, fmt, std::forward(args)...); + } + catch (std::exception const& e) + { + return fmt::format_to(out, "Wrong format occurred ({}). Fmt string: '{}'", e.what(), fmt.get()); + } + } + + /// Format with pre-built format args. + inline std::string StringVFormat(FormatStringView fmt, FormatArgs args) + { + try + { + return fmt::vformat(fmt, args); + } + catch (std::exception const& e) + { + return fmt::format("Wrong format occurred ({}). Fmt string: '{}'", e.what(), fmt); + } + } + + /// Format with pre-built format args directly to an output iterator. + template + inline OutputIt StringVFormatTo(OutputIt out, FormatStringView fmt, FormatArgs args) + { + try + { + return fmt::vformat_to(out, fmt, args); + } + catch (std::exception const& e) + { + return fmt::format_to(out, "Wrong format occurred ({}). Fmt string: '{}'", e.what(), fmt); + } + } + /// Returns true if the given char pointer is null. inline bool IsFormatEmptyOrNull(char const* fmt) { diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index bc57e4d2a..236331ac8 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -733,7 +733,7 @@ bool PetAI::CanAttack(Unit* target, SpellInfo const* spellInfo) return me->GetCharmInfo()->IsCommandAttack(); // CC - mobs under crowd control can be attacked if owner commanded - if (target->HasBreakableByDamageCrowdControlAura() && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR4_REACTIVE_DAMAGE_PROC))) + if (target->HasBreakableByDamageCrowdControlAura() && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS))) return me->GetCharmInfo()->IsCommandAttack(); // Returning - pets ignore attacks only if owner clicked follow diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index aea815009..089b9cd45 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -978,7 +978,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage if (attacker && damagetype != DOT && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MELEE && !(spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_HOLY)) attacker->DealDamageShieldDamage(victim); - if (!spellProto->HasAttribute(SPELL_ATTR4_REACTIVE_DAMAGE_PROC)) + if (!spellProto->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)) victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto->Id); } else diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 4c3ba5502..c90c3ff0e 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -96,6 +96,12 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recv_data) recv_data >> guid >> menuId >> gossipListId; + if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(gossipListId)) + { + recv_data.rfinish(); + return; + } + if (_player->PlayerTalkClass->IsGossipOptionCoded(gossipListId)) recv_data >> code; diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 5d8afc9a4..87b77ff25 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -64,9 +64,9 @@ public: uint8 GetSlot() const { return _slot; } uint8 GetFlags() const { return _flags; } uint8 GetEffectMask() const { return _flags & (AFLAG_EFF_INDEX_0 | AFLAG_EFF_INDEX_1 | AFLAG_EFF_INDEX_2); } - bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return _flags & (1 << effect); } - bool IsPositive() const { return _flags & AFLAG_POSITIVE; } - bool IsSelfcasted() const { return _flags & AFLAG_CASTER; } + bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return (_flags & (1 << effect)) != 0; } + bool IsPositive() const { return (_flags & AFLAG_POSITIVE) != 0; } + bool IsSelfcasted() const { return (_flags & AFLAG_CASTER) != 0; } uint8 GetEffectsToApply() const { return _effectsToApply; } void SetRemoveMode(AuraRemoveMode mode) { _removeMode = mode; } diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 4a693d7b3..9f80a266f 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -1063,7 +1063,7 @@ void SpellMgr::LoadSpellInfoCorrections() ApplySpellFix({ 44461, 55361, 55362 }, [](SpellInfo* spellInfo) { spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPPRESS_TARGET_PROCS; - spellInfo->AttributesEx4 |= SPELL_ATTR4_REACTIVE_DAMAGE_PROC; + spellInfo->AttributesEx4 |= SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS; }); // Evocation diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 8cfa60525..87b6c6ab3 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -529,7 +529,7 @@ enum SpellAttr4 : uint32 SPELL_ATTR4_NO_PARTIAL_IMMUNITY = 0x00000800, // TITLE Unknown attribute 11@Attr4 SPELL_ATTR4_AURA_IS_BUFF = 0x00001000, // TITLE Unknown attribute 12@Attr4 SPELL_ATTR4_DO_NOT_LOG_CASTER = 0x00002000, // TITLE Unknown attribute 13@Attr4 - SPELL_ATTR4_REACTIVE_DAMAGE_PROC = 0x00004000, // TITLE Damage does not break auras + SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS = 0x00004000, // TITLE Damage does not break auras SPELL_ATTR4_NOT_IN_SPELLBOOK = 0x00008000, // TITLE Unknown attribute 15@Attr4 SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND = 0x00010000, // TITLE Not usable in arena DESCRIPTION Makes spell unusable despite CD <= 10min SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS = 0x00020000, // TITLE Usable in arena DESCRIPTION Makes spell usable despite CD > 10min diff --git a/src/server/shared/enuminfo_SharedDefines.cpp b/src/server/shared/enuminfo_SharedDefines.cpp index d8e6f7634..bb68abfb0 100644 --- a/src/server/shared/enuminfo_SharedDefines.cpp +++ b/src/server/shared/enuminfo_SharedDefines.cpp @@ -688,7 +688,7 @@ AC_API_EXPORT EnumText EnumUtils::ToString(SpellAttr4 value) case SPELL_ATTR4_NO_PARTIAL_IMMUNITY: return { "SPELL_ATTR4_NO_PARTIAL_IMMUNITY", "Unknown attribute 11@Attr4", "" }; case SPELL_ATTR4_AURA_IS_BUFF: return { "SPELL_ATTR4_AURA_IS_BUFF", "Unknown attribute 12@Attr4", "" }; case SPELL_ATTR4_DO_NOT_LOG_CASTER: return { "SPELL_ATTR4_DO_NOT_LOG_CASTER", "Unknown attribute 13@Attr4", "" }; - case SPELL_ATTR4_REACTIVE_DAMAGE_PROC: return { "SPELL_ATTR4_REACTIVE_DAMAGE_PROC", "Damage does not break auras", "" }; + case SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS: return { "SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS", "Damage does not break auras", "" }; case SPELL_ATTR4_NOT_IN_SPELLBOOK: return { "SPELL_ATTR4_NOT_IN_SPELLBOOK", "Unknown attribute 15@Attr4", "" }; case SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND: return { "SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND", "Not usable in arena", "Makes spell unusable despite CD <= 10min" }; case SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS: return { "SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS", "Usable in arena", "Makes spell usable despite CD > 10min" }; @@ -732,7 +732,7 @@ AC_API_EXPORT SpellAttr4 EnumUtils::FromIndex(std::size_t index) case 11: return SPELL_ATTR4_NO_PARTIAL_IMMUNITY; case 12: return SPELL_ATTR4_AURA_IS_BUFF; case 13: return SPELL_ATTR4_DO_NOT_LOG_CASTER; - case 14: return SPELL_ATTR4_REACTIVE_DAMAGE_PROC; + case 14: return SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS; case 15: return SPELL_ATTR4_NOT_IN_SPELLBOOK; case 16: return SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND; case 17: return SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS; @@ -773,7 +773,7 @@ AC_API_EXPORT std::size_t EnumUtils::ToIndex(SpellAttr4 value) case SPELL_ATTR4_NO_PARTIAL_IMMUNITY: return 11; case SPELL_ATTR4_AURA_IS_BUFF: return 12; case SPELL_ATTR4_DO_NOT_LOG_CASTER: return 13; - case SPELL_ATTR4_REACTIVE_DAMAGE_PROC: return 14; + case SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS: return 14; case SPELL_ATTR4_NOT_IN_SPELLBOOK: return 15; case SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND: return 16; case SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS: return 17;