145 lines
7.0 KiB
Lua
145 lines
7.0 KiB
Lua
-- Guda CacheWarmer
|
||
-- Pre-warms ItemDetection's tooltip-scan cache and BagScanner's bag-data
|
||
-- cache shortly after PLAYER_LOGIN, in the background, so the FIRST time the
|
||
-- user opens the bag frame they don't pay for ~80 synchronous tooltip scans.
|
||
--
|
||
-- Reuses Utils:QueueWork (Core/Utils.lua) for frame-budgeted background
|
||
-- processing — no new scheduler.
|
||
|
||
local addon = Guda
|
||
|
||
local CacheWarmer = {}
|
||
addon.Modules.CacheWarmer = CacheWarmer
|
||
|
||
-- Walk player bags 0-4 and warm BagScanner per-bag cache. Cheap, runs inline.
|
||
function CacheWarmer:WarmBagScanner()
|
||
local BagScanner = Guda.Modules.BagScanner
|
||
if not BagScanner or not BagScanner.ScanBag then return end
|
||
-- ScanBag populates the per-bag cache used by GetBagData later.
|
||
for bagID = 0, 4 do
|
||
local ok = pcall(function() BagScanner:ScanBag(bagID) end)
|
||
if not ok then break end
|
||
end
|
||
end
|
||
|
||
-- Walk player bags 0-4 and queue tooltip-scan work for every cache the
|
||
-- bag-open path reads, so the first DisplayItemsByCategory never blocks on
|
||
-- a cold lookup. Targets:
|
||
-- * ItemDetection.detectionCache — used for isJunk / isQuestItem rules
|
||
-- * 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 ItemDetection = Guda.Modules.ItemDetection
|
||
local CategoryManager = Guda.Modules.CategoryManager
|
||
local BagScanner = Guda.Modules.BagScanner
|
||
if not (Utils and Utils.QueueWork) then return end
|
||
if not (ItemDetection and ItemDetection.GetItemProperties) then return end
|
||
|
||
-- WarmBagScanner ran just before us, so GetBagData() is a cache hit and
|
||
-- returns the already-populated itemData with class/subclass/quality.
|
||
local bagData = BagScanner and BagScanner.GetBagData and BagScanner:GetBagData()
|
||
|
||
for bagID = 0, 4 do
|
||
local numSlots = GetContainerNumSlots(bagID)
|
||
if numSlots and numSlots > 0 then
|
||
for slotID = 1, numSlots do
|
||
local link = GetContainerItemLink(bagID, slotID)
|
||
if link then
|
||
-- Capture upvalues so the closure has stable bag/slot/link.
|
||
local b, s, l = bagID, slotID, link
|
||
|
||
Utils:QueueWork(function()
|
||
-- Minimal itemData — GetItemProperties only needs
|
||
-- .link to compute the cache key; bagID/slotID feed
|
||
-- the tooltip scan.
|
||
ItemDetection:GetItemProperties({ link = l }, b, s)
|
||
end, "CacheWarmer.itemDetection")
|
||
|
||
-- Consumable restoreTag cache (only consumables carry
|
||
-- "while eating" / "while drinking" / "use: restores").
|
||
Utils:QueueWork(function()
|
||
local _, _, _, _, itemType = GetItemInfo(l)
|
||
if itemType == "Consumable" and Utils.GetConsumableRestoreTag then
|
||
Utils:GetConsumableRestoreTag(b, s, l)
|
||
end
|
||
end, "CacheWarmer.restoreTag")
|
||
|
||
-- BoE tooltip cache. Only weapons/armor can be BoE; skip
|
||
-- everything else so we don't scan consumables / trade
|
||
-- goods tooltips for nothing.
|
||
Utils:QueueWork(function()
|
||
local _, _, _, _, _, class = GetItemInfo(l)
|
||
if (class == "Weapon" or class == "Armor") and Utils.IsBindOnEquip then
|
||
Utils:IsBindOnEquip(b, s, l)
|
||
end
|
||
end, "CacheWarmer.boe")
|
||
|
||
-- CategoryManager cache. Snapshot itemData fields because
|
||
-- BagScanner uses a pool and may recycle the table later.
|
||
-- The restoreTag field is populated inside the callback
|
||
-- (not here) because the restoreTag warmer runs first via
|
||
-- FIFO queue order — so by the time this fires, the
|
||
-- tooltipCache.restoreTag lookup is a cache hit.
|
||
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
|
||
|
||
function CacheWarmer:Initialize()
|
||
-- Defer ~0.5s so we don't compete with the busy login frame. The user
|
||
-- typically opens bags several seconds later, so the cache will be hot.
|
||
Guda_ScheduleTimer(0.5, function()
|
||
CacheWarmer:WarmBagScanner()
|
||
CacheWarmer:WarmItemDetectionCache()
|
||
|
||
-- Completion marker — runs after all prior warmers drain. Re-layouts
|
||
-- 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
|
||
if Utils and Utils.QueueWork then
|
||
Utils:QueueWork(function()
|
||
if Guda_BagFrame and Guda_BagFrame:IsShown()
|
||
and Guda.Modules.BagFrame and Guda.Modules.BagFrame.Update then
|
||
Guda.Modules.BagFrame:Update()
|
||
end
|
||
if Guda_BagFrame_UpdateAllUsabilityTints then
|
||
Guda_BagFrame_UpdateAllUsabilityTints()
|
||
end
|
||
end, "CacheWarmer.completionSweep")
|
||
end
|
||
end)
|
||
end
|