Fixes for merged harm/'help with misc and concurrent getiteminfo #12


Open
  • TorakTheUnprepared created this issue Nov 4, 2019

    I have made a change to your released (4.3.2) version of LibRangeCheck to solve two issues:

    1. Merge the 'misc' checks with both harmful and friendly checks.  Otherwise you can/will end up with situations where harm/help will be missing ranges that DO exist under misc.  eg. 8 yards, 28 yards.

    2. Added GetXRanges() functions, so I can get a list of all the transition points (so I can put them in a dropdown menu).

    3. Allow concurrent GetItemInfo requests (by default, unless CacheAll is enabled, one per range for harm and help).  This will speed up the time to getting useful range spells (which makes a difference, otherwise it can take far too long to get an accurate list of range spells).

     

    I am using this for range-based conditions in Rotation Master, thanks for writing it so I can pull out my bodge job of attempting to do the same.  I don't know how to attach a file with this issue system, so here is the diff.

     

    --- LibRangeCheck-2.0.lua.orig  2019-11-04 04:13:25.754001100 -0500
    +++ LibRangeCheck-2.0.lua       2019-11-04 05:17:33.797220600 -0500
    @@ -110,6 +110,7 @@
     FriendSpells["HUNTER"] = {}
     HarmSpells["HUNTER"] = {
         75, -- ["Auto Shot"], -- 40
    +    1495, -- ["Mongoose Bite"], -- Melee
     }
    
     FriendSpells["MAGE"] = {
    @@ -383,6 +384,7 @@
     local setmetatable = setmetatable
     local tonumber = tonumber
     local pairs = pairs
    +local ipairs = ipairs
     local tostring = tostring
     local print = print
     local next = next
    @@ -416,8 +418,7 @@
    
     -- temporary stuff
    
    -local pendingItemRequest
    -local itemRequestTimeoutAt
    +local itemRequestTimeoutAt = {}
     local foundNewItems
     local cacheAllItems
     local friendItemRequests
    @@ -621,6 +622,27 @@
         end
     end
    
    +local function ranges(array)
    +    local vals = {}
    +    for k,v in pairs(array) do
    +        if v.range == 0 then
    +            vals[MeleeRange] = 1
    +        else
    +            vals[v.range] = 1
    +        end
    +        if v.minRange then
    +            vals[v.minRange] = 1
    +        end
    +    end
    +    local rv = {}
    +    for k,v in pairs(vals) do
    +        table.insert(rv, k)
    +    end
    +    -- Sort the keys
    +    table.sort(rv, function (lhs, rhs) return lhs > rhs end)
    +    return rv
    +end
    +
     local function getMinChecker(checkerList, range)
         local checker, checkerRange
         for i = 1, #checkerList do
    @@ -719,6 +741,42 @@
         return minRange .. " - " .. maxRange
     end
    
    +local function mergeCheckers(target, source)
    +    local i,j = 1, 1
    +
    +    while i <= #target and j <= #source do
    +        if source[j].range == target[i].range then
    +            if target[i].minRange == source[j].minRange then
    +                -- Both range and minRanges match, skip
    +                j = j + 1
    +            elseif target[i].minRange and (not source[j].minRange or
    +                    sourece[j].minRange > target[i].minRange) then
    +                -- Same range, but lower minRange, insert / advance
    +                table.insert(target, i, source[j])
    +                i = i + 1
    +                j = j + 1
    +            else
    +                -- Same range, but higher minRange, advance i (target) to insert after this one
    +                i = i + 1
    +            end
    +        elseif source[j].range > target[i].range then
    +            -- Lower range, insert here.
    +            table.insert(target, i, source[j])
    +            i = i + 1
    +            j = j + 1
    +        else
    +            -- Higher range, advance i (target) to insert after this one
    +            i = i + 1
    +        end
    +
    +    end
    +
    +    while j <= #source do
    +        table.insert(target, source[j])
    +        j = j + 1
    +    end
    +end
    +
     -- initialize RangeCheck if not yet initialized or if "forced"
     function lib:init(forced)
         if self.initialized and (not forced) then
    @@ -778,13 +836,15 @@
         local interactList = InteractLists[playerRace] or DefaultInteractList
         self.handSlotItem = GetInventoryItemLink("player", HandSlotId)
         local changed = false
    -    if updateCheckers(self.friendRC, createCheckerList(FriendSpells[playerClass], FriendItems, interactList)) then
    +    if updateCheckers(self.miscRC, createCheckerList(nil, nil, interactList)) then
             changed = true
         end
    -    if updateCheckers(self.harmRC, createCheckerList(HarmSpells[playerClass], HarmItems, interactList)) then
    +    if updateCheckers(self.friendRC, createCheckerList(FriendSpells[playerClass], FriendItems, interactList)) then
    +        mergeCheckers(self.friendRC, self.miscRC)
             changed = true
         end
    -    if updateCheckers(self.miscRC, createCheckerList(nil, nil, interactList)) then
    +    if updateCheckers(self.harmRC, createCheckerList(HarmSpells[playerClass], HarmItems, interactList)) then
    +        mergeCheckers(self.harmRC, self.miscRC)
             changed = true
         end
         if changed and self.callbacks then
    @@ -807,6 +867,21 @@
         return rcIterator(self.miscRC)
     end
    
    +--- Return an array of range boundaries available for friendly units.
    +function lib:GetFriendRanges()
    +    return ranges(self.friendRC)
    +end
    +
    +--- Return an array of range boundaries available for enemy units.
    +function lib:GetHarmRanges()
    +    return ranges(self.harmRC)
    +end
    +
    +--- Return an array of range boundaries available for miscellaneous units.  These units are neither enemy nor friendly, such as people in sanctuaries or corpses.
    +function lib:GetMiscRanges()
    +    return ranges(self.miscRC)
    +end
    +
     --- Return a checker suitable for out-of-range checking on friendly units, that is, a checker whose range is equal or larger than the requested range.
     -- @param range the range to check for.
     -- @return **checker**, **range** pair or **nil** if no suitable checker is available. **range** is the actual range the returned **checker** checks for.
    @@ -970,8 +1045,7 @@
    
     function lib:GET_ITEM_INFO_RECEIVED(event, item, success)
         -- print("### GET_ITEM_INFO_RECEIVED: " .. tostring(item) .. ", " .. tostring(success))
    -    if item == pendingItemRequest then
    -        pendingItemRequest = nil
    +    if itemRequestTimeoutAt[item] then
             if not success then
                 self.failedItemRequests[item] = true
             end
    @@ -980,76 +1054,90 @@
     end
    
     function lib:processItemRequests(itemRequests)
    -    while true do
    -        local range, items = next(itemRequests)
    +    local waiting
    +    for range, items in pairs(itemRequests) do
             if not range then return end
    -        while true do
    -            local i, item = next(items)
    -            if not i then
    -                itemRequests[range] = nil
    -                break
    -            elseif self.failedItemRequests[item] then
    -                -- print("### processItemRequests: failed: " .. tostring(item))
    -                tremove(items, i)
    -            elseif item == pendingItemRequest and GetTime() < itemRequestTimeoutAt then
    -                return true; -- still waiting for server response
    -            elseif GetItemInfo(item) then
    -                -- print("### processItemRequests: found: " .. tostring(item))
    -                if itemRequestTimeoutAt then
    -                    -- print("### processItemRequests: new: " .. tostring(item))
    -                    foundNewItems = true
    -                    itemRequestTimeoutAt = nil
    -                    pendingItemRequest = nil
    -                end
    -                if not cacheAllItems then
    -                    itemRequests[range] = nil
    +        if not items then
    +            itemRequests[range] = nil
    +        else
    +            local count = 0
    +            for i, item in ipairs(items) do
    +                if count > 0 and not cacheAllItems then
                         break
                     end
    -                tremove(items, i)
    -            elseif not itemRequestTimeoutAt then
    -                -- print("### processItemRequests: waiting: " .. tostring(item))
    -                itemRequestTimeoutAt = GetTime() + ItemRequestTimeout
    -                pendingItemRequest = item
    -                if not self.frame:IsEventRegistered("GET_ITEM_INFO_RECEIVED") then
    -                    self.frame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
    +                local localWaiting
    +                if self.failedItemRequests[item] then
    +                    -- print("### processItemRequests: failed: " .. tostring(item))
    +                    tremove(items, i)
    +                elseif itemRequestTimeoutAt[item] and GetTime() < itemRequestTimeoutAt[item] then
    +                    localWaiting = true
    +                elseif GetItemInfo(item) then
    +                    -- print("### processItemRequests: found: " .. tostring(item))
    +                    if itemRequestTimeoutAt[item] then
    +                        -- print("### processItemRequests: new: " .. tostring(item))
    +                        foundNewItems = true
    +                        itemRequestTimeoutAt[item] = nil
    +                    end
    +                    if not cacheAllItems then
    +                        itemRequests[range] = nil
    +                        break
    +                    end
    +                    tremove(items, i)
    +                elseif not itemRequestTimeoutAt[item] then
    +                    -- print("### processItemRequests: waiting: " .. tostring(item))
    +                    itemRequestTimeoutAt[item] = GetTime() + ItemRequestTimeout
    +                    localWaiting = true
    +                    if not self.frame:IsEventRegistered("GET_ITEM_INFO_RECEIVED") then
    +                        self.frame:RegisterEvent("GET_ITEM_INFO_RECEIVED")
    +                    end
    +                elseif GetTime() >= itemRequestTimeoutAt[item] then
    +                    -- print("### processItemRequests: timeout: " .. tostring(item))
    +                    if cacheAllItems then
    +                        print(MAJOR_VERSION .. ": timeout for item: " .. tostring(item))
    +                    end
    +                    self.failedItemRequests[item] = true
    +                    itemRequestTimeoutAt[item] = nil
    +                    tremove(items, i)
    +                else
    +                    localWaiting = true
                     end
    -                return true
    -            elseif GetTime() >= itemRequestTimeoutAt then
    -                -- print("### processItemRequests: timeout: " .. tostring(item))
    -                if cacheAllItems then
    -                    print(MAJOR_VERSION .. ": timeout for item: " .. tostring(item))
    +                if localWaiting then
    +                    waiting = true
    +                    count = count + 1
                     end
    -                self.failedItemRequests[item] = true
    -                itemRequestTimeoutAt = nil
    -                pendingItemRequest = nil
    -                tremove(items, i)
    -            else
    -                return true -- still waiting for server response
    +            end
    +            if count == 0 then
    +                itemRequests[range] = nil
                 end
             end
         end
    +    return waiting
     end
    
     function lib:initialOnUpdate()
         self:init()
         if friendItemRequests then
    -        if self:processItemRequests(friendItemRequests) then return end
    -        friendItemRequests = nil
    +        if not self:processItemRequests(friendItemRequests) then
    +            friendItemRequests = nil
    +        end
         end
         if harmItemRequests then
    -        if self:processItemRequests(harmItemRequests) then return end
    -        harmItemRequests = nil
    +        if not self:processItemRequests(harmItemRequests) then
    +            harmItemRequests = nil
    +        end
         end
         if foundNewItems then
             self:init(true)
             foundNewItems = nil
         end
    -    if cacheAllItems then
    -        print(MAJOR_VERSION .. ": finished cache")
    -        cacheAllItems = nil
    +    if not friendItemRequests and not harmItemRequests then
    +        if cacheAllItems then
    +            print(MAJOR_VERSION .. ": finished cache")
    +            cacheAllItems = nil
    +        end
    +        self.frame:Hide()
    +        self.frame:UnregisterEvent("GET_ITEM_INFO_RECEIVED")
         end
    -    self.frame:Hide()
    -    self.frame:UnregisterEvent("GET_ITEM_INFO_RECEIVED")
     end
    
     function lib:scheduleInit()

     

  • mitchnull posted a comment Nov 4, 2019

    Hello!

     

    Thanks for the patch, I'll take a look at it if I'll have some spare time.

    Regarding the misc ranges: they started out being mixed with the normal ranges, but their calculation is a bit different and can cause issues if mixed, that'd why they got separated. (something to do with hitbox size)

     

    the concurrent item query is also an interesting question, as someone is already reporting event storm caused by item queries.  I'll look into it...

  • TorakTheUnprepared posted a comment Nov 5, 2019

    The way I have it currently coded, only one spell of each distance will be 'outstanding' at once (well, one for friendly, one for harmful).  Finding all ranges at once, but not all spells in all ranges at once.  So if it can't find the first one of a given distance, it will still try and find the next one (so if you have 3 '30 yard' items it will still do them one at a time).  It just allows concurrency across different distances.


    Edited Nov 5, 2019
  • nullKomplex posted a comment Dec 20, 2019

    Hey is there any update on implementing this? I'm pretty certain this problem is related to my issues implementing this into WeakAuras for Classic.


To post a comment, please login or register a new account.