Paragon: server-authoritative CP/rune sync + cascade-spell revoke hardening
mod-paragon Paragon_Essence.cpp: - Broaden SkillLinesLinkedToSpell: collect every SkillLineAbility row for an anchor spell regardless of AcquireMethod, so anchor spells whose primary SLA uses AcquireMethod 0 (e.g. Blood Strike) correctly identify their skill lines and let the dependent classifier do its job. - IsSpellSkillLineCascadeDependent / RevokeUnwantedCascadeSpellsForPlayer use the broadened helper. HandleCommit calls the post-purchase sweep immediately so the spellbook never carries lingering cascade dependents (Blood Presence / Forceful Deflection / Death Coil / Death Grip). - New character_paragon_panel_spell_revoked table tracks which active dependents we've revoked per (guid, parent) so OnPlayerLogin can re-revoke them after AC's _LoadSkills -> learnSkillRewardedSpells silently re-grants them. - OnPlayerLogin opens the client SILENCE window via SendSilenceOpenForCommit with an empty allow list and intentionally omits the matching SendSilenceClose: the chat frame buffers CHAT_MSG_SYSTEM during the loading screen and only flushes after PLAYER_ENTERING_WORLD, so a paired CLOSE would shut the filter before the buffered "you have unlearned X" toasts hit it. The addon's 8s fail-open closes the window after the flush. - New `.paragon hat` chat command for diagnosing Honor Among Thieves triggers (talent rank, learned spell, applied aura, proc table entry). mod-paragon Paragon_SC.cpp: - OnPlayerUpdate pushes server-authoritative combo points to the client via PARAA "R CP <n>" whenever the count changes. The client-side ComboFrame Paragon simulator listens for this and updates the target frame, fixing HAT-generated CP not displaying (HAT's trigger casts with a null target, which the combat-log inference path can't see). - OnPlayerUpdate also pushes "R RUNES <cd0..cd5>" (ms remaining per rune slot) on rune mask changes, so the client RuneFrame simulator stays in lock-step with Spell::TakeRunePower instead of drifting through combat-log latency. mod-paragon SQL: - New updates/2026_05_09_00.sql migration creates character_paragon_panel_spell_revoked for AC's auto-DBUpdater so a fresh checkout can stand up an existing characters DB without manual intervention. Matching CREATE TABLE IF NOT EXISTS in base/character_paragon_panel_learned.sql for fresh installs. mod-paragon conf: - New Paragon.Diag.PanelLearn flag traces every PanelLearnSpellChain commit (chain ids, before/after spell-map sizes, side-spell classification) for diagnosing "spell reappears on relog" bugs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,15 +5,20 @@
|
||||
* so Paragon can reuse other classes' mechanics in narrowly scoped contexts.
|
||||
*/
|
||||
|
||||
#include "Chat.h"
|
||||
#include "Config.h"
|
||||
#include "GameTime.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "UnitDefines.h"
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "GameTime.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class Paragon_PlayerScript : public PlayerScript
|
||||
@@ -184,6 +189,45 @@ public:
|
||||
{
|
||||
player->ResyncRunes(MAX_RUNES);
|
||||
st.lastReadyMask = readyMask;
|
||||
|
||||
// Authoritative rune CD pump (PARAA "R RUNES cd0 cd1 ... cd5",
|
||||
// ms remaining per slot, 0 = ready). The 3.3.5 client engine
|
||||
// class-gates SMSG_RESYNC_RUNES / SMSG_SPELL_GO RUNE_LIST to DK,
|
||||
// so the Paragon RuneFrame sim drives the visual entirely off
|
||||
// COMBAT_LOG_EVENT_UNFILTERED:SPELL_CAST_SUCCESS. The combat log
|
||||
// arrives ~100–200ms after the server already started the
|
||||
// cooldown, so the client's local timer trails the server. When
|
||||
// the user spams a rune spell, the server's slot refreshes
|
||||
// first, accepts the next cast, but the client UI still shows
|
||||
// CD remaining → "leak-through" past a greyed icon. Pushing the
|
||||
// actual remaining ms on every mask transition keeps the
|
||||
// visual locked to server state.
|
||||
std::string body = "R RUNES";
|
||||
for (uint8 i = 0; i < MAX_RUNES; ++i)
|
||||
body += " " + std::to_string(player->GetRuneCooldown(i));
|
||||
std::string const payload = std::string(kParagonAddonPrefix) + "\t" + body;
|
||||
WorldPacket runePkt;
|
||||
ChatHandler::BuildChatPacket(runePkt, CHAT_MSG_WHISPER, LANG_ADDON, player, player, payload);
|
||||
player->SendDirectMessage(&runePkt);
|
||||
}
|
||||
|
||||
// Combo point pump: the 3.3.5 client engine class-gates SMSG_UPDATE_COMBO_POINTS
|
||||
// to rogue / druid, so the Paragon UI sim never sees CP changes from
|
||||
// Honor Among Thieves / Mutilate / etc. via either the engine state or
|
||||
// the client-side combat-log inference (HAT's 51699 trigger fires with a
|
||||
// null target and doesn't always emit SPELL_CAST_SUCCESS in the log).
|
||||
// Push the count over PARAA whenever it changes; the addon's combo
|
||||
// simulator listens for "R CP <n>" and overwrites paragonCP, so the
|
||||
// ComboFrame on the target frame paints reliably.
|
||||
int8 const cp = player->GetComboPoints();
|
||||
if (cp != st.lastCp)
|
||||
{
|
||||
std::string const payload = std::string(kParagonAddonPrefix) + "\t"
|
||||
+ fmt::format("R CP {}", int32(cp));
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, player, player, payload);
|
||||
player->SendDirectMessage(&data);
|
||||
st.lastCp = cp;
|
||||
}
|
||||
|
||||
if (!sConfigMgr->GetOption<bool>("Paragon.Diag.RuneTrace", false))
|
||||
@@ -214,8 +258,11 @@ private:
|
||||
struct ParagonRuneSyncState
|
||||
{
|
||||
uint8 lastReadyMask{0xFFu}; // sentinel: no prior snapshot
|
||||
int8 lastCp{-1}; // sentinel: no prior snapshot
|
||||
};
|
||||
|
||||
static constexpr char const* kParagonAddonPrefix = "PARAA";
|
||||
|
||||
static std::unordered_map<ObjectGuid, ParagonRuneSyncState> runeSyncByGuid;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user