42dbe9d526
* Enchanting fixes, reverted Crafting module to previous version that supports Ascension specific crafts and Vellums. * Updated enchanting names from DB * Added the remaining Ascension specific recipes * Replace SpellNames2IDs.lua with UnitCastingSpellID * Bugfix: Ascension uses exact quality QueryAuctionItems, TSM was designed for quality or higher * Bugfix: Properly get list of Professions via index * Replaced factionrealm with realm Ascension does not have strict faction seperation * GetTradeSkillCooldown -> SpellHasBaseCooldown GetTradeSkillCooldown only tells you if a spell is on CD, not if it has a CD Data is from DB, which isn't populated fully yet. * Implement backported API GetSpellBaseCooldown * bugfix: hasCD is nil if no cd, not 0
628 lines
22 KiB
Lua
628 lines
22 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- http://www.curse.com/addons/wow/tradeskill-master --
|
|
-- --
|
|
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
|
|
-- All Rights Reserved* - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
-- This file contains code for scanning the auction house
|
|
local TSM = select(2, ...)
|
|
local AuctionScanning = TSM:NewModule("AuctionScanning", "AceEvent-3.0")
|
|
TSMAPI.AuctionScan = {}
|
|
|
|
local RETRY_DELAY = 2
|
|
local MAX_RETRIES = 4
|
|
local BASE_DELAY = 0.10 -- time to delay for before trying to scan a page again when it isn't fully loaded
|
|
local private = { callbackHandler = nil, query = {}, options = {}, data = {}, isScanning = nil }
|
|
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.AuctionScanning_private")
|
|
local scanCache = {}
|
|
|
|
local CACHE_DECAY_PER_DAY = 5
|
|
local CACHE_AUTO_HIT_TIME = 10 * 60
|
|
local SECONDS_PER_DAY = 60 * 60 * 24
|
|
|
|
|
|
local function DoCallback(...)
|
|
if type(private.callbackHandler) == "function" then
|
|
private.callbackHandler(...)
|
|
end
|
|
end
|
|
|
|
local function eventHandler(event)
|
|
if event == "AUCTION_HOUSE_CLOSED" then
|
|
-- auction house was closed, make sure all scanning is stopped
|
|
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
private.auctionHouseShown = false
|
|
DoCallback("INTERRUPTED")
|
|
private:StopScanning()
|
|
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
|
|
-- gets called whenever the AH window is updated (something is shown in the results section)
|
|
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
TSMAPI:CancelFrame("updateDelay")
|
|
-- now that our query was successful, we can get our data
|
|
private:ScanAuctions()
|
|
end
|
|
end
|
|
|
|
function AuctionScanning:OnEnable()
|
|
AuctionScanning:RegisterEvent("AUCTION_HOUSE_CLOSED", eventHandler)
|
|
end
|
|
|
|
function private:ScanAuctionPage(resolveSellers)
|
|
local shown = GetNumAuctionItems("list")
|
|
local badData = false
|
|
local auctions = {}
|
|
|
|
for i = 1, shown do
|
|
-- checks to make sure all the data has been sent to the client
|
|
-- if not, the data is bad and we'll wait / try again
|
|
-- local count, _, _, _, _, _, _, buyout, _, _, _, seller = select(3, GetAuctionItemInfo("list", i))
|
|
local count, _, _, _, _, _, buyout, _, _, seller = select(3, GetAuctionItemInfo("list", i))
|
|
local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", i))
|
|
auctions[i] = { itemString = itemString, index = i, count = count, buyout = buyout, seller = seller }
|
|
if not (itemString and buyout and count and (seller or not resolveSellers or buyout == 0)) then
|
|
badData = true
|
|
end
|
|
end
|
|
|
|
return badData, auctions
|
|
end
|
|
|
|
function IsDuplicatePage()
|
|
if not private.pageTemp or GetNumAuctionItems("list") == 0 then return false end
|
|
|
|
local numLinks, prevLink = 0, nil
|
|
for i = 1, GetNumAuctionItems("list") do
|
|
-- local _, _, count, _, _, _, _, minBid, minInc, buyout, bid, _, _, seller = GetAuctionItemInfo("list", i)
|
|
local _, _, count, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
|
local link = GetAuctionItemLink("list", i)
|
|
local temp = private.pageTemp[i]
|
|
|
|
if not prevLink then
|
|
prevLink = link
|
|
elseif prevLink ~= link then
|
|
prevLink = link
|
|
numLinks = numLinks + 1
|
|
end
|
|
|
|
if not temp or temp.count ~= count or temp.minBid ~= minBid or temp.minInc ~= minInc or temp.buyout ~= buyout or temp.bid ~= bid or temp.seller ~= seller or temp.link ~= link then
|
|
return false
|
|
end
|
|
end
|
|
|
|
if numLinks > 1 and private.pageTemp.shown == GetNumAuctionItems("list") then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function PopulatePageTemp()
|
|
local shown = GetNumAuctionItems("list")
|
|
private.pageTemp = { numShown = shown }
|
|
|
|
for i = 1, shown do
|
|
-- checks to make sure all the data has been sent to the client
|
|
-- if not, the data is bad and we'll wait / try again
|
|
-- local _, _, count, _, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
|
local _, _, count, _, _, _, minBid, minInc, buyout, bid, _, seller = GetAuctionItemInfo("list", i)
|
|
local link = GetAuctionItemLink("list", i)
|
|
|
|
private.pageTemp[i] = { count = count, minBid = minBid, minInc = minInc, buyout = buyout, bid = bid, seller = seller, link = link }
|
|
end
|
|
end
|
|
|
|
-- Starts a scan of the auction house.
|
|
-- query - A single query containing QueryAuctionItem paramters:
|
|
-- name, minLevel, maxLevel, invType, class, subClass, usable, quality
|
|
-- resolveSellers - whether or not to resolve seller names
|
|
-- maxPrice - stop scanning when prices go above this price
|
|
function TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, maxPrice, doCache)
|
|
TSMAPI.AuctionScan:StopScan() -- stop any scan in progress
|
|
|
|
if not AuctionFrame:IsVisible() then
|
|
return -1 -- the auction house isn't open (return code -1)
|
|
elseif type(query) ~= "table" then
|
|
return -2 -- the scan queue is not a table (return code -2)
|
|
elseif not CanSendAuctionQuery() then
|
|
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:RunQuery(query, callbackHandler, resolveSellers, maxPrice, doCache) end)
|
|
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
|
end
|
|
|
|
-- sort by buyout
|
|
SortAuctionItems("list", "buyout")
|
|
if IsAuctionSortReversed("list", "buyout") then
|
|
SortAuctionItems("list", "buyout")
|
|
end
|
|
|
|
-- setup the query
|
|
private.query = CopyTable(query)
|
|
private.query.page = 0 -- the current page of this query we're scanning
|
|
private.query.timeDelay = 0 -- a delay used to wait for information to show up
|
|
private.query.retries = 0 -- how many times we've done a hard retry so far
|
|
private.query.hardRetry = nil -- if a page hasn't loaded after we've tried a delay, we'll do a hard retry and re-send the query
|
|
private.cache = doCache and { query = CopyTable(query), items = {} } or nil
|
|
|
|
-- setup other stuff
|
|
wipe(private.data)
|
|
private.isScanning = true
|
|
private.callbackHandler = callbackHandler
|
|
private.resolveSellers = resolveSellers
|
|
private.scanType = "query"
|
|
private.maxPrice = maxPrice or math.huge
|
|
|
|
--starts scanning
|
|
private:SendQuery()
|
|
return 1 -- scan started successfully (return code 1)
|
|
end
|
|
|
|
function TSMAPI.AuctionScan:ScanLastPage(callbackHandler)
|
|
private:StopScanning() -- stop any scan in progress
|
|
|
|
if not AuctionFrame:IsVisible() then
|
|
return -1 -- the auction house isn't open (return code -1)
|
|
elseif not CanSendAuctionQuery() then
|
|
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:ScanLastPage(callbackHandler) end)
|
|
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
|
end
|
|
|
|
-- clear the auction sort
|
|
SortAuctionClearSort("list")
|
|
|
|
-- setup the query
|
|
private.query = {name="", page=0}
|
|
private.query.timeDelay = 0 -- a delay used to wait for information to show up
|
|
private.query.retries = 0 -- how many times we've done a hard retry so far
|
|
private.query.hardRetry = nil -- if a page hasn't loaded after we've tried a delay, we'll do a hard retry and re-send the query
|
|
|
|
-- setup other stuff
|
|
wipe(private.data)
|
|
private.isScanning = true
|
|
private.callbackHandler = callbackHandler
|
|
private.scanType = "lastPage"
|
|
|
|
--starts scanning
|
|
private:SendQuery()
|
|
return 1 -- scan started successfully (return code 1)
|
|
end
|
|
|
|
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
|
|
function private:SendQuery()
|
|
if not private.isScanning then return end
|
|
|
|
if CanSendAuctionQuery() then
|
|
-- stop delay timer
|
|
TSMAPI:CancelFrame("queryDelay")
|
|
|
|
-- Query the auction house (then waits for AUCTION_ITEM_LIST_UPDATE to fire)
|
|
AuctionScanning:RegisterEvent("AUCTION_ITEM_LIST_UPDATE", eventHandler)
|
|
-- [exact] cardinal ruby 0 0 nil 0 0 0 0 0
|
|
-- [normal] cardinal ruby nil nil nil nil nil 0 nil nil
|
|
QueryAuctionItems(private.query.name, private.query.minLevel, private.query.maxLevel, private.query.invType, private.query.class, private.query.subClass, private.query.page, private.query.usable, private.query.quality)
|
|
else
|
|
-- run delay timer then try again to scan
|
|
TSMAPI:CreateTimeDelay("queryDelay", 0.05, private.SendQuery)
|
|
end
|
|
end
|
|
|
|
--scans the currently shown page of auctions and collects all the data
|
|
function private:ScanAuctions()
|
|
if not private.isScanning then return end
|
|
local shown, total = GetNumAuctionItems("list")
|
|
local totalPages = ceil(total / NUM_AUCTION_ITEMS_PER_PAGE)
|
|
|
|
if private.scanType == "numPages" then
|
|
local cacheData = TSM.db.realm.numPagesCache[private.query.cacheKey]
|
|
cacheData.lastScan = time()
|
|
local confidence = (120 - cacheData.confidence) / (CACHE_DECAY_PER_DAY * 2)
|
|
local diff = abs(cacheData.avg - totalPages)
|
|
if diff <= 1 and diff > 0.5 then
|
|
confidence = floor(confidence * (1.5 - diff))
|
|
elseif diff > 1 then
|
|
confidence = floor(confidence - CACHE_DECAY_PER_DAY * diff)
|
|
end
|
|
cacheData.confidence = max(floor(cacheData.confidence + confidence), 0)
|
|
cacheData.avg = (cacheData.avg * cacheData.numScans + totalPages) / (cacheData.numScans + 1)
|
|
cacheData.numScans = cacheData.numScans + 1
|
|
|
|
private:StopScanning()
|
|
return DoCallback("NUM_PAGES", totalPages)
|
|
elseif private.scanType == "lastPage" then
|
|
local lastPage = floor(total / NUM_AUCTION_ITEMS_PER_PAGE)
|
|
if private.query.page ~= lastPage then
|
|
private.query.page = lastPage
|
|
return private:SendQuery()
|
|
end
|
|
end
|
|
|
|
local dataIsBad, auctions = private:ScanAuctionPage(private.resolveSellers)
|
|
|
|
-- check that we have good data
|
|
if dataIsBad or IsDuplicatePage() then
|
|
if private.query.retries < MAX_RETRIES then
|
|
if private.query.hardRetry then
|
|
-- Hard retry
|
|
-- re-sends the entire query
|
|
private.query.retries = private.query.retries + 1
|
|
private.query.timeDelay = 0
|
|
private.query.hardRetry = nil
|
|
private:SendQuery()
|
|
else
|
|
-- Soft retry
|
|
-- runs a delay and then tries to scan the query again
|
|
private.query.timeDelay = private.query.timeDelay + BASE_DELAY
|
|
TSMAPI:CreateTimeDelay("updateDelay", BASE_DELAY, private.ScanAuctions)
|
|
|
|
-- If after 2 seconds of retrying we still don't have data, will go and requery to try and solve the issue
|
|
-- if we still don't have data, we try to scan it anyway and move on.
|
|
if private.query.timeDelay >= RETRY_DELAY then
|
|
private.query.hardRetry = true
|
|
end
|
|
end
|
|
return
|
|
end
|
|
end
|
|
|
|
if private.cache then
|
|
-- store info in cache
|
|
for i, v in ipairs(auctions) do
|
|
local cacheTmp = CopyTable(v)
|
|
cacheTmp.index = private.query.page * 50 + i
|
|
tinsert(private.cache, cacheTmp)
|
|
private.cache.items[cacheTmp.itemString] = true
|
|
end
|
|
end
|
|
|
|
private.query.hardRetry = nil
|
|
private.query.retries = 0
|
|
private.query.timeDelay = 0
|
|
if private.scanType ~= "lastPage" then
|
|
private.query.page = private.query.page + 1 -- increment current page
|
|
if totalPages > 0 then
|
|
DoCallback("SCAN_PAGE_UPDATE", private.query.page, totalPages)
|
|
end
|
|
end
|
|
PopulatePageTemp()
|
|
|
|
-- now that we know our query is good, time to verify and then store our data
|
|
for _, v in ipairs(auctions) do
|
|
if private:AddAuctionRecord(v.index) then
|
|
-- we've hit the max price so we're done scanning
|
|
private:StopScanning()
|
|
return DoCallback("SCAN_COMPLETE", private.data)
|
|
end
|
|
end
|
|
|
|
if private.scanType == "lastPage" then
|
|
return DoCallback("SCAN_LAST_PAGE_COMPLETE", private.data)
|
|
elseif private.query.page >= totalPages then
|
|
-- we have finished scanning this query
|
|
private:StopScanning()
|
|
return DoCallback("SCAN_COMPLETE", private.data)
|
|
end
|
|
|
|
-- query the next page and continue scanning
|
|
private:SendQuery()
|
|
end
|
|
|
|
-- Add a new record to the private.data table
|
|
function private:AddAuctionRecord(index)
|
|
-- local name, texture, count, _, _, _, _, minBid, minIncrement, buyout, bid, highBidder, highBidder_full, seller, seller_full = GetAuctionItemInfo("list", index)
|
|
local name, texture, count, _, _, _, minBid, minIncrement, buyout, bid, highBidder, seller = GetAuctionItemInfo("list", index)
|
|
seller = TSM:GetAuctionPlayer(seller, null)
|
|
highBidder = TSM:GetAuctionPlayer(highBidder, null)
|
|
local timeLeft = GetAuctionItemTimeLeft("list", index)
|
|
local link = GetAuctionItemLink("list", index)
|
|
local itemString = TSMAPI:GetItemString(link)
|
|
if not itemString then return end
|
|
|
|
-- Create a new entry in the table
|
|
if not private.data[itemString] then
|
|
private.data[itemString] = TSMAPI.AuctionScan:NewAuctionItem()
|
|
private.data[itemString]:SetItemLink(link)
|
|
private.data[itemString]:SetTexture(texture)
|
|
end
|
|
private.data[itemString]:AddAuctionRecord(count, minBid, minIncrement, buyout, bid, highBidder, seller or "?", timeLeft)
|
|
|
|
-- add the base item if necessary
|
|
local baseItemString = TSMAPI:GetBaseItemString(itemString)
|
|
if baseItemString ~= itemString then
|
|
-- Create a new entry in the table
|
|
if not private.data[baseItemString] then
|
|
private.data[baseItemString] = TSMAPI.AuctionScan:NewAuctionItem()
|
|
private.data[baseItemString]:SetItemLink(link)
|
|
private.data[baseItemString]:SetTexture(texture)
|
|
end
|
|
private.data[baseItemString]:AddAuctionRecord(count, minBid, minIncrement, buyout, bid, highBidder, seller or "?", timeLeft)
|
|
private.data[baseItemString].isBaseItem = true
|
|
end
|
|
|
|
if select(8, TSMAPI:GetSafeItemInfo(link)) == count then
|
|
return (buyout or 0) / count > (private.maxPrice or math.huge)
|
|
end
|
|
end
|
|
|
|
-- stops the scan when we are finished scanning, it was interrupted, or somebody stopped it
|
|
function private:StopScanning()
|
|
TSMAPI:CancelFrame("cantSendAuctionQueryDelay")
|
|
if not private.isScanning then return end
|
|
|
|
if private.cache then
|
|
-- store the cache info
|
|
sort(private.cache, function(a, b) return a.index < b.index end)
|
|
for itemString in pairs(private.cache.items) do
|
|
scanCache[itemString] = private.cache
|
|
end
|
|
wipe(private.cache.items)
|
|
private.cache = nil
|
|
end
|
|
|
|
-- cancel any delays that might still be running
|
|
TSMAPI:CancelFrame("queryDelay")
|
|
TSMAPI:CancelFrame("updateDelay")
|
|
AuctionScanning:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
private.isScanning = nil
|
|
private.pageTemp = nil
|
|
end
|
|
|
|
-- API for stopping the scan
|
|
-- returns true/false if we were/weren't actually scanning
|
|
function TSMAPI.AuctionScan:StopScan()
|
|
private:StopScanning()
|
|
TSM:StopGeneratingQueries()
|
|
end
|
|
|
|
|
|
-- Gets the number of pages for a given query
|
|
function TSMAPI.AuctionScan:GetNumPages(query, callbackHandler)
|
|
private:StopScanning() -- stop any scan in progress
|
|
|
|
if not AuctionFrame:IsVisible() then
|
|
return -1 -- the auction house isn't open (return code -1)
|
|
elseif type(query) ~= "table" then
|
|
return -2 -- the scan queue is not a table (return code -2)
|
|
elseif not CanSendAuctionQuery() then
|
|
TSMAPI:CreateTimeDelay("cantSendAuctionQueryDelay", 0.1, function() TSMAPI.AuctionScan:GetNumPages(query, callbackHandler) end)
|
|
return 0 -- the query will start as soon as it can but did not start immediately (return code 0)
|
|
end
|
|
|
|
-- fancy caching
|
|
local temp = {}
|
|
for i, field in ipairs({ "name", "minLevel", "maxLevel", "invType", "class", "subClass", "usable", "quality" }) do
|
|
temp[i] = tostring(query[field])
|
|
end
|
|
local cacheKey = table.concat(temp, "~")
|
|
local cacheData = TSM.db.realm.numPagesCache[cacheKey]
|
|
if cacheData then
|
|
local cacheHit
|
|
if time() - cacheData.lastScan < CACHE_AUTO_HIT_TIME then
|
|
-- auto cache hit
|
|
cacheHit = true
|
|
elseif random(1, 100) <= cacheData.confidence then
|
|
-- cache hit
|
|
cacheData.confidence = cacheData.confidence - floor(((time() - cacheData.lastScan) / SECONDS_PER_DAY) * CACHE_DECAY_PER_DAY + 0.5)
|
|
cacheData.confidence = max(cacheData.confidence, 0) -- ensure >= 0
|
|
cacheHit = true
|
|
end
|
|
|
|
if cacheHit then
|
|
local numPages = max(ceil(cacheData.avg), 1) -- round avg num of pages up and ensure >= 1
|
|
TSMAPI:CreateTimeDelay("numPagesCacheDelay", 0, function() callbackHandler("NUM_PAGES", numPages) end)
|
|
return 2
|
|
end
|
|
else
|
|
TSM.db.realm.numPagesCache[cacheKey] = { avg = 0, confidence = 0, numScans = 0, lastScan = 0 }
|
|
end
|
|
|
|
-- setup the query
|
|
private.query = CopyTable(query)
|
|
private.query.cacheKey = cacheKey
|
|
|
|
-- setup other stuff
|
|
wipe(private.data)
|
|
private.isScanning = true
|
|
private.callbackHandler = callbackHandler
|
|
private.scanType = "numPages"
|
|
|
|
--starts scanning
|
|
private:SendQuery()
|
|
return 1 -- scan started successfully (return code 1)
|
|
end
|
|
|
|
function TSMAPI.AuctionScan:CacheRemove(itemString, index)
|
|
if scanCache[itemString] then
|
|
tremove(scanCache[itemString], index)
|
|
end
|
|
end
|
|
|
|
function TSMAPI.AuctionScan:ClearCache()
|
|
wipe(scanCache)
|
|
end
|
|
|
|
|
|
|
|
|
|
local findPrivate = {}
|
|
findPrivate.findFrame = findPrivate.findFrame or CreateFrame("Frame")
|
|
|
|
local function eventHandler(frame, event)
|
|
if event == "AUCTION_HOUSE_SHOW" then
|
|
-- auction house was opened
|
|
elseif event == "AUCTION_HOUSE_CLOSED" then
|
|
frame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
if findPrivate.isScanning then -- stop scanning if we were scanning (pass true to specify it was interrupted)
|
|
TSMAPI.AuctionScan:StopFindScan()
|
|
end
|
|
elseif event == "AUCTION_ITEM_LIST_UPDATE" then
|
|
frame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
if findPrivate.isScanning then
|
|
findPrivate.timeDelay = 0
|
|
TSMAPI:CancelFrame("auctionFindScanDelay")
|
|
|
|
-- now that our query was successful we can get our data
|
|
findPrivate:ScanAuctions()
|
|
end
|
|
end
|
|
end
|
|
|
|
findPrivate.findFrame:SetScript("OnEvent", eventHandler)
|
|
findPrivate.findFrame:RegisterEvent("AUCTION_HOUSE_CLOSED")
|
|
findPrivate.findFrame:RegisterEvent("AUCTION_HOUSE_SHOW")
|
|
|
|
local function CompareTableKeys(tbl1, tbl2)
|
|
for _, key in ipairs(findPrivate.keys) do
|
|
if tbl1[key] ~= tbl2[key] then
|
|
return
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function IsTargetAuction(index)
|
|
local itemString = TSMAPI:GetItemString(GetAuctionItemLink("list", index))
|
|
-- local _, _, count, _, _, _, _, minBid, bidIncrement, buyout, bidAmount, _, _, seller, seller_full = GetAuctionItemInfo("list", index)
|
|
local _, _, count, _, _, _, minBid, bidIncrement, buyout, bidAmount, _, _, seller = GetAuctionItemInfo("list", index)
|
|
seller = TSM:GetAuctionPlayer(seller, nil)
|
|
local bid = bidAmount == 0 and minBid or bidAmount
|
|
local tmp = { itemString = itemString, count = count, bid = bid, buyout = buyout, seller = seller }
|
|
return CompareTableKeys(tmp, findPrivate.targetInfo)
|
|
end
|
|
|
|
-- valid targetInfo keys: itemString, count, bid, buyout, seller
|
|
function TSMAPI.AuctionScan:FindAuction(callback, targetInfo, useCache)
|
|
if findPrivate.isScanning then TSMAPI.AuctionScan:StopFindScan() end
|
|
|
|
findPrivate.keys = { "itemString", "count", "bid", "buyout", "seller" }
|
|
for i = #findPrivate.keys, 1, -1 do
|
|
if not targetInfo[findPrivate.keys[i]] then
|
|
tremove(findPrivate.keys, i)
|
|
end
|
|
end
|
|
|
|
local cacheIndex
|
|
if useCache and scanCache[targetInfo.itemString] then
|
|
for i, v in ipairs(scanCache[targetInfo.itemString]) do
|
|
if CompareTableKeys(v, targetInfo) then
|
|
cacheIndex = i
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if cacheIndex then
|
|
findPrivate.page = floor((cacheIndex - 1) / 50)
|
|
findPrivate.query = scanCache[targetInfo.itemString].query
|
|
else
|
|
local name, _, rarity, _, minLevel, class, subClass = TSMAPI:GetSafeItemInfo(targetInfo.itemString)
|
|
findPrivate.query = { name = name, minLevel = minLevel, maxLevel = minLevel, class = class, subClass = subClass, rarity = rarity }
|
|
findPrivate.page = 0
|
|
end
|
|
findPrivate.targetInfo = targetInfo
|
|
findPrivate.callback = callback
|
|
findPrivate.cacheIndex = cacheIndex
|
|
findPrivate.isScanning = targetInfo.itemString
|
|
findPrivate.retries = 0
|
|
findPrivate.hardRetry = nil
|
|
|
|
-- check if the item is on the current page
|
|
for i = 1, GetNumAuctionItems("list") do
|
|
if IsTargetAuction(i) then
|
|
TSMAPI.AuctionScan:StopFindScan()
|
|
TSMAPI:CreateTimeDelay("queryFoundDelay", 0.1, function() findPrivate.callback(i) end)
|
|
return
|
|
end
|
|
end
|
|
|
|
findPrivate:SendQuery()
|
|
end
|
|
|
|
-- sends a query to the AH frame once it is ready to be queried (uses frame as a delay)
|
|
function findPrivate:SendQuery()
|
|
if not findPrivate.isScanning then return end
|
|
if CanSendAuctionQuery() then
|
|
-- stop delay timer
|
|
TSMAPI:CancelFrame("auctionFindQueryDelay")
|
|
|
|
-- query the auction house (then waits for AUCTION_ITEM_LIST_UPDATE to fire)
|
|
findPrivate.findFrame:RegisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
local q = findPrivate.query
|
|
QueryAuctionItems(q.name, q.minLevel, q.maxLevel, q.invType, q.class, q.subClass, findPrivate.page, 0, q.rarity)
|
|
else
|
|
-- run delay timer then try again to scan
|
|
TSMAPI:CreateTimeDelay("auctionFindQueryDelay", 0.05, function() findPrivate:SendQuery() end)
|
|
end
|
|
end
|
|
|
|
-- scans the currently shown page of auctions and collects all the data
|
|
function findPrivate:ScanAuctions()
|
|
if not findPrivate.isScanning then return end
|
|
-- collects data on the query:
|
|
-- # of auctions on current page
|
|
-- # of pages total
|
|
local shown, total = GetNumAuctionItems("list")
|
|
local totalPages = math.ceil(total / 50)
|
|
local dataIsBad, temp = private:ScanAuctionPage(findPrivate.targetInfo.seller)
|
|
|
|
-- Check for bad data
|
|
if findPrivate.retries < 3 then
|
|
if dataIsBad then
|
|
if findPrivate.hardRetry then
|
|
-- Hard retry
|
|
-- re-sends the entire query
|
|
findPrivate.retries = findPrivate.retries + 1
|
|
findPrivate:SendQuery()
|
|
else
|
|
-- Soft retry
|
|
-- runs a delay and then tries to scan the query again
|
|
findPrivate.timeDelay = findPrivate.timeDelay + BASE_DELAY
|
|
TSMAPI:CreateTimeDelay("auctionFindScanDelay", BASE_DELAY, findPrivate.ScanAuctions)
|
|
|
|
-- If after 4 seconds of retrying we still don't have data, will go and requery to try and solve the issue
|
|
-- if we still don't have data, we try to scan it anyway and move on.
|
|
if findPrivate.timeDelay >= 4 then
|
|
findPrivate.hardRetry = true
|
|
findPrivate.retries = 0
|
|
end
|
|
end
|
|
|
|
return
|
|
end
|
|
end
|
|
|
|
findPrivate.hardRetry = nil
|
|
findPrivate.retries = 0
|
|
|
|
-- now that we know our query is good, time to verify and then store our data
|
|
for i = 1, shown do
|
|
if IsTargetAuction(temp[i].index) then
|
|
TSMAPI.AuctionScan:StopFindScan()
|
|
return findPrivate.callback(temp[i].index, findPrivate.cacheIndex == findPrivate.page and findPrivate.page * 50 + temp[i].index)
|
|
end
|
|
end
|
|
|
|
-- This query has more pages to scan
|
|
-- increment the page # and send the new query
|
|
if not findPrivate.cacheIndex and totalPages > (findPrivate.page + 1) then
|
|
findPrivate.page = findPrivate.page + 1
|
|
findPrivate:SendQuery()
|
|
return
|
|
end
|
|
|
|
-- we are done scanning!
|
|
TSMAPI.AuctionScan:StopFindScan()
|
|
return findPrivate.callback()
|
|
end
|
|
|
|
-- returns whether or not we're currently doing a find scan
|
|
function TSMAPI.AuctionScan:IsFindScanning()
|
|
return findPrivate.isScanning
|
|
end
|
|
|
|
-- stops the scan because it was either interrupted or it was completed successfully
|
|
function TSMAPI.AuctionScan:StopFindScan()
|
|
findPrivate.findFrame:UnregisterEvent("AUCTION_ITEM_LIST_UPDATE")
|
|
findPrivate.isScanning = nil
|
|
TSMAPI:CancelFrame("auctionFindQueryDelay")
|
|
TSMAPI:CancelFrame("auctionFindScanDelay")
|
|
end |