perf: add caching and transfer limiting for bag/bank operations

This commit is contained in:
Salikh Gurgenidze
2026-01-24 00:32:30 +04:00
parent 9305b46a85
commit 21efe4c1cc
5 changed files with 291 additions and 38 deletions
+112 -20
View File
@@ -1,12 +1,100 @@
-- Guda Bag Scanner
-- Scans and stores bag contents
-- Scans and stores bag contents with caching and event pending tracking
local addon = Guda
local BagScanner = {}
addon.Modules.BagScanner = BagScanner
-- Scan all bags and return data
-- Cache for bag data to avoid re-scanning all slots on every update
local bagCache = nil
local cacheValid = false
-- Event pending tracking (like Baganator's IsBagEventPending)
local eventPending = false
local dirtySlots = {} -- Track specific slots that changed: dirtySlots[bagID][slotID] = true
-- Clear the bag cache
function BagScanner:ClearCache()
bagCache = nil
cacheValid = false
dirtySlots = {}
end
-- Check if a bag event is pending (use before transfers)
function BagScanner:IsEventPending()
return eventPending
end
-- Clear the pending flag (call after processing)
function BagScanner:ClearEventPending()
eventPending = false
end
-- Mark a specific slot as dirty (incremental tracking)
function BagScanner:MarkSlotDirty(bagID, slotID)
if not dirtySlots[bagID] then
dirtySlots[bagID] = {}
end
dirtySlots[bagID][slotID] = true
eventPending = true
end
-- Get dirty slots and clear them
function BagScanner:GetAndClearDirtySlots()
local dirty = dirtySlots
dirtySlots = {}
return dirty
end
-- Get cached bag data, or scan if cache is invalid
function BagScanner:GetBagData()
if cacheValid and bagCache then
-- Process any dirty slots incrementally
for bagID, slots in pairs(dirtySlots) do
if bagCache[bagID] then
for slotID in pairs(slots) do
local oldData = bagCache[bagID].slots[slotID]
local newData = self:ScanSlot(bagID, slotID)
bagCache[bagID].slots[slotID] = newData
-- Update free slot count
local wasEmpty = (oldData == nil)
local isEmpty = (newData == nil)
if wasEmpty and not isEmpty then
bagCache[bagID].freeSlots = bagCache[bagID].freeSlots - 1
elseif not wasEmpty and isEmpty then
bagCache[bagID].freeSlots = bagCache[bagID].freeSlots + 1
end
end
else
-- Bag not in cache, scan it
bagCache[bagID] = self:ScanBag(bagID)
end
end
dirtySlots = {}
return bagCache
end
-- Cache miss - do full scan
bagCache = self:ScanBags()
cacheValid = true
dirtySlots = {}
return bagCache
end
-- Invalidate cache (force full re-scan on next update)
function BagScanner:InvalidateCache()
cacheValid = false
end
-- Invalidate a specific bag in the cache
function BagScanner:InvalidateBag(bagID)
if not bagCache then return end
bagCache[bagID] = nil
end
-- Scan all bags and return data (full scan)
function BagScanner:ScanBags()
local bagData = {}
@@ -61,8 +149,6 @@ function BagScanner:ScanSlot(bagID, slot)
local itemLink = GetContainerItemLink(bagID, slot)
-- Get item info
-- Correct order: itemName, itemLink, itemRarity, itemLevel, itemCategory, itemType,
-- itemStackCount, itemSubType, itemTexture, itemEquipLoc, itemSellPrice
local name, link, itemQuality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture, itemEquipLoc, itemSellPrice
if itemLink then
name, link, itemQuality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture, itemEquipLoc, itemSellPrice = addon.Modules.Utils:GetItemInfo(itemLink)
@@ -76,9 +162,9 @@ function BagScanner:ScanSlot(bagID, slot)
name = name,
iLevel = iLevel,
type = itemType,
class = itemCategory, -- Category (e.g., "Consumable", "Armor")
subclass = itemSubType, -- SubType (e.g., "Potion", "Cloth")
equipSlot = itemEquipLoc, -- Equipment slot (e.g., "INVTYPE_HEAD") - correct now!
class = itemCategory,
subclass = itemSubType,
equipSlot = itemEquipLoc,
locked = locked,
}
end
@@ -88,29 +174,35 @@ function BagScanner:SaveToDatabase()
local bagData = self:ScanBags()
addon.Modules.DB:SaveBags(bagData)
addon:Debug("Bag data saved to database")
-- Clear tooltip cache so counts update immediately
if addon.Modules.Tooltip and addon.Modules.Tooltip.ClearCache then
addon.Modules.Tooltip:ClearCache()
end
end
-- Initialize with auto-save on bag changes
-- Initialize with event pending tracking
function BagScanner:Initialize()
-- Create event frame for bag updates
local eventFrame = CreateFrame("Frame")
self.eventFrame = eventFrame
-- Register bag update events
-- Register bag update events for pending tracking
eventFrame:RegisterEvent("BAG_UPDATE")
eventFrame:RegisterEvent("BAG_UPDATE_DELAYED")
--
--eventFrame:SetScript("OnEvent", function()
-- if event == "BAG_UPDATE" or event == "BAG_UPDATE_DELAYED" then
-- addon:Debug("Bag update detected, saving data...")
-- self:SaveToDatabase()
-- end
--end)
eventFrame:SetScript("OnEvent", function()
eventPending = true
-- Mark the specific bag as having changes
if arg1 then
if not dirtySlots[arg1] then
dirtySlots[arg1] = {}
end
-- We don't know which slot, so mark entire bag for rescan
-- by invalidating it
if bagCache and bagCache[arg1] then
bagCache[arg1] = nil
end
end
end)
addon:Debug("Bag scanner initialized with auto-save")
end
addon:Debug("Bag scanner initialized with event pending tracking")
end
+101 -5
View File
@@ -1,5 +1,5 @@
-- Guda Bank Scanner
-- Scans and stores bank contents
-- Scans and stores bank contents with caching and event pending tracking
local addon = Guda
@@ -8,7 +8,102 @@ addon.Modules.BankScanner = BankScanner
local bankOpen = false
-- Scan all bank bags and return data
-- Cache for bank data to avoid re-scanning all slots on every update
local bankCache = nil
local cacheValid = false
-- Event pending tracking (like Baganator's IsBagEventPending)
local eventPending = false
local dirtySlots = {} -- Track specific slots that changed
-- Clear the bank cache (called when bank opens or significant changes occur)
function BankScanner:ClearCache()
bankCache = nil
cacheValid = false
dirtySlots = {}
eventPending = false
end
-- Check if a bank event is pending (use before transfers)
function BankScanner:IsEventPending()
return eventPending
end
-- Clear the pending flag (call after processing)
function BankScanner:ClearEventPending()
eventPending = false
end
-- Mark a specific slot as dirty (incremental tracking)
function BankScanner:MarkSlotDirty(bagID, slotID)
if not dirtySlots[bagID] then
dirtySlots[bagID] = {}
end
dirtySlots[bagID][slotID] = true
eventPending = true
end
-- Get cached bank data, or scan if cache is invalid
function BankScanner:GetBankData()
if not bankOpen then
return {}
end
if cacheValid and bankCache then
-- Process any dirty slots incrementally
for bagID, slots in pairs(dirtySlots) do
if bankCache[bagID] then
for slotID in pairs(slots) do
local oldData = bankCache[bagID].slots[slotID]
local newData = addon.Modules.BagScanner:ScanSlot(bagID, slotID)
bankCache[bagID].slots[slotID] = newData
-- Update free slot count
local wasEmpty = (oldData == nil)
local isEmpty = (newData == nil)
if wasEmpty and not isEmpty then
bankCache[bagID].freeSlots = bankCache[bagID].freeSlots - 1
elseif not wasEmpty and isEmpty then
bankCache[bagID].freeSlots = bankCache[bagID].freeSlots + 1
end
end
else
-- Bag not in cache, scan it
bankCache[bagID] = self:ScanBankBag(bagID)
end
end
dirtySlots = {}
return bankCache
end
-- Cache miss - do full scan
bankCache = self:ScanBank()
cacheValid = true
dirtySlots = {}
return bankCache
end
-- Update a single slot in the cache (incremental update)
function BankScanner:UpdateSlot(bagID, slotID)
if not bankOpen then return end
-- Mark as dirty for next GetBankData call
self:MarkSlotDirty(bagID, slotID)
end
-- Invalidate cache (force full re-scan on next update)
function BankScanner:InvalidateCache()
cacheValid = false
end
-- Invalidate a specific bag in the cache (force re-scan of just that bag)
function BankScanner:InvalidateBag(bagID)
if not bankOpen then return end
if not bankCache then return end
bankCache[bagID] = nil
end
-- Scan all bank bags and return data (full scan)
function BankScanner:ScanBank()
if not bankOpen then
addon:Debug("Cannot scan bank - not open")
@@ -67,16 +162,17 @@ function BankScanner:SaveToDatabase()
return
end
local bankData = self:ScanBank()
local bankData = self:GetBankData() -- Use cached data
addon.Modules.DB:SaveBank(bankData)
addon:Debug("Bank data saved")
end
-- Initialize bank scanner
function BankScanner:Initialize()
-- Bank opened - save bank data
-- Bank opened - do initial scan
addon.Modules.Events:OnBankOpen(function()
bankOpen = true
BankScanner:ClearCache() -- Clear cache on open
addon:Debug("Bank opened")
-- Delay scan to ensure bank is fully loaded
@@ -94,11 +190,11 @@ function BankScanner:Initialize()
-- Bank closed
addon.Modules.Events:OnBankClose(function()
-- Do a final save on close before marking bank as closed
-- so that any last changes are captured.
addon:Debug("Bank closing - performing final save")
BankScanner:SaveToDatabase()
bankOpen = false
BankScanner:ClearCache() -- Clear cache on close
addon:Debug("Bank closed")
end, "BankScanner")
end