diff --git a/DataStore_Inventory/API/GuildComm.lua b/DataStore_Inventory/API/GuildComm.lua index ed9ab8a..523b807 100644 --- a/DataStore_Inventory/API/GuildComm.lua +++ b/DataStore_Inventory/API/GuildComm.lua @@ -25,23 +25,9 @@ local function GetThisGuild() return guildID and guilds[guildID] end -local function GetMemberKey(guild, member) - -- returns the appropriate key to address a guild member. - -- Either it's a known alt ==> point to the characters table - -- Or it's a guild member ==> point to the guild table - local main = DataStore:GetNameOfMain(member) - - if main and main == UnitName("player") then - local key = format("%s.%s.%s", DataStore.ThisAccount, DataStore.ThisRealm, member) - local id = DataStore:GetCharacterID(key) - - return DataStore_Inventory_Characters[id] - end - - if not guild or not guild.Members then - return nil - end - return guild.Members[member] +local function GetGuildMemberInfo(guild, member) + -- data received over the guild channel, for a member who broadcast something about himself + return guild and guild.Members and guild.Members[member] end local function GetAIL(alts) @@ -51,7 +37,11 @@ local function GetAIL(alts) local character = DataStore:GetCharacter() -- this character local ail = DataStore:GetAverageItemLevel(character) - TableInsert(out, format("%s:%d", UnitName("player"), ail)) + + -- a nil item level would error out of format(), taking the whole broadcast with it + if ail then + TableInsert(out, format("%s:%d", UnitName("player"), ail)) + end if strlen(alts) > 0 then for _, name in pairs( { strsplit("|", alts) }) do -- then all his alts @@ -105,7 +95,7 @@ local function _RequestGuildMemberEquipment(member) if not main then -- player is offline, check if his equipment is in the DB local thisGuild = GetThisGuild() - if thisGuild and thisGuild.Members[member] then -- player found + if thisGuild and thisGuild.Members and thisGuild.Members[member] then -- player found if thisGuild.Members[member].Inventory then -- equipment found AddonFactory:Broadcast("DATASTORE_PLAYER_EQUIPMENT_RECEIVED", player, member) return @@ -131,20 +121,33 @@ local function _RequestGuildMemberEquipment(member) DataStore:GuildWhisper(commPrefix, main, MSG_EQUIPMENT_REQUEST, member) end +--[[ + A guild member may be one of our own characters: the one currently logged in, or an alt in the + same guild. Those are answered from our own data, which is always there and always fresher than + anything the guild channel may have carried. Everyone else is answered from the guild table. + Note that we resolve the name through DataStore:GetCharacter() rather than asking + GetNameOfMain() who the member belongs to: that one answers from the guild alts broadcast, so + until that arrives it does not even recognize ourselves. +--]] local function _GetGuildMemberInventoryItem(guild, member, slotID) - local character = GetMemberKey(guild, member) - + local character = DataStore:GetCharacter(member) -- this realm, this account if character then - return character.Inventory[slotID] + return DataStore:GetInventoryItem(character, slotID) end + + -- a member known only through his item level broadcast has no inventory yet + local info = GetGuildMemberInfo(guild, member) + return info and info.Inventory and info.Inventory[slotID] end local function _GetGuildMemberAverageItemLevel(guild, member) - local character = GetMemberKey(guild, member) - + local character = DataStore:GetCharacter(member) -- this realm, this account if character then - return character.averageItemLvl + return (DataStore:GetAverageItemLevel(character)) end + + local info = GetGuildMemberInfo(guild, member) + return info and info.averageItemLvl end @@ -182,6 +185,8 @@ local commCallbacks = { [MSG_EQUIPMENT_TRANSFER] = function(sender, character, equipment) local thisGuild = GetThisGuild() if thisGuild then + thisGuild.Members = thisGuild.Members or {} + thisGuild.Members[character] = thisGuild.Members[character] or {} thisGuild.Members[character].Inventory = equipment thisGuild.Members[character].lastUpdate = time() AddonFactory:Broadcast("DATASTORE_PLAYER_EQUIPMENT_RECEIVED", sender, character) diff --git a/DataStore_Inventory/DataStore_Inventory.lua b/DataStore_Inventory/DataStore_Inventory.lua index 3fd5250..9302edc 100644 --- a/DataStore_Inventory/DataStore_Inventory.lua +++ b/DataStore_Inventory/DataStore_Inventory.lua @@ -43,18 +43,20 @@ local function ScanAverageItemLevel() char.lastUpdate = time() char.overallAIL = 0 - -- GetAverageItemLevel only exists in retail + -- GetAverageItemLevel does not exist on every version, and where it does it may still return + -- zeroes (Classic Era). Only trust it when it answers with usable values, fall through to the + -- manual calculation otherwise, or the character ends up with no item level at all. if type(GetAverageItemLevel) == "function" then local overallAiL, AiL = GetAverageItemLevel() if overallAiL and AiL and overallAiL > 0 and AiL > 0 then char.overallAIL = overallAiL char.averageItemLvl = AiL + return end - return end - - -- if we get here, GetAverageItemLevel does not exist, we must calculate manually. + + -- if we get here, GetAverageItemLevel is missing or unusable, we must calculate manually. local totalItemLevel = 0 local itemCount = 0 @@ -91,13 +93,19 @@ local function ScanInventorySlot(slot) local link = GetInventoryItemLink("player", slot) local currentContent = inventory[slot] - - if link then + + if link then if IsEnchanted(link) then -- if there's an enchant, save the full link inventory[slot] = link else -- .. otherwise, only save the id inventory[slot] = tonumber(link:match("item:(%d+)")) end + elseif GetInventoryItemID("player", slot) then + -- The slot holds an item, but its link cannot be built yet because the item is not in the + -- client cache (typically the first login after a patch, when the cache was invalidated). + -- Save the id rather than wiping a slot that is not actually empty. A later rescan, once + -- the cache is warm, replaces it with the full link if the item is enchanted. + inventory[slot] = GetInventoryItemID("player", slot) else inventory[slot] = nil end @@ -332,10 +340,12 @@ local function OnPlayerAlive() if isRetail then ScanTransmogSets() - - -- Scan again after 5 seconds, no less, to ensure that item info has been properly updated. - C_Timer.After(5, ScanInventory) end + + -- Scan again after 5 seconds, no less, to ensure that item info has been properly updated. + -- Needed on every version, not just retail : the first scan runs early enough that the item + -- cache may still be cold, which is what leaves slots unresolved right after a patch. + C_Timer.After(5, ScanInventory) end local function OnPlayerEquipmentChanged(event, slot)