init
This commit is contained in:
@@ -0,0 +1,628 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- 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.factionrealm.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.factionrealm.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.factionrealm.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
|
||||
Reference in New Issue
Block a user