Remove redundant cache warm scans

This commit is contained in:
Dusk-92
2026-09-05 15:35:43 +02:00
parent b6aa9ab5a7
commit 8278cbccdd
+63 -93
View File
@@ -1,114 +1,89 @@
-- Guda CacheWarmer -- Guda CacheWarmer
-- Pre-warms ItemDetection's tooltip-scan cache and BagScanner's bag-data -- Pre-warms ItemDetection and category-related tooltip caches shortly after
-- cache shortly after PLAYER_LOGIN, in the background, so the FIRST time the -- PLAYER_LOGIN so the first bag open does not pay for cold tooltip scans.
-- user opens the bag frame they don't pay for ~80 synchronous tooltip scans.
-- --
-- Reuses Utils:QueueWork (Core/Utils.lua) for frame-budgeted background -- Guda ClassicAPI uses the low-latency FIFO in Core/Performance.lua. Heavy
-- processing — no new scheduler. -- tooltip operations stay as separate work items so a single callback cannot
-- accidentally bundle several expensive tooltip scans into one frame.
local addon = Guda local addon = Guda
local CacheWarmer = {} local CacheWarmer = {}
addon.Modules.CacheWarmer = CacheWarmer addon.Modules.CacheWarmer = CacheWarmer
-- Walk player bags 0-4 and warm BagScanner per-bag cache. Cheap, runs inline. -- Populate the real BagScanner cache once. The original implementation called
-- ScanBag() for bags 0-4 but discarded every returned table, then GetBagData()
-- immediately scanned the bags again. Returning GetBagData() here makes the
-- warmup useful and gives the later stages the exact cached snapshot.
function CacheWarmer:WarmBagScanner() function CacheWarmer:WarmBagScanner()
local BagScanner = Guda.Modules.BagScanner local BagScanner = Guda.Modules.BagScanner
if not BagScanner or not BagScanner.ScanBag then return end if not BagScanner or not BagScanner.GetBagData then return nil end
-- ScanBag populates the per-bag cache used by GetBagData later. return BagScanner:GetBagData()
for bagID = 0, 4 do
local ok = pcall(function() BagScanner:ScanBag(bagID) end)
if not ok then break end
end
end end
-- Walk player bags 0-4 and queue tooltip-scan work for every cache the -- Queue tooltip/cache work using the already-cached BagScanner snapshot.
-- bag-open path reads, so the first DisplayItemsByCategory never blocks on -- This avoids another round of GetContainerNumSlots/GetContainerItemLink and
-- a cold lookup. Targets: -- avoids GetItemInfo calls just to discover the item class.
-- * ItemDetection.detectionCache — used for isJunk / isQuestItem rules function CacheWarmer:WarmItemDetectionCache(bagData)
-- * Utils.tooltipCache.restoreTag — used for consumable food/drink grouping
-- * Utils.tooltipCache.bindOnEquip — used for the isBoE rule
-- * CategoryManager.categoryCache — used directly by the layout path
-- Queue order is FIFO and budget-limited to 100ms/frame, so this all runs
-- in the 0.5s–~2s window after PLAYER_LOGIN.
function CacheWarmer:WarmItemDetectionCache()
local Utils = Guda.Modules.Utils local Utils = Guda.Modules.Utils
local ItemDetection = Guda.Modules.ItemDetection local ItemDetection = Guda.Modules.ItemDetection
local CategoryManager = Guda.Modules.CategoryManager local CategoryManager = Guda.Modules.CategoryManager
local BagScanner = Guda.Modules.BagScanner local BagScanner = Guda.Modules.BagScanner
if not (Utils and Utils.QueueWork) then return end if not (Utils and Utils.QueueWork) then return end
if not (ItemDetection and ItemDetection.GetItemProperties) then return end if not (ItemDetection and ItemDetection.GetItemProperties) then return end
-- WarmBagScanner ran just before us, so GetBagData() is a cache hit and bagData = bagData or (BagScanner and BagScanner.GetBagData
-- returns the already-populated itemData with class/subclass/quality. and BagScanner:GetBagData())
local bagData = BagScanner and BagScanner.GetBagData and BagScanner:GetBagData() if not bagData then return end
for bagID = 0, 4 do for bagID = 0, 4 do
local numSlots = GetContainerNumSlots(bagID) local bag = bagData[bagID]
if numSlots and numSlots > 0 then if bag and bag.slots then
for slotID = 1, numSlots do for slotID, slotData in pairs(bag.slots) do
local link = GetContainerItemLink(bagID, slotID) if slotData and slotData.link then
if link then local b, s = bagID, slotID
-- Capture upvalues so the closure has stable bag/slot/link. local snapshot = {
local b, s, l = bagID, slotID, link link = slotData.link,
itemID = slotData.itemID,
name = slotData.name,
class = slotData.class,
type = slotData.type,
subclass = slotData.subclass,
quality = slotData.quality,
texture = slotData.texture,
equipSlot = slotData.equipSlot,
}
-- Central property scan. Keep this as its own queue item:
-- SetBagItem can be expensive on a cold client cache.
Utils:QueueWork(function() Utils:QueueWork(function()
-- Minimal itemData — GetItemProperties only needs ItemDetection:GetItemProperties(snapshot, b, s)
-- .link to compute the cache key; bagID/slotID feed
-- the tooltip scan.
ItemDetection:GetItemProperties({ link = l }, b, s)
end, "CacheWarmer.itemDetection") end, "CacheWarmer.itemDetection")
-- Consumable restoreTag cache (only consumables carry -- Only consumables need the restore-text tooltip cache.
-- "while eating" / "while drinking" / "use: restores"). if snapshot.class == "Consumable"
Utils:QueueWork(function() and Utils.GetConsumableRestoreTag then
local _, _, _, _, itemType = GetItemInfo(l) Utils:QueueWork(function()
if itemType == "Consumable" and Utils.GetConsumableRestoreTag then snapshot.restoreTag =
Utils:GetConsumableRestoreTag(b, s, l) Utils:GetConsumableRestoreTag(b, s, snapshot.link)
end end, "CacheWarmer.restoreTag")
end, "CacheWarmer.restoreTag") end
-- BoE tooltip cache. Only weapons/armor can be BoE; skip -- Only equipment can be Bind on Equip.
-- everything else so we don't scan consumables / trade if (snapshot.class == "Weapon" or snapshot.class == "Armor")
-- goods tooltips for nothing. and Utils.IsBindOnEquip then
Utils:QueueWork(function() Utils:QueueWork(function()
local _, _, _, _, _, class = GetItemInfo(l) Utils:IsBindOnEquip(b, s, snapshot.link)
if (class == "Weapon" or class == "Armor") and Utils.IsBindOnEquip then end, "CacheWarmer.boe")
Utils:IsBindOnEquip(b, s, l) end
end
end, "CacheWarmer.boe")
-- CategoryManager cache. Snapshot itemData fields because -- Runs after the relevant warmers because the queue is FIFO,
-- BagScanner uses a pool and may recycle the table later. -- so category rules hit warm ItemDetection/tooltip caches.
-- The restoreTag field is populated inside the callback if CategoryManager and CategoryManager.CategorizeItem then
-- (not here) because the restoreTag warmer runs first via Utils:QueueWork(function()
-- FIFO queue order — so by the time this fires, the CategoryManager:CategorizeItem(snapshot, b, s, false)
-- tooltipCache.restoreTag lookup is a cache hit. end, "CacheWarmer.category")
if CategoryManager and CategoryManager.CategorizeItem and bagData then
local bag = bagData[b]
local slotData = bag and bag.slots and bag.slots[s]
if slotData and slotData.link then
local snapshot = {
link = slotData.link,
name = slotData.name,
class = slotData.class,
type = slotData.type,
subclass = slotData.subclass,
quality = slotData.quality,
texture = slotData.texture,
equipSlot = slotData.equipSlot,
}
Utils:QueueWork(function()
-- Pull restoreTag from warm cache so rules that
-- key on it (Food/Drink categories) match.
if snapshot.class == "Consumable"
and Utils.GetConsumableRestoreTag then
snapshot.restoreTag =
Utils:GetConsumableRestoreTag(b, s, snapshot.link)
end
CategoryManager:CategorizeItem(snapshot, b, s, false)
end, "CacheWarmer.category")
end
end end
end end
end end
@@ -117,17 +92,12 @@ function CacheWarmer:WarmItemDetectionCache()
end end
function CacheWarmer:Initialize() function CacheWarmer:Initialize()
-- Defer ~0.5s so we don't compete with the busy login frame. The user -- Defer startup work so we do not compete with the login frame.
-- typically opens bags several seconds later, so the cache will be hot.
Guda_ScheduleTimer(0.5, function() Guda_ScheduleTimer(0.5, function()
CacheWarmer:WarmBagScanner() local bagData = CacheWarmer:WarmBagScanner()
CacheWarmer:WarmItemDetectionCache() CacheWarmer:WarmItemDetectionCache(bagData)
-- Completion marker — runs after all prior warmers drain. Re-layouts -- Completion marker runs after all prior FIFO work drains.
-- the bag so items that fell back to their class bucket on the first
-- (cold-cache) render now land in their real categories, and re-sweeps
-- tints for items whose detection finished while the bag was already
-- open.
local Utils = Guda.Modules.Utils local Utils = Guda.Modules.Utils
if Utils and Utils.QueueWork then if Utils and Utils.QueueWork then
Utils:QueueWork(function() Utils:QueueWork(function()