fix(items): keep retrying nil slot reads instead of accepting after one pass

Single-retry was still flashing slots empty on the inventory when lag
exceeded 0.25s — the retry pass would read nil and broadcast the empty.
Track a per-slot retry counter (capped at RETRY_MAX=12, ~3s) so we keep
deferring until the link comes back or we hit the ceiling. Bank was
never affected — it goes through PLAYERBANKSLOTS_CHANGED, not BAG_UPDATE.
This commit is contained in:
2026-05-14 02:12:38 +02:00
parent 1af22578d7
commit 60e3cea9fd
+28 -14
View File
@@ -46,6 +46,14 @@ Bagnon.BagEvents = BagEvents
local slots = {}
local bagTypes = {}
local pendingBags = {}
local retryCounts = {}
-- Retry knobs. Server lag spikes on Ascension can easily exceed a second;
-- keep retrying so we don't broadcast an empty slot while item data is still
-- in flight. Accept whatever we read after RETRY_MAX attempts so a genuinely
-- emptied slot eventually resolves.
local RETRY_INTERVAL = 0.25
local RETRY_MAX = 12 -- ~3s worst case
local function ToIndex(bag, slot)
return (bag < 0 and bag*100 - slot) or bag*100 + slot
@@ -55,20 +63,20 @@ 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
if self.elapsed < RETRY_INTERVAL then return end
self.elapsed = 0
self:Hide()
local bags = pendingBags
pendingBags = {}
for bag in pairs(bags) do
Bagnon.BagEvents:UpdateItems(bag, true)
Bagnon.BagEvents:UpdateItems(bag)
end
if not next(pendingBags) then
self:Hide()
end
end)
@@ -134,8 +142,9 @@ function BagEvents:RemoveItem(bag, slot)
end
end
function BagEvents:UpdateItem(bag, slot, isRetry)
local data = slots[ToIndex(bag, slot)]
function BagEvents:UpdateItem(bag, slot)
local index = ToIndex(bag, slot)
local data = slots[index]
if data then
local prevLink = data[1]
@@ -146,12 +155,17 @@ function BagEvents:UpdateItem(bag, slot, isRetry)
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
-- BAG_UPDATE and item data arriving from the server. Keep deferring up to
-- RETRY_MAX times so a multi-second lag spike doesn't flash items empty.
if prevLink and (not link) then
local n = (retryCounts[index] or 0) + 1
if n < RETRY_MAX then
retryCounts[index] = n
scheduleRetry(bag)
return
end
end
retryCounts[index] = nil
if not(prevLink == link and prevCount == count) then
data[1] = link
@@ -164,9 +178,9 @@ function BagEvents:UpdateItem(bag, slot, isRetry)
end
end
function BagEvents:UpdateItems(bag, isRetry)
function BagEvents:UpdateItems(bag)
for slot = 1, GetBagSize(bag) do
self:UpdateItem(bag, slot, isRetry)
self:UpdateItem(bag, slot)
end
end