Skip to content

Commit 7c2317a

Browse files
committed
Suppress aura cache fallback when descriptor is authoritative
Stuck auras (pfUI issue #20 part 2): a no-duration debuff (Ghost/Spirit in battlegrounds) stayed visible after the unit resurrected. It can only come from the Aura::Source cache fallback — the descriptor path re-reads the live aura array each call. The aura is cached with expirationMs == 0 (infinite), so OnWorldTick never expires it, and its real removal came via a bulk object drop (death/leave-visibility, then resurrect) rather than a per-aura removal, so OnAuraRemoved never fired to evict it. The infinite entry lingered and the fallback resurrected it on the now-alive unit. The fallback only exists for rogue stealth and party range fluctuation, which both clear the unit's entire aura array at once. Gate both fallback entry points on DescriptorHasVisibleAura: if the descriptor exposes any live aura it's authoritative, so a cache-only aura is genuinely gone. Stealth/range recovery is unaffected — those produce a wholesale-empty aura array, so the fallback still fires there exactly as before.
1 parent b764b14 commit 7c2317a

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

src/aura/Data.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,25 @@ void PushFromCache(void *L, const uint8_t *unit,
492492
c.casterGuid);
493493
}
494494

495+
// True if `unit`'s descriptor currently exposes any visible aura in either
496+
// range. The cache fallback is only trustworthy when the descriptor has been
497+
// wholesale-cleared: the two states it exists for — rogue stealth and party
498+
// range fluctuation — clear the *entire* aura array at once. If the unit shows
499+
// even one live aura, the descriptor is authoritative, so a cache entry the
500+
// descriptor lacks is a genuinely-gone aura we never saw removed (e.g. a
501+
// no-duration debuff — Ghost/Spirit — on a unit that died and resurrected out
502+
// of our visibility, so no OnAuraRemoved fired and its infinite entry never
503+
// expired). Surfacing it then produces a stuck phantom.
504+
bool DescriptorHasVisibleAura(const uint8_t *unit) {
505+
if (unit == nullptr)
506+
return false;
507+
for (int slot = 0; slot < Offsets::UNIT_AURA_TOTAL; ++slot) {
508+
if (IsSlotPopulated(unit, slot))
509+
return true;
510+
}
511+
return false;
512+
}
513+
495514
// Counts populated descriptor slots matching `filter` (and `playerOnly`).
496515
int CountSlots(const uint8_t *unit, Filter filter, bool playerOnly) {
497516
if (unit == nullptr)
@@ -540,6 +559,8 @@ bool PushNthCacheFallback(void *L, const uint8_t *unit, int oneBasedIndex,
540559
Filter filter, bool playerOnly) {
541560
if (unit == nullptr)
542561
return false;
562+
if (DescriptorHasVisibleAura(unit))
563+
return false; // descriptor authoritative — see DescriptorHasVisibleAura
543564
// The descriptor held `descCount` matches; the caller already found fewer
544565
// than `oneBasedIndex` there, so the cache supplies index
545566
// `oneBasedIndex - descCount` (1-based) of the entries it didn't.
@@ -568,6 +589,8 @@ void AppendCacheFallbacks(void *L, const uint8_t *unit, Filter filter,
568589
bool playerOnly, int outerIdx, int &nextKey) {
569590
if (unit == nullptr)
570591
return;
592+
if (DescriptorHasVisibleAura(unit))
593+
return; // descriptor authoritative — see DescriptorHasVisibleAura
571594
const uint64_t guid = UnitGuid(unit);
572595
if (guid == 0)
573596
return;

0 commit comments

Comments
 (0)