diff --git a/ItemScanner.lua b/ItemScanner.lua
index ddee57b..72ffa1e 100644
--- a/ItemScanner.lua
+++ b/ItemScanner.lua
@@ -1,7 +1,8 @@
local minItem, maxItem, chunkSize = 1, 99999, 20000
local CONSECUTIVE_INVALID_TO_IGNORE = 5
local VALID_DELAY = 0.025
-local INVALID_DELAY = 5
+local INVALID_DELAY = 10
+local NUM_THREADS = 4
local patterns = {
"^Design:",
@@ -22,21 +23,22 @@ local skippedLines = {
"^Collected %(%d/%d%)$",
}
-local function scanItemLink(link)
+local function scanItemLink(link, tooltip, threadNumber)
local textL, textR
+ local tooltipName = tooltip:GetName()
-- Populate hidden tooltip
- ItemScannerHiddenTooltip:ClearLines()
- ItemScannerHiddenTooltip:SetHyperlink(link)
+ tooltip:ClearLines()
+ tooltip:SetHyperlink(link)
local startTime = GetTime()
local isPattern, hasReq = false, false
while true do
- local numLines = ItemScannerHiddenTooltip:NumLines()
+ local numLines = tooltip:NumLines()
local done = numLines > 1
if done then
if not isPattern then
- local text = _G["ItemScannerHiddenTooltipTextLeft1"]:GetText()
+ local text = _G[tooltipName .. "TextLeft1"]:GetText()
for _, pattern in ipairs(patterns) do
if text:find(pattern) then
isPattern = true
@@ -46,7 +48,7 @@ local function scanItemLink(link)
end
-- We can skip the first line because it will only be RETRIEVING_ITEM_INFO if we only have one line
for i = 2, numLines do
- local text = _G["ItemScannerHiddenTooltipTextLeft" .. i]:GetText()
+ local text = _G[tooltipName .. "TextLeft" .. i]:GetText()
if text:find(RETRIEVING_ITEM_INFO) then
done = false
break
@@ -74,7 +76,7 @@ local function scanItemLink(link)
end
end
- if IS_item_info[link] and #(IS_item_info[link]) >= ItemScannerHiddenTooltip:NumLines() then
+ if IS_item_info[link] and #(IS_item_info[link]) >= tooltip:NumLines() then
return true
end
@@ -89,9 +91,9 @@ local function scanItemLink(link)
harmful = IsHarmfulItem(link),
}
local numSkipped = 0
- for i = 1, ItemScannerHiddenTooltip:NumLines() do
- textL = _G["ItemScannerHiddenTooltipTextLeft" .. i]:GetText()
- textR = _G["ItemScannerHiddenTooltipTextRight" .. i]:GetText()
+ for i = 1, tooltip:NumLines() do
+ textL = _G[tooltipName .. "TextLeft" .. i]:GetText()
+ textR = _G[tooltipName .. "TextRight" .. i]:GetText()
local skip = false
for _, pattern in ipairs(skippedLines) do
if textL:find(pattern) then
@@ -100,7 +102,7 @@ local function scanItemLink(link)
break
end
end
- if not skip and (i ~= ItemScannerHiddenTooltip:NumLines() or (textL ~= " " and textL ~= "" and textR ~= " " and textR ~= "")) then
+ if not skip and (i ~= tooltip:NumLines() or (textL ~= " " and textL ~= "" and textR ~= " " and textR ~= "")) then
IS_item_info[link][i - numSkipped] = {
left = textL,
right = textR,
@@ -112,17 +114,33 @@ local function scanItemLink(link)
end
local frame = CreateFrame("Frame")
+local nextSlot = 1
local function OnUpdate(self)
local status, err
if self.itemco then
- status, err = pcall(self.itemco)
- if status == false then
- print("ItemScanner: item scan error, aborting.")
- self.itemco = nil
- IS_status.items.scan_active = false
- error(err)
- return
- elseif err == true then
+ if nextSlot > #(self.itemco) then
+ nextSlot = 1
+ end
+ if self.itemco[nextSlot] then
+ status, err = pcall(self.itemco[nextSlot])
+ if status == false then
+ print("ItemScanner: item scan error, aborting thread " .. nextSlot .. ".")
+ table.remove(self.itemco, nextSlot)
+ if #(self.itemco) == 0 then
+ IS_status.items.scan_active = false
+ self.itemco = nil
+ end
+ error(err)
+ return
+ elseif err == true then
+ table.remove(self.itemco, nextSlot)
+ if #(self.itemco) == 0 then
+ self.itemco = nil
+ end
+ return
+ end
+ nextSlot = nextSlot + 1
+ else
self.itemco = nil
end
else
@@ -130,20 +148,26 @@ local function OnUpdate(self)
end
end
-local function itemParseCoroutine(start)
- local chunkStart, chunkEnd = chunkSize * math.floor(start / chunkSize), chunkSize * math.floor((start + chunkSize) / chunkSize) - 1
- IS_status.items.scan_active = true
- IS_status.items.finished = false
- coroutine.yield()
- local startTime = GetTime()
- local roundStartTime = startTime
- local numProcessed, roundNumProcessed, valid, roundValid = 0, 0, 0, 0
- print(string.format("ItemScanner: starting scan at item %d", start))
+local startTime, roundStartTime, numProcessed, roundNumProcessed, valid, roundValid, chunkStart, chunkEnd
+local function itemParseCoroutine(start, tooltip, threadNumber)
+ if threadNumber == 1 then
+ chunkStart, chunkEnd = chunkSize * math.floor(start / chunkSize), chunkSize * math.floor((start + chunkSize) / chunkSize) - 1
+ IS_status.items.scan_active = true
+ IS_status.items.finished = false
+ coroutine.yield()
+ startTime = GetTime()
+ roundStartTime = startTime
+ IS_status.items.last_scanned = start - 1
+ numProcessed, roundNumProcessed, valid, roundValid = 0, 0, 0, 0
+ print(string.format("ItemScanner: starting scan at item %d", start))
+ else
+ coroutine.yield()
+ end
local function scan(i, scanningAll)
IS_status.items.last_scanned = i
local itemStartTime = GetTime()
- if scanItemLink("item:" .. i .. ":0:0:0:0:0:0:0:85") then
+ if scanItemLink("item:" .. i .. ":0:0:0:0:0:0:0:85", tooltip, threadNumber) then
if scanningAll then
if IS_status.items.invalid[i] ~= false then
valid, roundValid = valid + 1, roundValid + 1
@@ -175,15 +199,24 @@ local function itemParseCoroutine(start)
end
end
- for i = start, math.min(chunkEnd, maxItem) do
- if not IS_status.items.invalid[i] or (type(IS_status.items.invalid[i]) == "number" and IS_status.items.invalid[i] < CONSECUTIVE_INVALID_TO_IGNORE) then
- scan(i, false)
- if not IS_status.items.scan_active then
+ while IS_status.items.scan_active and not IS_status.items.finished do
+ local i = IS_status.items.last_scanned
+ if i == nil then
+ break
+ end
+ repeat
+ i = i + 1
+ if i > chunkEnd or i > maxItem then
+ IS_status.items.finished = true
break
end
+ until not IS_status.items.invalid[i] or (type(IS_status.items.invalid[i]) == "number" and IS_status.items.invalid[i] < CONSECUTIVE_INVALID_TO_IGNORE)
+ if IS_status.items.finished then
+ break
end
+ scan(i, false)
end
- IS_status.items.finished = true
+
local chunkName = chunkStart .. "-" .. chunkEnd
while IS_status.items.scan_active do
local i = (IS_status.items.last_invalid[chunkName] or chunkStart - 1)
@@ -197,11 +230,13 @@ local function itemParseCoroutine(start)
scan(i, true)
end
- local elapsedTime = GetTime() - startTime
- if elapsedTime == 0 then
- elapsedTime = 0.001
+ if threadNumber == 1 then
+ local elapsedTime = GetTime() - startTime
+ if elapsedTime == 0 then
+ elapsedTime = 0.001
+ end
+ print(string.format("ItemScanner: scan stopped at item %d, %d in %.1f sec. (%.2f/min.) (%d valid)", IS_status.items.last_scanned, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid))
end
- print(string.format("ItemScanner: scan stopped at item %d, %d in %.1f sec. (%.2f/min.) (%d valid)", IS_status.items.last_scanned, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid))
return true
end
@@ -249,8 +284,15 @@ local function commandHandler(msg)
return
end
- frame.itemco = coroutine.wrap(itemParseCoroutine)
- frame.itemco(start)
+ frame.itemco = {}
+ for i = 1, NUM_THREADS do
+ local tooltip = _G["ItemScannerHiddenTooltip" .. i]
+ if not tooltip then
+ tooltip = CreateFrame("GameTooltip", "ItemScannerHiddenTooltip" .. i, nil, "ItemScannerHiddenTooltip")
+ end
+ table.insert(frame.itemco, coroutine.wrap(itemParseCoroutine))
+ frame.itemco[i](start + i - 1, tooltip, i)
+ end
frame:SetScript("OnUpdate", OnUpdate)
else
print("Usage: /is <arg> (or /itemscanner <arg>")
diff --git a/ItemScanner.xml b/ItemScanner.xml
index 0b96ba0..7771c7e 100644
--- a/ItemScanner.xml
+++ b/ItemScanner.xml
@@ -1,7 +1,7 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\FrameXML\UI.xsd">
<Script file="ItemScanner.lua"/>
- <GameTooltip name="ItemScannerHiddenTooltip" inherits="GameTooltipTemplate">
+ <GameTooltip name="ItemScannerHiddenTooltip" inherits="GameTooltipTemplate" virtual="true">
<Scripts>
<Onload>
self:SetOwner(WorldFrame, "ANCHOR_NONE")