mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
feat(lua): Item follows retail
Util/Item.lua is now retail Blizzard_ObjectAPI/Mainline/Item.lua in
retail's own text, and its listener is retail's AsyncCallbackSystem.lua
with the item and quest listeners. There is no spell listener: every
spell is client-side data here, so there is nothing to wait for. The one
other difference is that GetItemQualityColor reads ITEM_QUALITY_COLORS,
the same entry retail's ColorManager returns.
What follows retail now:
- Load callbacks get (id, success).
- An item callback also runs when the load fails, with success nil.
TradeSkillLink's two callbacks now stop on a failure, because
re-rendering would request the missing item again.
- C_Item.RequestLoadItemDataByID runs once per item, not once per
callback.
- New methods: GetStackCount, ContinueWithCancelOnRecordLoad and
IsRecordDataCached. ContinueOnItemLoad's usage errors name the bad
itemID or link, and GetItemGUID returns the GUID an item was made from.
C_Item.GetItemIDForItemInfo is new in the DLL for ItemMixin:GetItemID's
fallback. It uses the resolver that GetItemInfoInstant uses.
Verified in game: Item:CreateFromItemID(19019) calls back with
(19019, 1), and C_Item.GetItemIDForItemInfo("item:6948") returns 6948.
This commit is contained in:
@@ -149,7 +149,7 @@ on a frame timer" goal landed as the request/event pair below:
|
||||
cache (or synchronously if it was already loaded).
|
||||
|
||||
Together those replace the polyfill's 180-second polling frame
|
||||
(`ItemEventListener` in `Util/ItemUtil.lua:254-372`) — addons can now
|
||||
(`ItemEventListener`, now in `Util/AsyncCallbackSystem.lua`) — addons can now
|
||||
fire `RequestLoadItemDataByID(id)` and listen for
|
||||
`ITEM_DATA_LOAD_RESULT` instead.
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ Util\ColorUtil.lua
|
||||
Util\ItemLocation.lua
|
||||
Util\PlayerLocation.lua
|
||||
Util\EventUtil.lua
|
||||
Util\AsyncCallbackSystem.lua
|
||||
Util\Item.lua
|
||||
Util\AddOnCompat.lua
|
||||
Util\FunctionUtil.lua
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
-- Backport of retail's Blizzard_ObjectAPI/Mainline/AsyncCallbackSystem.lua.
|
||||
--
|
||||
-- Differences from the retail source:
|
||||
-- - No ASYNC_SPELL type and no SpellEventListener. This client keeps every
|
||||
-- spell in its own data files, so there is no spell data to wait for and
|
||||
-- no C_Spell.RequestLoadSpellData / SPELL_DATA_LOAD_RESULT.
|
||||
|
||||
--[[
|
||||
Queries some data retrieval API (specifically where the data may not be currently available) and when it becomes available
|
||||
calls a user-supplied function. The callback can be canceled if necessary (e.g. the frame that would use the data becomes
|
||||
hidden before the data arrives).
|
||||
|
||||
The API is managed so that arbitrary query functions cannot be executed.
|
||||
--]]
|
||||
|
||||
AsyncCallbackAPIType = {
|
||||
ASYNC_QUEST = 1,
|
||||
ASYNC_ITEM = 2,
|
||||
}
|
||||
|
||||
local permittedAPI =
|
||||
{
|
||||
[AsyncCallbackAPIType.ASYNC_QUEST] = { event = "QUEST_DATA_LOAD_RESULT", accessor = C_QuestLog.RequestLoadQuestByID },
|
||||
[AsyncCallbackAPIType.ASYNC_ITEM] = { event = "ITEM_DATA_LOAD_RESULT", accessor = C_Item.RequestLoadItemDataByID },
|
||||
};
|
||||
|
||||
AsyncCallbackSystemMixin = {};
|
||||
|
||||
function AsyncCallbackSystemMixin:Init(apiType)
|
||||
self.callbacks = {};
|
||||
|
||||
-- API Type should be set up from key value pairs before OnLoad.
|
||||
self.api = permittedAPI[apiType];
|
||||
|
||||
self:SetScript("OnEvent",
|
||||
function(self, event, ...)
|
||||
if event == self.api.event then
|
||||
local id, success = ...;
|
||||
if success or apiType == AsyncCallbackAPIType.ASYNC_ITEM then
|
||||
self:FireCallbacks(id, success);
|
||||
else
|
||||
self:ClearCallbacks(id);
|
||||
end
|
||||
end
|
||||
end
|
||||
);
|
||||
self:RegisterEvent(self.api.event);
|
||||
end
|
||||
|
||||
local CANCELED_SENTINEL = -1;
|
||||
|
||||
function AsyncCallbackSystemMixin:AddCallback(id, callbackFunction)
|
||||
local callbacks = self:GetOrCreateCallbacks(id);
|
||||
table.insert(callbacks, callbackFunction);
|
||||
local needsAccessorCall = #callbacks == 1;
|
||||
if needsAccessorCall then
|
||||
self.api.accessor(id);
|
||||
end
|
||||
|
||||
return #callbacks, callbacks;
|
||||
end
|
||||
|
||||
function AsyncCallbackSystemMixin:AddCancelableCallback(id, callbackFunction)
|
||||
-- NOTE: If the data is currently availble then the callback will be executed and callbacks cleared, so there will be nothing to cancel.
|
||||
local index, callbacks = self:AddCallback(id, callbackFunction);
|
||||
return function()
|
||||
if #callbacks > 0 and callbacks[index] ~= CANCELED_SENTINEL then
|
||||
callbacks[index] = CANCELED_SENTINEL;
|
||||
return true;
|
||||
end
|
||||
return false;
|
||||
end;
|
||||
end
|
||||
|
||||
function AsyncCallbackSystemMixin:FireCallbacks(id, success)
|
||||
local callbacks = self:GetCallbacks(id);
|
||||
if callbacks then
|
||||
self:ClearCallbacks(id);
|
||||
for i, callback in ipairs(callbacks) do
|
||||
if callback ~= CANCELED_SENTINEL then
|
||||
xpcall(callback, CallErrorHandler, id, success);
|
||||
end
|
||||
end
|
||||
|
||||
-- The cancel functions have a reference to this table, so ensure that it's cleared out.
|
||||
for i = #callbacks, 1, -1 do
|
||||
callbacks[i] = nil;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AsyncCallbackSystemMixin:ClearCallbacks(id)
|
||||
self.callbacks[id] = nil;
|
||||
end
|
||||
|
||||
function AsyncCallbackSystemMixin:GetCallbacks(id)
|
||||
return self.callbacks[id];
|
||||
end
|
||||
|
||||
function AsyncCallbackSystemMixin:GetOrCreateCallbacks(id)
|
||||
local callbacks = self.callbacks[id];
|
||||
if not callbacks then
|
||||
callbacks = {};
|
||||
self.callbacks[id] = callbacks;
|
||||
end
|
||||
return callbacks;
|
||||
end
|
||||
|
||||
local function CreateListener(apiType)
|
||||
local listener = Mixin(CreateFrame("Frame"), AsyncCallbackSystemMixin);
|
||||
listener:Init(apiType);
|
||||
return listener;
|
||||
end
|
||||
|
||||
ItemEventListener = CreateListener(AsyncCallbackAPIType.ASYNC_ITEM);
|
||||
QuestEventListener = CreateListener(AsyncCallbackAPIType.ASYNC_QUEST);
|
||||
@@ -1,70 +1,55 @@
|
||||
-- Backport of 3.3.5 FrameXML/ItemUtil.lua to vanilla 1.12 / Lua 5.0.
|
||||
-- Backport of retail's Blizzard_ObjectAPI/Mainline/Item.lua.
|
||||
--
|
||||
-- Implementation differences from the 3.3.5 source:
|
||||
-- - Many C_Item.* calls the modern code expects are not exposed by
|
||||
-- ClassicAPI.dll. We shim the missing ones at the top using the
|
||||
-- stock 1.12 globals (GetItemInfo, GetInventoryItemLink,
|
||||
-- GetContainerItemLink/Info). DLL-provided functions are left alone
|
||||
-- via `or` guards so we never clobber a real implementation.
|
||||
-- - Lua 5.0 has no select(); calls that previously did
|
||||
-- `select(N, GetItemInfo(...))` use named locals to peel off the
|
||||
-- return values instead.
|
||||
-- - CallErrorHandler isn't in 1.12; we route through geterrorhandler().
|
||||
-- - In 1.12 OnEvent scripts, event name + payload are exposed via the
|
||||
-- `event`, `arg1`, `arg2`... globals rather than as function args.
|
||||
-- - 1.12 has no concept of an item GUID and no public item-lock API,
|
||||
-- so those return nil / no-op.
|
||||
-- Differences from the retail source:
|
||||
-- - GetItemQualityColor reads ITEM_QUALITY_COLORS directly; retail's
|
||||
-- ColorManager.GetColorDataForItemQuality returns the same entry.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Item = {}
|
||||
ItemMixin = {}
|
||||
|
||||
local ItemEventListener
|
||||
Item = {};
|
||||
ItemMixin = {};
|
||||
|
||||
--[[static]] function Item:CreateFromItemLocation(itemLocation)
|
||||
if type(itemLocation) ~= "table" or type(itemLocation.HasAnyLocation) ~= "function" or not itemLocation:HasAnyLocation() then
|
||||
error("Usage: Item:CreateFromItemLocation(notEmptyItemLocation)", 2)
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin)
|
||||
item:SetItemLocation(itemLocation)
|
||||
return item
|
||||
if type(itemLocation) ~= "table" or type(itemLocation.HasAnyLocation) ~= "function" or not itemLocation:HasAnyLocation() then
|
||||
error("Usage: Item:CreateFromItemLocation(notEmptyItemLocation)", 2);
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin);
|
||||
item:SetItemLocation(itemLocation);
|
||||
return item;
|
||||
end
|
||||
|
||||
--[[static]] function Item:CreateFromBagAndSlot(bagID, slotIndex)
|
||||
if type(bagID) ~= "number" or type(slotIndex) ~= "number" then
|
||||
error("Usage: Item:CreateFromBagAndSlot(bagID, slotIndex)", 2)
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin)
|
||||
item:SetItemLocation(ItemLocation:CreateFromBagAndSlot(bagID, slotIndex))
|
||||
return item
|
||||
if type(bagID) ~= "number" or type(slotIndex) ~= "number" then
|
||||
error("Usage: Item:CreateFromBagAndSlot(bagID, slotIndex)", 2);
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin);
|
||||
item:SetItemLocation(ItemLocation:CreateFromBagAndSlot(bagID, slotIndex));
|
||||
return item;
|
||||
end
|
||||
|
||||
--[[static]] function Item:CreateFromEquipmentSlot(equipmentSlotIndex)
|
||||
if type(equipmentSlotIndex) ~= "number" then
|
||||
error("Usage: Item:CreateFromEquipmentSlot(equipmentSlotIndex)", 2)
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin)
|
||||
item:SetItemLocation(ItemLocation:CreateFromEquipmentSlot(equipmentSlotIndex))
|
||||
return item
|
||||
if type(equipmentSlotIndex) ~= "number" then
|
||||
error("Usage: Item:CreateFromEquipmentSlot(equipmentSlotIndex)", 2);
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin);
|
||||
item:SetItemLocation(ItemLocation:CreateFromEquipmentSlot(equipmentSlotIndex));
|
||||
return item;
|
||||
end
|
||||
|
||||
--[[static]] function Item:CreateFromItemLink(itemLink)
|
||||
if type(itemLink) ~= "string" then
|
||||
error("Usage: Item:CreateFromItemLink(itemLinkString)", 2)
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin)
|
||||
item:SetItemLink(itemLink)
|
||||
return item
|
||||
if type(itemLink) ~= "string" then
|
||||
error("Usage: Item:CreateFromItemLink(itemLinkString)", 2);
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin);
|
||||
item:SetItemLink(itemLink);
|
||||
return item;
|
||||
end
|
||||
|
||||
--[[static]] function Item:CreateFromItemID(itemID)
|
||||
if type(itemID) ~= "number" then
|
||||
error("Usage: Item:CreateFromItemID(itemID)", 2)
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin)
|
||||
item:SetItemID(itemID)
|
||||
return item
|
||||
if type(itemID) ~= "number" then
|
||||
error("Usage: Item:CreateFromItemID(itemID)", 2);
|
||||
end
|
||||
local item = CreateFromMixins(ItemMixin);
|
||||
item:SetItemID(itemID);
|
||||
return item;
|
||||
end
|
||||
|
||||
--[[static]] function Item:CreateFromItemGUID(itemGUID)
|
||||
@@ -103,18 +88,18 @@ function ItemMixin:Matches(item)
|
||||
end
|
||||
|
||||
function ItemMixin:SetItemLocation(itemLocation)
|
||||
self:Clear()
|
||||
self.itemLocation = itemLocation
|
||||
self:Clear();
|
||||
self.itemLocation = itemLocation;
|
||||
end
|
||||
|
||||
function ItemMixin:SetItemLink(itemLink)
|
||||
self:Clear()
|
||||
self.itemLink = itemLink
|
||||
self:Clear();
|
||||
self.itemLink = itemLink;
|
||||
end
|
||||
|
||||
function ItemMixin:SetItemID(itemID)
|
||||
self:Clear()
|
||||
self.itemID = itemID
|
||||
self:Clear();
|
||||
self.itemID = itemID;
|
||||
end
|
||||
|
||||
function ItemMixin:SetItemGUID(itemGUID)
|
||||
@@ -133,129 +118,149 @@ function ItemMixin:GetItemLocation()
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemGUID()
|
||||
if self.itemGUID then
|
||||
return self.itemGUID;
|
||||
end
|
||||
|
||||
if self.itemLocation then
|
||||
return C_Item.GetItemGUID(self.itemLocation);
|
||||
end
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:HasItemLocation()
|
||||
return self.itemLocation ~= nil
|
||||
return self.itemLocation ~= nil;
|
||||
end
|
||||
|
||||
function ItemMixin:Clear()
|
||||
self.itemLocation = nil
|
||||
self.itemLink = nil
|
||||
self.itemID = nil
|
||||
self.itemGUID = nil
|
||||
self.itemLocation = nil;
|
||||
self.itemLink = nil;
|
||||
self.itemID = nil;
|
||||
self.itemGUID = nil;
|
||||
end
|
||||
|
||||
function ItemMixin:IsItemEmpty()
|
||||
if self:GetStaticBackingItem() then
|
||||
return not C_Item.DoesItemExistByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return not C_Item.DoesItemExistByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
return not self:IsItemInPlayersControl()
|
||||
return not self:IsItemInPlayersControl();
|
||||
end
|
||||
|
||||
function ItemMixin:GetStaticBackingItem()
|
||||
return self.itemLink or self.itemID
|
||||
return self.itemLink or self.itemID;
|
||||
end
|
||||
|
||||
function ItemMixin:IsItemInPlayersControl()
|
||||
local itemLocation = self:GetItemLocation()
|
||||
return itemLocation and C_Item.DoesItemExist(itemLocation)
|
||||
local itemLocation = self:GetItemLocation();
|
||||
return itemLocation and C_Item.DoesItemExist(itemLocation);
|
||||
end
|
||||
|
||||
-- Item API
|
||||
function ItemMixin:GetItemID()
|
||||
if self:GetStaticBackingItem() then
|
||||
return (C_Item.GetItemInfoInstant(self:GetStaticBackingItem()))
|
||||
end
|
||||
local backingItem = self:GetStaticBackingItem();
|
||||
if backingItem then
|
||||
return C_Item.GetItemInfoInstant(backingItem) or C_Item.GetItemIDForItemInfo(backingItem);
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemID(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemID(self:GetItemLocation());
|
||||
end
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:IsItemLocked()
|
||||
return self:IsItemInPlayersControl() and C_Item.IsLocked(self:GetItemLocation())
|
||||
return self:IsItemInPlayersControl() and C_Item.IsLocked(self:GetItemLocation());
|
||||
end
|
||||
|
||||
function ItemMixin:LockItem()
|
||||
if self:IsItemInPlayersControl() then
|
||||
C_Item.LockItem(self:GetItemLocation())
|
||||
end
|
||||
if self:IsItemInPlayersControl() then
|
||||
C_Item.LockItem(self:GetItemLocation());
|
||||
end
|
||||
end
|
||||
|
||||
function ItemMixin:UnlockItem()
|
||||
if self:IsItemInPlayersControl() then
|
||||
C_Item.UnlockItem(self:GetItemLocation())
|
||||
end
|
||||
if self:IsItemInPlayersControl() then
|
||||
C_Item.UnlockItem(self:GetItemLocation());
|
||||
end
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemIcon() -- requires item data to be loaded
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemIconByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemIconByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemIcon(self:GetItemLocation())
|
||||
end
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemIcon(self:GetItemLocation());
|
||||
end
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemName() -- requires item data to be loaded
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemNameByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemNameByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemName(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemName(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemLink() -- requires item data to be loaded
|
||||
if self.itemLink then
|
||||
return self.itemLink
|
||||
end
|
||||
if self.itemLink then
|
||||
return self.itemLink;
|
||||
end
|
||||
|
||||
if self.itemID then
|
||||
return (select(2, C_Item.GetItemInfo(self.itemID)));
|
||||
end
|
||||
if self.itemID then
|
||||
return (select(2, C_Item.GetItemInfo(self.itemID)));
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemLink(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemLink(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemQuality() -- requires item data to be loaded
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemQualityByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemQualityByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemQuality(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemQuality(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetStackCount() -- requires item data to be loaded
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetStackCount(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetCurrentItemLevel() -- requires item data to be loaded
|
||||
if self:GetStaticBackingItem() then
|
||||
return (C_Item.GetDetailedItemLevelInfo(self:GetStaticBackingItem()))
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return (C_Item.GetDetailedItemLevelInfo(self:GetStaticBackingItem()));
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetCurrentItemLevel(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetCurrentItemLevel(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemQualityColor() -- requires item data to be loaded
|
||||
local itemQuality = self:GetItemQuality()
|
||||
return ITEM_QUALITY_COLORS[itemQuality] -- may be nil if item data isn't loaded
|
||||
local itemQuality = self:GetItemQuality();
|
||||
return ITEM_QUALITY_COLORS[itemQuality]; -- may be nil if item data isn't loaded
|
||||
end
|
||||
|
||||
|
||||
function ItemMixin:GetItemQualityColorRGB() -- requires item data to be loaded
|
||||
local colorTbl = self:GetItemQualityColor()
|
||||
return colorTbl.color:GetRGB()
|
||||
local colorTbl = self:GetItemQualityColor();
|
||||
return colorTbl.color:GetRGB();
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemMaxStackSize() -- requires item data to be loaded
|
||||
@@ -275,141 +280,75 @@ function ItemMixin:IsStackable() -- requires item data to be loaded
|
||||
end
|
||||
|
||||
function ItemMixin:GetInventoryType()
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemInventoryTypeByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.GetItemInventoryTypeByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemInventoryType(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function ItemMixin:GetItemGUID()
|
||||
if self:GetStaticBackingItem() then
|
||||
return nil
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemGUID(self:GetItemLocation())
|
||||
end
|
||||
return nil
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.GetItemInventoryType(self:GetItemLocation());
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
function ItemMixin:GetInventoryTypeName()
|
||||
if not self:IsItemEmpty() then
|
||||
return select(4, C_Item.GetItemInfoInstant(self:GetItemID()));
|
||||
end
|
||||
if not self:IsItemEmpty() then
|
||||
return select(4, C_Item.GetItemInfoInstant(self:GetItemID()));
|
||||
end
|
||||
end
|
||||
|
||||
function ItemMixin:IsItemDataCached()
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.IsItemDataCachedByID(self:GetStaticBackingItem())
|
||||
end
|
||||
if self:GetStaticBackingItem() then
|
||||
return C_Item.IsItemDataCachedByID(self:GetStaticBackingItem());
|
||||
end
|
||||
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.IsItemDataCached(self:GetItemLocation())
|
||||
end
|
||||
return true
|
||||
if not self:IsItemEmpty() then
|
||||
return C_Item.IsItemDataCached(self:GetItemLocation());
|
||||
end
|
||||
return true;
|
||||
end
|
||||
|
||||
function ItemMixin:IsDataEvictable()
|
||||
return true
|
||||
-- Item data could be evicted from the cache
|
||||
return true;
|
||||
end
|
||||
|
||||
-- Default error handler routing: stock 1.12 has no CallErrorHandler.
|
||||
local function ItemUtilErrorHandler(err)
|
||||
local h = geterrorhandler and geterrorhandler()
|
||||
if h then return h(err) end
|
||||
function ItemMixin:ValidateForContinueOnItemLoad(methodName, callbackFunction)
|
||||
if type(callbackFunction) ~= "function" then
|
||||
error(("Usage: NonEmptyItem:%s(callbackFunction): invalid callbackFunction"):format(methodName), 3);
|
||||
end
|
||||
|
||||
if self:IsItemEmpty() then
|
||||
if self.itemLink then
|
||||
error(("Usage: NonEmptyItem:%s(callbackFunction) invalid itemLink: <%s>"):format(methodName, self.itemLink), 3);
|
||||
elseif self.itemID then
|
||||
error(("Usage: NonEmptyItem:%s(callbackFunction) invalid itemID: <%d>"):format(methodName, self.itemID), 3);
|
||||
end
|
||||
|
||||
error(("Usage: NonEmptyItem:%s(callbackFunction): invalid item"):format(methodName), 3);
|
||||
else
|
||||
if not self:GetItemID() then
|
||||
error(("Usage: NonEmptyItem:%s(callbackFunction): item appeared non-empty, but had invalid itemID. itemID: <%s>, itemLink: <%s>"):format(methodName, tostring(self.itemID), tostring(self.itemLink)), 3);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add a callback to be executed when item data is loaded, if the item data is already loaded then execute it immediately
|
||||
function ItemMixin:ContinueOnItemLoad(callbackFunction)
|
||||
if type(callbackFunction) ~= "function" or self:IsItemEmpty() then
|
||||
error("Usage: NonEmptyItem:ContinueOnLoad(callbackFunction)", 2)
|
||||
end
|
||||
|
||||
ItemEventListener:AddCallback(self:GetItemID(), callbackFunction)
|
||||
self:ValidateForContinueOnItemLoad("ContinueOnItemLoad", callbackFunction);
|
||||
ItemEventListener:AddCallback(self:GetItemID(), callbackFunction);
|
||||
end
|
||||
|
||||
-- Same as ContinueOnItemLoad, except it returns a function that when called will cancel the continue
|
||||
function ItemMixin:ContinueWithCancelOnItemLoad(callbackFunction)
|
||||
if type(callbackFunction) ~= "function" or self:IsItemEmpty() then
|
||||
error("Usage: NonEmptyItem:ContinueWithCancelOnItemLoad(callbackFunction)", 2)
|
||||
end
|
||||
|
||||
return ItemEventListener:AddCancelableCallback(self:GetItemID(), callbackFunction)
|
||||
self:ValidateForContinueOnItemLoad("ContinueWithCancelOnItemLoad", callbackFunction);
|
||||
return ItemEventListener:AddCancelableCallback(self:GetItemID(), callbackFunction);
|
||||
end
|
||||
|
||||
--[ Item Event Listener ]
|
||||
|
||||
ItemEventListener = CreateFrame("Frame")
|
||||
ItemEventListener.callbacks = {}
|
||||
|
||||
-- 1.12 OnEvent receives no function args; event name and payload live in
|
||||
-- the `event`, `arg1`, ... globals.
|
||||
ItemEventListener:SetScript("OnEvent", function()
|
||||
if event == "ITEM_DATA_LOAD_RESULT" then
|
||||
local itemID, success = arg1, arg2
|
||||
if success then
|
||||
ItemEventListener:FireCallbacks(itemID)
|
||||
else
|
||||
ItemEventListener:ClearCallbacks(itemID)
|
||||
end
|
||||
end
|
||||
end)
|
||||
ItemEventListener:RegisterEvent("ITEM_DATA_LOAD_RESULT")
|
||||
|
||||
local CANCELED_SENTINEL = -1
|
||||
|
||||
function ItemEventListener:AddCallback(itemID, callbackFunction)
|
||||
local callbacks = self:GetOrCreateCallbacks(itemID)
|
||||
table.insert(callbacks, callbackFunction)
|
||||
C_Item.RequestLoadItemDataByID(itemID)
|
||||
-- Generic aliases for use with ContinuableContainer
|
||||
function ItemMixin:ContinueWithCancelOnRecordLoad(callbackFunction)
|
||||
return self:ContinueWithCancelOnItemLoad(callbackFunction);
|
||||
end
|
||||
|
||||
function ItemEventListener:AddCancelableCallback(itemID, callbackFunction)
|
||||
local callbacks = self:GetOrCreateCallbacks(itemID)
|
||||
table.insert(callbacks, callbackFunction)
|
||||
C_Item.RequestLoadItemDataByID(itemID)
|
||||
|
||||
local index = table.getn(callbacks)
|
||||
return function()
|
||||
if table.getn(callbacks) > 0 and callbacks[index] ~= CANCELED_SENTINEL then
|
||||
callbacks[index] = CANCELED_SENTINEL
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function ItemEventListener:FireCallbacks(itemID)
|
||||
local callbacks = self:GetCallbacks(itemID)
|
||||
if callbacks then
|
||||
self:ClearCallbacks(itemID)
|
||||
for i, callback in ipairs(callbacks) do
|
||||
if callback ~= CANCELED_SENTINEL then
|
||||
xpcall(callback, ItemUtilErrorHandler)
|
||||
end
|
||||
end
|
||||
|
||||
for i = table.getn(callbacks), 1, -1 do
|
||||
callbacks[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ItemEventListener:ClearCallbacks(itemID)
|
||||
self.callbacks[itemID] = nil
|
||||
end
|
||||
|
||||
function ItemEventListener:GetCallbacks(itemID)
|
||||
return self.callbacks[itemID]
|
||||
end
|
||||
|
||||
function ItemEventListener:GetOrCreateCallbacks(itemID)
|
||||
local callbacks = self.callbacks[itemID]
|
||||
if not callbacks then
|
||||
callbacks = {}
|
||||
self.callbacks[itemID] = callbacks
|
||||
end
|
||||
return callbacks
|
||||
function ItemMixin:IsRecordDataCached()
|
||||
return self:IsItemDataCached();
|
||||
end
|
||||
|
||||
@@ -182,8 +182,14 @@ local function ResolveSubclasses(list, onLoaded)
|
||||
if subType and subType ~= "" then
|
||||
r.subclass = subType;
|
||||
elseif onLoaded then
|
||||
-- Not cached — re-derive when the item loads.
|
||||
Item:CreateFromItemID(r.createdItem):ContinueOnItemLoad(onLoaded);
|
||||
-- Not cached — re-derive when the item loads. The callback
|
||||
-- also runs when the load fails; re-deriving then would ask
|
||||
-- for the item again.
|
||||
Item:CreateFromItemID(r.createdItem):ContinueOnItemLoad(function(_, success)
|
||||
if success then
|
||||
onLoaded();
|
||||
end
|
||||
end);
|
||||
end
|
||||
end
|
||||
if r.subclass and not present[r.subclass] then
|
||||
@@ -526,8 +532,8 @@ local function CreateFrame_TradeSkillLink()
|
||||
local function itemInfo(id)
|
||||
local item = Item:CreateFromItemID(id);
|
||||
if not item:IsItemDataCached() then
|
||||
item:ContinueOnItemLoad(function()
|
||||
if self:IsShown() and self.selectedSpellID == entry.spellID then
|
||||
item:ContinueOnItemLoad(function(_, success)
|
||||
if success and self:IsShown() and self.selectedSpellID == entry.spellID then
|
||||
self:RenderDetail(entry);
|
||||
end
|
||||
end);
|
||||
|
||||
@@ -120,6 +120,28 @@ static int __fastcall Script_GetItemInfoInstant(void *L) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
// `C_Item.GetItemIDForItemInfo(itemInfo)` — the itemID alone, from the same
|
||||
// resolver `GetItemInfoInstant` echoes: an itemID, an `"item:N"` string or an
|
||||
// item link. ItemMixin:GetItemID falls back to it when GetItemInfoInstant
|
||||
// returns nothing. Returns nothing for input that names no item.
|
||||
static const Game::Doc::Field kGetItemIDForItemInfoArgs[] = {
|
||||
Game::Doc::Req("itemInfo", "ItemInfo"),
|
||||
};
|
||||
static const Game::Doc::Field kGetItemIDForItemInfoRets[] = {
|
||||
Game::Doc::Req("itemID", "number"),
|
||||
};
|
||||
static const Game::Doc::Function kGetItemIDForItemInfo{
|
||||
"Returns the item ID of an item ID, item string or item link.", kGetItemIDForItemInfoArgs,
|
||||
kGetItemIDForItemInfoRets};
|
||||
|
||||
static int __fastcall Script_GetItemIDForItemInfo(void *L) {
|
||||
const int itemID = Item::Arg::ResolveItemID(L, 1);
|
||||
if (itemID <= 0)
|
||||
return 0;
|
||||
Game::Lua::PushNumber(L, static_cast<double>(itemID));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Pushes the icon path for `itemID` and returns 1, or returns 0 (= nil
|
||||
// to Lua) if the item isn't cached, the displayInfoID is missing, or
|
||||
// the icon record's path slot is empty. Shared by all three
|
||||
@@ -315,6 +337,8 @@ static int __fastcall Script_C_Item_GetItemInfo(void *L) {
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemInfo", &Script_C_Item_GetItemInfo);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemInfoInstant", &Script_GetItemInfoInstant);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemIDForItemInfo",
|
||||
&Script_GetItemIDForItemInfo, &kGetItemIDForItemInfo);
|
||||
Game::Lua::RegisterGlobalFunction("GetItemIcon", &Script_GetItemIcon);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemIcon", &Script_C_Item_GetItemIcon);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemIconByID", &Script_C_Item_GetItemIconByID);
|
||||
|
||||
Reference in New Issue
Block a user