fix(items): defer empty draw when slot reads nil after lag

Under server lag BAG_UPDATE can fire before item data arrives, causing
GetContainerItemInfo to return nil for a still-occupied slot and Bagnon
to broadcast ITEM_SLOT_UPDATE with no link — the slot then renders empty
until the next bag event. UpdateItem now queues a single ~0.25s retry
for the bag when a previously occupied slot reads nil; the retry pass
trusts whatever it reads, so genuine empties still resolve.
This commit is contained in:
2026-05-14 01:50:42 +02:00
parent 0aac38f254
commit 1af22578d7
+35 -3
View File
@@ -45,6 +45,7 @@ Bagnon.BagEvents = BagEvents
local slots = {}
local bagTypes = {}
local pendingBags = {}
local function ToIndex(bag, slot)
return (bag < 0 and bag*100 - slot) or bag*100 + slot
@@ -54,6 +55,29 @@ local function GetBagSize(bag)
return (bag == KEYRING_CONTAINER and GetKeyRingSize()) or GetContainerNumSlots(bag)
end
-- Defers a re-check of a bag by ~0.25s; used when a previously occupied slot
-- reads back nil (lag race between BAG_UPDATE and item data arriving).
local retryFrame = CreateFrame('Frame')
retryFrame.elapsed = 0
retryFrame:Hide()
retryFrame:SetScript('OnUpdate', function(self, elapsed)
self.elapsed = self.elapsed + elapsed
if self.elapsed < 0.25 then return end
self.elapsed = 0
self:Hide()
local bags = pendingBags
pendingBags = {}
for bag in pairs(bags) do
Bagnon.BagEvents:UpdateItems(bag, true)
end
end)
local function scheduleRetry(bag)
pendingBags[bag] = true
retryFrame.elapsed = 0
retryFrame:Show()
end
--[[ Startup ]]--
@@ -110,7 +134,7 @@ function BagEvents:RemoveItem(bag, slot)
end
end
function BagEvents:UpdateItem(bag, slot)
function BagEvents:UpdateItem(bag, slot, isRetry)
local data = slots[ToIndex(bag, slot)]
if data then
@@ -121,6 +145,14 @@ function BagEvents:UpdateItem(bag, slot)
local start, duration, enable = GetContainerItemCooldown(bag, slot)
local onCooldown = (start > 0 and duration > 0 and enable > 0)
-- A previously occupied slot reading back nil is usually a lag race between
-- BAG_UPDATE and item data arriving from the server. Defer once before drawing
-- the slot as empty; on the second pass we trust whatever we read.
if prevLink and (not link) and (not isRetry) then
scheduleRetry(bag)
return
end
if not(prevLink == link and prevCount == count) then
data[1] = link
data[2] = count
@@ -132,9 +164,9 @@ function BagEvents:UpdateItem(bag, slot)
end
end
function BagEvents:UpdateItems(bag)
function BagEvents:UpdateItems(bag, isRetry)
for slot = 1, GetBagSize(bag) do
self:UpdateItem(bag, slot)
self:UpdateItem(bag, slot, isRetry)
end
end