mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-21 23:26:56 +00:00
Make nameplate castbars event-driven; unthrottle target bar
Previously every visible plate polled C_Spell.UnitCastingInfo each throttled tick just to detect casts. Now cast state is event-driven: - SPELL_START_OTHER (nampower) stamps a per-GUID castState cache (spellId, timing, channel flag from spellType); SPELL_FAILED_OTHER clears it; normal completion expires at endTime. GetCastInfo just reads the cache, so all call sites (castbar update, non-target detection, casting name-color) stop polling. - plateByGuid routes events to plates in O(1) and bounds the cache to on-screen casters; NAME_PLATE_UNIT_ADDED seeds an already-casting unit with one poll. - The vestigial nameplate.castUpdate flag now fires on cast start to bypass the throttle for an immediate bar. With the poll gone, per-tick work is a cache read + SetValue, so the dedicated target castbar frame now runs unthrottled (every frame) for the smoothest sweep, and the non-target nameplates_castbar default rises 50 -> 100 FPS. Known limits: other-unit cast pushback isn't reported by nampower, and a mob-cancelled channel with no fail event lingers until endTime.
This commit is contained in:
@@ -160,7 +160,7 @@ libthrottle:SetScript("OnEvent", function()
|
||||
-- Set defaults for custom fields if missing
|
||||
if not _G.pfUI_throttle.nameplates_target_custom then _G.pfUI_throttle.nameplates_target_custom = "50" end
|
||||
if not _G.pfUI_throttle.nameplates_custom then _G.pfUI_throttle.nameplates_custom = "10" end
|
||||
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "50" end
|
||||
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
|
||||
if not _G.pfUI_throttle.nameplates_mass_custom then _G.pfUI_throttle.nameplates_mass_custom = "7" end
|
||||
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
|
||||
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
|
||||
|
||||
+84
-15
@@ -61,11 +61,19 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
|
||||
local raidGuidCache = {} -- guid -> name (rebuilt on RAID_ROSTER_UPDATE/PARTY_MEMBERS_CHANGED)
|
||||
|
||||
-- Resolve a unit token to its cast/channel info via C_Spell. Returns a
|
||||
-- compact struct (spellName / icon / startTime / endTime / duration /
|
||||
-- isChannel) or nil when the unit isn't casting. Callers already hold the
|
||||
-- nameplate token, so there's no GUID->token round-trip.
|
||||
local function GetCastInfo(unit)
|
||||
-- Per-GUID cast state, populated by nampower's SPELL_START_OTHER events and
|
||||
-- cleared on SPELL_FAILED_OTHER / plate removal / expiry. This replaces the
|
||||
-- old per-tick C_Spell poll on every visible plate: cast detection is now
|
||||
-- event driven, and GetCastInfo just reads this cache.
|
||||
local castState = {}
|
||||
-- guid -> nameplate, maintained on NAME_PLATE_UNIT_ADDED/_REMOVED so a cast
|
||||
-- event can find its plate in O(1) and only cache casts we actually show.
|
||||
local plateByGuid = {}
|
||||
|
||||
-- One-shot C_Spell poll. Only used to seed a plate that spawns while its
|
||||
-- unit is already mid-cast (its SPELL_START_OTHER fired before the plate
|
||||
-- existed). Never called per frame.
|
||||
local function PollCastInfo(unit)
|
||||
if not unit then return nil end
|
||||
local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit)
|
||||
local isChannel
|
||||
@@ -84,6 +92,20 @@ pfUI:RegisterModule("nameplates", function ()
|
||||
isChannel = isChannel,
|
||||
}
|
||||
end
|
||||
|
||||
-- Read a unit's current cast from the event-driven cache (keyed by GUID).
|
||||
-- Returns the cached struct while the cast is still active, else nil (and
|
||||
-- prunes the expired entry). Same struct shape and callers as before, minus
|
||||
-- the per-tick poll.
|
||||
local function GetCastInfo(unit)
|
||||
if not unit then return nil end
|
||||
local guid = UnitGUID(unit)
|
||||
if not guid then return nil end
|
||||
local info = castState[guid]
|
||||
if info and info.endTime > GetTime() then return info end
|
||||
if info then castState[guid] = nil end
|
||||
return nil
|
||||
end
|
||||
|
||||
local debuffCache = {} -- guid -> { [spellID] = { start, duration } }
|
||||
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
|
||||
@@ -453,6 +475,11 @@ nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
||||
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
||||
nameplates:RegisterEvent("UNIT_AURA")
|
||||
nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
-- nampower cast lifecycle for other units (gated by NP_EnableSpell{Start,Go}
|
||||
-- Events, enabled by libdebuff). Drives castbars event-first instead of
|
||||
-- polling C_Spell on every plate each tick. Mirrors castbar.lua's target bar.
|
||||
nameplates:RegisterEvent("SPELL_START_OTHER")
|
||||
nameplates:RegisterEvent("SPELL_FAILED_OTHER")
|
||||
|
||||
nameplates:SetScript("OnEvent", function()
|
||||
-- Stop event handling during logout to prevent crash 132
|
||||
@@ -529,8 +556,16 @@ nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
-- token itself for token-based UnitX reads (stable per plate lifetime).
|
||||
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
|
||||
if plate and plate.nameplate then
|
||||
plate.nameplate.cachedGuid = UnitGUID(arg1)
|
||||
local guid = UnitGUID(arg1)
|
||||
plate.nameplate.cachedGuid = guid
|
||||
plate.nameplate.unit = arg1
|
||||
if guid then
|
||||
plateByGuid[guid] = plate.nameplate
|
||||
-- Seed: the unit may already be mid-cast (its SPELL_START_OTHER fired
|
||||
-- before this plate existed). One poll here catches that; ongoing
|
||||
-- casts arrive via the event.
|
||||
castState[guid] = PollCastInfo(arg1)
|
||||
end
|
||||
nameplates.OnShow(plate)
|
||||
end
|
||||
visiblePlateCount = visiblePlateCount + 1
|
||||
@@ -544,6 +579,8 @@ nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
if debuffCache[guid] then debuffCache[guid] = nil end
|
||||
if threatMemory[guid] then threatMemory[guid] = nil end
|
||||
if combatColorCache[guid] then combatColorCache[guid] = nil end
|
||||
if castState[guid] then castState[guid] = nil end
|
||||
if plateByGuid[guid] then plateByGuid[guid] = nil end
|
||||
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
|
||||
if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then
|
||||
plate.nameplate.cachedGuid = nil
|
||||
@@ -563,6 +600,41 @@ nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "SPELL_START_OTHER" then
|
||||
-- nampower: arg2=spellId, arg3=casterGuid, arg6=castTime(ms),
|
||||
-- arg7=channel duration(ms, 0 if not a channel), arg8=spellType
|
||||
-- (1 = channel). Cache the cast only for a unit we have a plate for, so
|
||||
-- the table stays bounded to on-screen casters.
|
||||
local casterGuid = arg3
|
||||
local plate = casterGuid and plateByGuid[casterGuid]
|
||||
if plate then
|
||||
local isChannel = arg8 == 1
|
||||
local durationMs = isChannel and arg7 or arg6
|
||||
if durationMs and durationMs > 0 then
|
||||
local spellId = arg2
|
||||
local now = GetTime()
|
||||
castState[casterGuid] = {
|
||||
spellName = C_Spell.GetSpellName(spellId),
|
||||
spellID = spellId,
|
||||
icon = C_Spell.GetSpellTexture(spellId),
|
||||
startTime = now,
|
||||
endTime = now + durationMs / 1000,
|
||||
duration = durationMs / 1000,
|
||||
isChannel = isChannel,
|
||||
}
|
||||
plate.castUpdate = true -- bypass the throttle so the bar shows now
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "SPELL_FAILED_OTHER" then
|
||||
-- nampower: arg1=casterGuid, arg2=spellId. Clear on interrupt/failure.
|
||||
local casterGuid = arg1
|
||||
if casterGuid and castState[casterGuid] then
|
||||
castState[casterGuid] = nil
|
||||
local plate = plateByGuid[casterGuid]
|
||||
if plate then plate.castUpdate = true end
|
||||
end
|
||||
|
||||
elseif event == "UNIT_AURA" then
|
||||
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set
|
||||
-- changes (add/remove/modify). Flag the matching plate so OnUpdate does a
|
||||
@@ -1590,20 +1662,17 @@ nameplates:RegisterEvent("UNIT_FLAGS")
|
||||
if not nameplate.castbar.isShown then nameplate.castbar.isShown = true; nameplate.castbar:Show() end
|
||||
end
|
||||
|
||||
-- Dedicated frame that updates ONLY the target plate castbar.
|
||||
-- Uses nameplates_castbar throttle from libthrottle.
|
||||
-- Dedicated frame that updates ONLY the target plate castbar. Unthrottled:
|
||||
-- now that casts are event-driven, this just reads the cache + SetValue, so
|
||||
-- it animates the fill every frame for the smoothest sweep on the bar the
|
||||
-- player watches most. (Non-target plates stay throttled via the central
|
||||
-- loop's nameplates_castbar gate.)
|
||||
nameplates.castbarFrame = CreateFrame("Frame", nil, UIParent)
|
||||
nameplates.castbarFrame:SetScript("OnUpdate", function()
|
||||
if not cfg.showcastbar then return end
|
||||
local now = GetTime()
|
||||
local throttle = pfUI.throttle:Get("nameplates_castbar")
|
||||
if (this.tick or 0) > now then return end
|
||||
this.tick = now + throttle
|
||||
|
||||
local frame = C_NamePlate.GetNamePlateForUnit("target")
|
||||
if not frame or not frame.nameplate then return end
|
||||
|
||||
nameplates.UpdateCastbar(frame.nameplate, now)
|
||||
nameplates.UpdateCastbar(frame.nameplate, GetTime())
|
||||
end)
|
||||
|
||||
-- set nameplate game settings
|
||||
|
||||
Reference in New Issue
Block a user