Trying to figure out why Addon API call is throwing an error

I’m writing an addon to export all of my characters’ gear info. I’ve had limited success with the use of GetItemInfo(itemLink), where the itemLevel doesn’t always come back correctly. MOST of the time it does, but there are odd instances where it comes back with “1” or “39” or something, on my Level 70 characters that are clearly not wearing low level stuff.

So I tried to switch to a hybrid of GetItemInfo(itemLink) and GetDetailedItemLevelInfo(itemLink). Note that itemLink comes from a call to GetInventoryItemLink(“player”, #).

Now, with that added in, every time I login, I get an LUA error that makes zero sense to me:

Message: Usage: local actualItemLevel, previewLevel, sparseItemLevel = C_Item.GetDetailedItemLevelInfo(itemInfo)
Time: Mon Apr 29 21:00:24 2024
Count: 2
Stack: Usage: local actualItemLevel, previewLevel, sparseItemLevel = C_Item.GetDetailedItemLevelInfo(itemInfo)
[string “=[C]”]: in function `GetDetailedItemLevelInfo’
[string “@Interface/AddOns/CharacterGearExporter/CharacterGearExporter.lua”]:58: in function <…dOns/CharacterGearExporter/CharacterGearExporter.lua:11>

Locals: (*temporary) = “Usage: local actualItemLevel, previewLevel, sparseItemLevel = C_Item.GetDetailedItemLevelInfo(itemInfo)”

I’ve no idea what this is trying to tell me. Strangely the data is still saved and written to SavedVariables even with this error.

Some added info: My addon subscribes to PLAYER_LOGIN, PLAYER_LOGOUT, ADDON_LOADED, and PLAYER_EQUIPMENT_CHANGED. Before I attempt to set the SavedVariables information, I wait to be sure that both LOGIN and LOADED has happened. But maybe that’s not enough? Is there some other event? I have a C_Timer, and wait 2 seconds even after those 2 events.

Interestingly, if I set that C_Timer to 30 seconds…I don’t seem to get the error. So it’s seems like a race condition of when item information is actually available after logging in?

The error is probably because the itemInfo you’re sending is nil.

Item information isn’t automatically in place when the game starts. For an item not already cached (requested), a request is sent to the server and then you have to wait until the server returns the information. For an example using Item.ContinueOnItemLoad see:

https://warcraft.wiki.gg/wiki/ItemMixin#ContinueOnItemLoad

Information on methods you can use to get information from the returned item (using the ContinueOnItemLoad method) see:

https://warcraft.wiki.gg/wiki/ItemMixin#Methods

1 Like

Thank you, I’ll take a look.

Edit…I’m not sure that’s the case:

for k,v in pairs(equipmentMap) do
    local itemLink = GetInventoryItemLink("player", k);

    if itemLink then
        local itemName, itemLink = GetItemInfo(itemLink)
        local actualItemLevel = GetDetailedItemLevelInfo(itemLink)
        
        equippedItems[v] = {}
        equippedItems[v]["itemName"] = itemName
        equippedItems[v]["itemLink"] = itemLink
        equippedItems[v]["itemLevel"] = actualItemLevel
    end
end

equipmentMap is a hard-coded key-value-pair, and I have a nil check to ensure that we have something

That said, you might be onto something with the ContinueOnItemLoad. If I reloadUI, or logout/login on the same character, then everything works. I’m just not sure how to fit this in with what I’m doing ATM. :smiley:

Edit 2:
Ok. Leeeroy. I thought I got the error to go away. I removed an offending “;”…and the error was gone the next time I loaded a character…but then it came back. Again, as noted, the actual SavedVariables has all off the expected data.

At this point, here are my files:

 ## Interface: 100206
 ## Title: Character Gear Exporter
 ## Notes: Character Gear Exporter Tool Thingy
 ## Version: 0.1
 ## Author: Kevin Wiegand
 ## SavedVariables: PlayerData
 
 CharacterGearExporter.lua

And

local frame = CreateFrame("Frame")

frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_LOGOUT")
frame:RegisterEvent("ADDON_LOADED")

local loaded = false
local loggedIn = false

local function SetData()
	if PlayerData == nil then
		PlayerData = {}
    end

	if PlayerData["characters"] == nil then
		PlayerData["characters"] = {}
    end

	characterName, characterRealm = UnitFullName("player")

	local characterIdentifierStart = characterName .. " - "
    local characterIdentifier = characterIdentifierStart .. characterRealm

	PlayerData["characters"][characterIdentifier] = {}

    overallItemLevel, equippedItemLevel = GetAverageItemLevel()

    PlayerData["characters"][characterIdentifier]["overallItemLevel"] = overallItemLevel
	PlayerData["characters"][characterIdentifier]["equippedItemLevel"] = equippedItemLevel

    local equipmentMap = {}

    equipmentMap[1] = "head"
    equipmentMap[2] = "neck"
    equipmentMap[3] = "shoulder"
    equipmentMap[5] = "chest"
    equipmentMap[6] = "waist"
    equipmentMap[7] = "legs"
    equipmentMap[8] = "feet"
    equipmentMap[9] = "wrist"
    equipmentMap[10] = "hands"
    equipmentMap[11] = "finger 1"
    equipmentMap[12] = "finger 2"
    equipmentMap[13] = "trinket 1"
    equipmentMap[14] = "trinket 2"
    equipmentMap[15] = "back"
    equipmentMap[16] = "main hand"
    equipmentMap[17] = "off hand"

	local equippedItems = {}

    for k,v in pairs(equipmentMap) do
        local itemLink = GetInventoryItemLink("player", k)

        if itemLink then
            local itemName, itemLink = GetItemInfo(itemLink)
            local actualItemLevel = GetDetailedItemLevelInfo(itemLink)
            
            equippedItems[v] = {}
            equippedItems[v]["itemName"] = itemName
            equippedItems[v]["itemLink"] = itemLink
            equippedItems[v]["itemLevel"] = actualItemLevel
        end
    end

    PlayerData["characters"][characterIdentifier]["equippedItems"] = equippedItems
    PlayerData["characters"][characterIdentifier]["updatedTime"] = time()
end

SLASH_CGE1 = "/cge"
SlashCmdList["CGE"] = function(msg)
    SetData()
    print("Your gear has been exported.")
end 

local function eventHandler(self, event, ...)
    if event == "PLAYER_LOGIN" then
		loggedIn = true
	end

    if event == "PLAYER_LOGOUT" then
		loggedIn = false
	end

    if event == "ADDON_LOADED" then
        loaded = true
    end

    if loggedIn and loaded then
        C_Timer.After(5, SetData)
    end
end

frame:SetScript("OnEvent", eventHandler)

This is a very rough change to your code to use as an example. The LogItem function (which just prints slot information) would be where you could save the current slot information to your SavedVariables.

As a note, SavedVariables data is global ie. part of the information shared by all addons AND the Blizzard UI. As such, the name should be something unique to you addon instead of PlayerData eg. addonname_DATA

local frame = CreateFrame("Frame")

frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_LOGOUT")
frame:RegisterEvent("ADDON_LOADED")

local loaded = false
local loggedIn = false

-- Start Addition
local function LogItem(item, slot, slotname)
	print(slotname, item:GetItemName(), "Lvl:", item:GetCurrentItemLevel())
end
-- End Addition

local function SetData()
	if PlayerData == nil then
		PlayerData = {}
    end

	if PlayerData["characters"] == nil then
		PlayerData["characters"] = {}
    end

	characterName, characterRealm = UnitFullName("player")

	local characterIdentifierStart = characterName .. " - "
    local characterIdentifier = characterIdentifierStart .. characterRealm

	PlayerData["characters"][characterIdentifier] = {}

    overallItemLevel, equippedItemLevel = GetAverageItemLevel()

    PlayerData["characters"][characterIdentifier]["overallItemLevel"] = overallItemLevel
	PlayerData["characters"][characterIdentifier]["equippedItemLevel"] = equippedItemLevel

    local equipmentMap = {}

    equipmentMap[1] = "head"
    equipmentMap[2] = "neck"
    equipmentMap[3] = "shoulder"
    equipmentMap[5] = "chest"
    equipmentMap[6] = "waist"
    equipmentMap[7] = "legs"
    equipmentMap[8] = "feet"
    equipmentMap[9] = "wrist"
    equipmentMap[10] = "hands"
    equipmentMap[11] = "finger 1"
    equipmentMap[12] = "finger 2"
    equipmentMap[13] = "trinket 1"
    equipmentMap[14] = "trinket 2"
    equipmentMap[15] = "back"
    equipmentMap[16] = "main hand"
    equipmentMap[17] = "off hand"

	local equippedItems = {}

    for k,v in pairs(equipmentMap) do

-- Start Addition
	local id = GetInventoryItemID("player", k)
	if id then
		local item = Item:CreateFromItemID(id)
		item:ContinueOnItemLoad(function() 
			LogItem(item, k, v) 
		end)
	end
-- End Addition

--[[	Start Commented out
        local itemLink = GetInventoryItemLink("player", k)

        if itemLink then
            local itemName, itemLink = GetItemInfo(itemLink)
            local actualItemLevel = GetDetailedItemLevelInfo(itemLink)
            
            equippedItems[v] = {}
            equippedItems[v]["itemName"] = itemName
            equippedItems[v]["itemLink"] = itemLink
            equippedItems[v]["itemLevel"] = actualItemLevel
        end
End Commented out ]]--
    end

    PlayerData["characters"][characterIdentifier]["equippedItems"] = equippedItems
    PlayerData["characters"][characterIdentifier]["updatedTime"] = time()
end

SLASH_CGE1 = "/cge"
SlashCmdList["CGE"] = function(msg)
    SetData()
    print("Your gear has been exported.")
end 

local function eventHandler(self, event, ...)
    if event == "PLAYER_LOGIN" then
		loggedIn = true
	end

    if event == "PLAYER_LOGOUT" then
		loggedIn = false
	end

    if event == "ADDON_LOADED" then
        loaded = true
    end

    if loggedIn and loaded then
        C_Timer.After(5, SetData)
    end
end

frame:SetScript("OnEvent", eventHandler)

A /reload doesn’t clear the cache, therefore it will be available when your character logs in. Logout/Login will clear the cache.

1 Like