From c955937b12a74fba0a8a710571757a4cc7da85a3 Mon Sep 17 00:00:00 2001 From: Florian Berthold Date: Fri, 15 May 2026 16:13:34 +0200 Subject: [PATCH] fix(items): grace window for nil-after-occupied without retry polling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetContainerItemInfo returns nil for some occupied slots during server lag, causing Bagnon to draw the slot empty. Earlier retry-polling approach caused massive lag because every legitimately emptied slot also matched the "previously had item, now nil" condition and ran ~30 live reads over 3s per move. New approach: on a nil-after-occupied read, if we're within GRACE_WINDOW (0.5s) of the last good draw, keep the prior draw and queue ONE re-check at the deadline — no polling. A genuinely emptied slot resolves at the deadline; a recovered slot redraws with correct data. lastGoodTime is set only on non-nil reads, so persistent lag eventually accepts the empty. --- Bagnon/components/item.lua | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Bagnon/components/item.lua b/Bagnon/components/item.lua index e5c8324..9bded9a 100644 --- a/Bagnon/components/item.lua +++ b/Bagnon/components/item.lua @@ -248,6 +248,38 @@ end --[[ Update Methods ]] -- +-- Lag-race protection for slots that read nil briefly after being occupied. +-- When this happens we keep the prior draw and schedule ONE re-check at the +-- grace deadline; we never poll. After the deadline, whatever the live read +-- says wins, so genuinely emptied slots resolve within GRACE_WINDOW seconds. +local GRACE_WINDOW = 0.5 +local pendingVerify = {} -- slot -> deadline (GetTime) +local verifyFrame = CreateFrame('Frame') +verifyFrame:Hide() +verifyFrame:SetScript('OnUpdate', function(self, elapsed) + self.tick = (self.tick or 0) + elapsed + if self.tick < 0.1 then return end + self.tick = 0 + local now = GetTime() + for slot, deadline in pairs(pendingVerify) do + if now >= deadline then + pendingVerify[slot] = nil + if slot:IsVisible() then + slot:Update() + end + end + end + if not next(pendingVerify) then + self:Hide() + end +end) + +local function scheduleVerify(slot) + if pendingVerify[slot] then return end + pendingVerify[slot] = GetTime() + GRACE_WINDOW + verifyFrame:Show() +end + -- Update the texture, lock status, and other information about an item function ItemSlot:Update() @@ -255,6 +287,18 @@ function ItemSlot:Update() local texture, count, locked, quality, readable, lootable, link = self:GetItemSlotInfo() + -- If the slot was occupied at our last good draw and the live read just + -- returned nil within the grace window, keep the previous draw and queue + -- a single re-check. No retry storm. + if (not link) and self.hasItem and (self.lastGoodTime or 0) + GRACE_WINDOW > GetTime() then + scheduleVerify(self) + return + end + + if link then + self.lastGoodTime = GetTime() + end + self:SetItem(link) self:SetTexture(texture) self:SetCount(count)