From 87236a421791e5e2d2332eef02ff4cbdbc4310be Mon Sep 17 00:00:00 2001 From: Aleksi Blinnikka Date: Tue, 20 Mar 2018 14:28:15 +0200 Subject: [PATCH 1/1] Refactor OmaCD, create a more robust inspect scheme WoW inspect API doesn't work well, these changes try to work around the issues and hopefully provide requested info. Spec/talents are inspected fine, but inventory inspection requires luck and retries. --- OmaCD/Cooldowns.lua | 224 ++++++++++++++++++++++---------------------- OmaCD/Inspect.lua | 182 +++++++++++++++++++++++++++++++++++ OmaCD/OmaCD.toc | 1 + 3 files changed, 296 insertions(+), 111 deletions(-) create mode 100644 OmaCD/Inspect.lua diff --git a/OmaCD/Cooldowns.lua b/OmaCD/Cooldowns.lua index 916a5a0..3663615 100644 --- a/OmaCD/Cooldowns.lua +++ b/OmaCD/Cooldowns.lua @@ -13,7 +13,6 @@ local UnitIsDeadOrGhost, UnitIsConnected = UnitIsDeadOrGhost, UnitIsConnected; local GetTalentInfo, GetInspectSpecialization = GetTalentInfo, GetInspectSpecialization; local GetSpecializationInfo, GetSpecialization = GetSpecializationInfo, GetSpecialization; local GetInventoryItemLink, GetInventoryItemID = GetInventoryItemLink, GetInventoryItemID; -local CanInspect, NotifyInspect, ClearInspectPlayer = CanInspect, NotifyInspect, ClearInspectPlayer; local INVSLOT_MAINHAND, INVSLOT_WRIST = INVSLOT_MAINHAND, INVSLOT_WRIST; local upColor = {0, 0.7, 0.2, 0.7}; @@ -68,8 +67,8 @@ local runningcds = {}; -- CDs currently active local idToGuid = {}; local guidToId = {}; local guidToSpecid = {}; +local monks = {}; local monksWithLegendary = {}; -local inspectSent = {}; local dead = {}; local disconnected = {}; @@ -88,16 +87,6 @@ local function CdDown(guid, spellid, start) if start then frame.text:SetText(cds[guid][spellid]) end end -local function sendInspect(guid, id, func) - if not inspectSent[guid] and CanInspect(id) then - inspectSent[guid] = true; - local _, _, _, _, _, name = GetPlayerInfoByGUID(guid); - print(guid, name, "send inspect"); - NotifyInspect(id); - CTimerAfter(20, func); -- precaution for never receiving INSPECT_READY - end -end - local encounter = nil; local function tick() if not encounter and IsEncounterInProgress() then @@ -141,7 +130,12 @@ end local function removeFrame(frame) unused[#unused+1] = frame; frames[frame.guid][frame.spellid] = nil; + if not next(frames[frame.guid]) then frames[frame.guid] = nil end cds[frame.guid][frame.spellid] = nil; + monks[frame.guid] = nil; + monksWithLegendary[frame.guid] = nil; + dead[frame.guid] = nil; + disconnected[frame.guid] = nil; if runningcds[frame.guid] then runningcds[frame.guid][frame.spellid] = nil end tremove(shown, frame.pos); for i, frame in ipairs(shown) do @@ -209,47 +203,45 @@ local function updateUnitCD(guid) end end -local function updateMonk(guid) +local function updateMonk(guid, _, _, weapon, wrist) local id = guidToId[guid]; - if not id then return end -- called because another addon inspected - local weapon = GetInventoryItemLink(id, INVSLOT_MAINHAND); - if not weapon then - -- try again directly - if CanInspect(id) then - print(guid, "send inspect");NotifyInspect(id); - CTimerAfter(20, updateMonk); + if id then + local _, _, _, relic1, relic2, relic3 = strsplit(":", weapon); + local cdfix = 0; + if monkRelics[relic1] then cdfix = cdfix + monkRelics[relic1] end + if monkRelics[relic2] then cdfix = cdfix + monkRelics[relic2] end + if monkRelics[relic3] then cdfix = cdfix + monkRelics[relic3] end + if cdfix ~= 0 then + cdfixes[guid] = cdfix; + else + cdfixes[guid] = nil; end - return; + + if wrist == 137096 then -- Petrichor Lagniappe + monksWithLegendary[guid] = true; + else + monksWithLegendary[guid] = nil; + end + monks[guid] = true; + updateUnitCD(guid); end - if inspectSent[guid] then inspectSent[guid] = nil end - print("Got monk", guid); - local _, _, _, relic1, relic2, relic3 = strsplit(":", weapon); - local cdfix = 0; - if monkRelics[relic1] then cdfix = cdfix + monkRelics[relic1] end - if monkRelics[relic2] then cdfix = cdfix + monkRelics[relic2] end - if monkRelics[relic3] then cdfix = cdfix + monkRelics[relic3] end - if cdfix ~= 0 then cdfixes[guid] = cdfix end +end - local wrist = GetInventoryItemID(guidToId[guid], INVSLOT_WRIST); - if wrist == 137096 then -- Petrichor Lagniappe - monksWithLegendary[guid] = true; - else - monksWithLegendary[guid] = nil; +-- a secondary tick for monks inventory check out of combat +local function monkTick() + if not InCombatLockdown() then + for guid, _ in pairs(monks) do + local id = guidToId[guid]; + if id then OmaInspect.Request(guid, id, updateMonk) end + end end - updateUnitCD(guid); + CTimerAfter(10, monkTick); end -local function updateDruid(guid, player) +local function updateDruid(guid, _, talent) local id = guidToId[guid]; if id then - -- Inner Peace talent - local selected; - if player then - _, _, _, selected = GetTalentInfo(6, 2, 1); - else - _, _, _, selected = GetTalentInfo(6, 2, 1, true, id); - end - if selected then + if talent then cdfixes[guid] = -60; else cdfixes[guid] = nil; @@ -258,19 +250,14 @@ local function updateDruid(guid, player) end end -local function updatePriest(guid) +local function updatePriest(guid, specid) local id = guidToId[guid]; if id then - local specid = GetInspectSpecialization(id); - if specid == 0 then - -- try again directly - if CanInspect(id) then - print(guid, "send inspect");NotifyInspect(id); - CTimerAfter(20, updatePriest); + if guidToSpecid[guid] ~= specid then + for _, frame in pairs(frames[guid]) do + removeFrame(frame); end end - if inspectSent[guid] then inspectSent[guid] = nil end - print("Got priest", guid); guidToSpecid[guid] = specid; updateUnitCD(guid); end @@ -283,9 +270,12 @@ local function updatePlayer() guidToId[guid] = "player"; guidToSpecid[guid] = specid; if specid == 270 then -- Monk - updateMonk(guid); + local weapon = GetInventoryItemLink("player", INVSLOT_MAINHAND); + local wrist = GetInventoryItemID("player", INVSLOT_WRIST); + updateMonk(guid, nil, nil, weapon, wrist); elseif specid == 105 then -- Druid - updateDruid(guid, true); + local _, _, _, talent = GetTalentInfo(6, 2, 1); + updateDruid(guid, nil, talent); else updateUnitCD(guid); end @@ -306,29 +296,27 @@ local function updateUnitid(id) local _, class = UnitClass(id); if specs[class] ~= nil then guidToId[guid] = id; + local specid = guidToSpecid[guid]; + guidToSpecid[guid] = specs[class]; if specs[class] == false then - -- Priest, have to inspect to get spec, try immediately, if not available, inspect - local specid = GetInspectSpecialization(id); - if specid == 0 then - sendInspect(guid, id, updatePriest); - elseif specid == 256 or specid == 257 then - guidToSpecid[guid] = specid; -- only Holy and Discipline - updateUnitCD(guid); + if specid == 256 or specid == 257 then + guidToSpecid[guid] = specid; + else + guidToSpecid[guid] = 257; -- assume Holy for now to get something visible end + updateUnitCD(guid); + OmaInspect.Request(guid, id, updatePriest); elseif specs[class] == 270 then -- Monk, have to check inventory for CD modifications - guidToSpecid[guid] = specs[class]; -- updateUnitCD (without having fixes yet) updateUnitCD(guid); - sendInspect(guid, id, updateMonk); + OmaInspect.Request(guid, id, updateMonk, true); elseif specs[class] == 105 then -- Druid, have to inspect to get talents - guidToSpecid[guid] = specs[class]; -- updateUnitCD (without having fixes yet) updateUnitCD(guid); - sendInspect(guid, id, updateDruid); + OmaInspect.Request(guid, id, updateDruid); else - guidToSpecid[guid] = specs[class]; updateUnitCD(guid); end idToGuid[id] = guid; @@ -343,19 +331,15 @@ local function updateUnitid(id) end end +local prevGroupType = "solo"; +local prevGroupSize = 0; local updateQueued = nil; local function updateUnitids() local size = 0; - local prefix; - local newIdToGuid = {}; - local prevIdToGuid = {}; + local prefix = "solo"; guidToId = {}; updateQueued = nil; - for id, guid in pairs(idToGuid) do - prevIdToGuid[id] = guid; - end - if IsInGroup() then if IsInRaid() then size = 40; @@ -366,21 +350,53 @@ local function updateUnitids() end end + if prevGroupType ~= prefix then + -- clean up players from old group + for i = 1,prevGroupSize do + local id = format("%s%i", prevGroupType, i); + local guid = idToGuid[id]; + idToGuid[id] = nil; + if frames[guid] then + print(guid, "removing frames in overall updateUnitids"); + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end + end + end + prevGroupType = prefix; + prevGroupSize = size; + end + updatePlayer(); - if prevIdToGuid["player"] then prevIdToGuid["player"] = nil end for i = 1,size do local id = format("%s%i", prefix, i); - if not UnitIsUnit(id, "player") then + if not UnitIsUnit(id, "player") then -- player is already done updateUnitid(id); end - if prevIdToGuid[id] then prevIdToGuid[id] = nil end end +end - -- clean up players leaving - for id, guid in pairs(prevIdToGuid) do - if frames[guid] then - for _, frame in pairs(frames[guid]) do - removeFrame(frame); +-- just check removals, don't cause inspects +local function fastUpdateUnitids() + local size = 0; + local prefix = "solo"; + if IsInGroup() then + if IsInRaid() then + size = 40; + prefix = "raid"; + else + size = 4; + prefix = "party"; + end + end + for i = 1,size do + local id = format("%s%i", prefix, i); + if not UnitExists(id) or UnitGroupRolesAssigned(id) ~= "HEALER" then + idToGuid[id] = nil; + if frames[guid] then + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end end end end @@ -426,47 +442,34 @@ local events = { if runningcds[guid] and runningcds[guid][115310] then runningcds[guid][115310] = runningcds[guid][115310] - 2; end + elseif guidToSpecid[guid] == 257 and spellid == 62618 then + -- assumed Holy priest cast Barrier, set to update to Disc + guidToSpecid[guid] = 256; + for _, frame in pairs(frames[guid]) do + removeFrame(frame); + end + updateUnitCD(guid); + CdDown(guid, spellid, GetTime()); end end end, ["PLAYER_SPECIALIZATION_CHANGED"] = function(id) updateUnitid(id); end, - ["UNIT_INVENTORY_CHANGED"] = function(id) - local guid = idToGuid[id]; - if guid and guidToSpecid[guid] == 270 then - print("OmaCD Inventory: Monk inventory update"); - updateMonk(guid); - end - end, ["GROUP_ROSTER_UPDATE"] = function() + fastUpdateUnitids(); if not updateQueued then updateQueued = true - CTimerAfter(2, updateUnitids); - end - end, - ["INSPECT_READY"] = function(guid) - local specid = guidToSpecid[guid]; - if inspectSent[guid] then inspectSent[guid] = nil end - if specid == 105 then -- Druid - print("OmaCD Inspect: druid,", guid); - updateDruid(guid); - ClearInspectPlayer(); - elseif specid == 270 then -- Monk - print("OmaCD Inspect: monk,", guid); - -- don't clear inspect player, have to track inventory changes - elseif specid == false or specid == 256 or specid == 257 then -- Priest - print("OmaCD Inspect: priest,", guid); - updatePriest(guid); - ClearInspectPlayer(); + CTimerAfter(4, updateUnitids); end end, }; events["PLAYER_ROLES_ASSIGNED"] = events["GROUP_ROSTER_UPDATE"]; +events["PLAYER_ENTERING_WORLD"] = events["GROUP_ROSTER_UPDATE"]; local function cdtracker() cdframe:SetFrameStrata("LOW"); - cdframe:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 200, -200); + cdframe:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 580, -400); cdframe:SetWidth(1); cdframe:SetHeight(1); @@ -478,13 +481,12 @@ local function cdtracker() cdframe:RegisterEvent("UNIT_CONNECTION"); cdframe:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED"); cdframe:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED"); - cdframe:RegisterEvent("UNIT_INVENTORY_CHANGED"); cdframe:RegisterEvent("GROUP_ROSTER_UPDATE"); - cdframe:RegisterEvent("INSPECT_READY"); cdframe:RegisterEvent("PLAYER_ROLES_ASSIGNED"); - -- initial update - events["GROUP_ROSTER_UPDATE"](); + cdframe:RegisterEvent("PLAYER_ENTERING_WORLD"); + -- initial tick CTimerAfter(0.5, tick); + CTimerAfter(10, monkTick); end cdframe:RegisterEvent("PLAYER_LOGIN"); diff --git a/OmaCD/Inspect.lua b/OmaCD/Inspect.lua new file mode 100644 index 0000000..498ea06 --- /dev/null +++ b/OmaCD/Inspect.lua @@ -0,0 +1,182 @@ +-- Inspect.lua +local _; +local next, print = next, print; +local format = string.format; +local InCombatLockdown = InCombatLockdown; +local CTimerAfter = C_Timer.After; +local IsInGroup, IsInRaid = IsInGroup, IsInRaid; +local CheckInteractDistance = CheckInteractDistance; +local GetTime, UnitGUID, UnitName = GetTime, UnitGUID, UnitName; +local CanInspect, NotifyInspect, ClearInspectPlayer = CanInspect, NotifyInspect, ClearInspectPlayer; +local GetInventoryItemLink, GetInventoryItemID = GetInventoryItemLink, GetInventoryItemID; +local GetTalentInfo, GetInspectSpecialization = GetTalentInfo, GetInspectSpecialization; +local INVSLOT_MAINHAND, INVSLOT_WRIST = INVSLOT_MAINHAND, INVSLOT_WRIST; + +local cache = {}; +local pending = {}; +local inspecting = nil; +local inspectui = nil; +local frame = CreateFrame("Frame", "OmaInspect"); +frame:Hide(); +OmaInspect = {}; + +local redoGuid = nil; +local function redo() + if redoGuid and inspecting == redoGuid then + print("hit redo timeout, putting back on pending"); + pending[redoGuid] = true; + inspecting = nil; + redoGuid = nil; + if not inspectui then ClearInspectPlayer() end + end +end + +local function inspect(id) + local guid = UnitGUID(id); + if not guid then return end + if InCombatLockdown() then + pending[guid] = true; + elseif not inspectui then + -- not necessary, but there's too many issues with inspect, + -- try to hope being nearby makes it more likely to work + -- no need to inspect people not in range before pull anyway + if CheckInteractDistance(id, 1) then + if not inspecting then + inspecting = guid; + print("Sending inspect", UnitName(id)); + NotifyInspect(id); + redoGuid = guid; + CTimerAfter(20, redo); + else + pending[guid] = true; + end + else + pending[guid] = true; + end + else + pending[guid] = true; + end +end + +hooksecurefunc("InspectUnit", function() + inspectui = true; +end); +hooksecurefunc("NotifyInspect", function(id) + if inspecting ~= UnitGUID(id) then + inspectui = true; + end +end); +hooksecurefunc("ClearInspectPlayer", function() + inspectui = nil; +end); + +local function tick() + -- make sure pending inspects get done at some point + if not InCombatLockdown() then + local guid, _ = next(pending); + if guid and cache[guid] and cache[guid].id then + pending[guid] = nil; + inspect(cache[guid].id); + end + end + CTimerAfter(2, tick); +end + +function OmaInspect.Request(guid, id, callback, needItems, force) + if not id or not CanInspect(id) then return false end + if not guid or UnitGUID(id) ~= guid then return false end + + if not cache[guid] then cache[guid] = {} end + local cached = cache[guid]; + if not cached.time then cached.time = 0 end + cached.id = id; + cached.cb = callback; + cached.needItems = needItems; + local elapsed = GetTime() - cached.time; + if force or (not needItems and (not cached.talent ~= nil or elapsed > 60)) or + (needItems and (not cached.weapon or elapsed > 60)) then + if not pending[guid] or inspecting ~= guid then -- don't send multiple inspects + inspect(id); + end + else + cached.cb(guid, cached.spec, cached.talent, cached.weapon, cached.wrist); + end + return true; +end + +local function inspectReady(guid) + if not guid or inspectui then return end + local cached = cache[guid]; + if cached and cached.id and UnitGUID(cached.id) == guid then + local time = GetTime(); + local id = cached.id; + inspecting = nil; + redoGuid = nil; + if InCombatLockdown() and not inspectui then + pending[guid] = true; + ClearInspectPlayer(); + else + if CanInspect(id) then + if time - cached.time > 60 then -- refresh >1min + -- new inspect + cached.time = time; + if needItems then + cached.weapon = GetInventoryItemLink(id, INVSLOT_MAINHAND); + cached.wrist = GetInventoryItemID(id, INVSLOT_WRIST); + end + cached.spec = GetInspectSpecialization(id); + local _, _, _, selected = GetTalentInfo(6, 2, 1, true, id); + cached.talent = selected; + end + end + ClearInspectPlayer(); + if (cached.needItems and cached.weapon) or + (not cached.needItems and cached.talent ~= nil) then + print("Got inspect done", UnitName(id)); + cached.cb(guid, cached.spec, cached.talent, cached.weapon, cached.wrist); + else + pending[guid] = true; + end + end + end +end + +local function updateIds() + local size = 0; + local prefix = "solo"; + if IsInGroup() then + if IsInRaid() then + size = 40; + prefix = "raid"; + else + size = 4; + prefix = "party"; + end + end + for i = 1,size do + local id = format("%s%i", prefix, i); + local guid = UnitGUID(id); + if cache[guid] then cache[guid].id = id end + end +end + +local function create() + frame:UnregisterAllEvents(); + frame:SetScript("OnEvent", function(self, event, guid) + if event == "INSPECT_READY" then + inspectReady(guid); + elseif event == "GROUP_ROSTER_UPDATE" then + updateIds(); + end + end); + frame:RegisterEvent("INSPECT_READY"); + frame:RegisterEvent("GROUP_ROSTER_UPDATE"); + tick(); +end + +frame:RegisterEvent("PLAYER_LOGIN"); +frame:SetScript("OnEvent", function(self, event) + if event == "PLAYER_LOGIN" then + return create(); + end +end); diff --git a/OmaCD/OmaCD.toc b/OmaCD/OmaCD.toc index 6e1a15e..6182dd3 100644 --- a/OmaCD/OmaCD.toc +++ b/OmaCD/OmaCD.toc @@ -5,4 +5,5 @@ ## Notes: My CD tracker Bres.lua +Inspect.lua Cooldowns.lua -- 2.39.5