Quantcast

Combined item and suffix scanning to use a single coroutine

Kevin Lyles [02-12-13 - 03:12]
Combined item and suffix scanning to use a single coroutine
Filename
ItemScanner.lua
diff --git a/ItemScanner.lua b/ItemScanner.lua
index 969fa74..c83bde0 100644
--- a/ItemScanner.lua
+++ b/ItemScanner.lua
@@ -165,168 +165,97 @@ local function OnUpdate(self)
 	end
 end

-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
+local startTime, roundStartTime, numProcessed, roundNumProcessed, valid, roundValid
+local function genericCoroutine(start, min, max, name, linkTemplate, delay, statusVars, tooltip, threadNumber)
+	if not threadNumber or threadNumber == 1 then
+		statusVars.scan_active = true
+		statusVars.finished = false
 		coroutine.yield()
 		startTime = GetTime()
 		roundStartTime = startTime
-		IS_status.items.last_scanned = start - 1
+		statusVars.last_scanned = start - 1
 		numProcessed, roundNumProcessed, valid, roundValid = 0, 0, 0, 0
-		print(string.format("ItemScanner: starting scan at item %d", start))
+		print(string.format("ItemScanner: starting scan at %s %d", name, start))
 	else
 		coroutine.yield()
 	end

 	local function scan(i, scanningAll)
-		IS_status.items.last_scanned = i
+		statusVars.last_scanned = i
 		local itemStartTime = GetTime()
-		if scanItemLink("item:" .. i .. ":0:0:0:0:0:0:0:85", tooltip, threadNumber) then
+		if scanItemLink(string.format(linkTemplate, i), tooltip, threadNumber) then
 			if scanningAll then
-				if IS_status.items.invalid[i] ~= false then
+				if statusVars.invalid[i] ~= false then
 					valid, roundValid = valid + 1, roundValid + 1
 				end
 			else
-				if IS_status.items.scan_active then
-					IS_status.items.last_valid = i
+				if statusVars.scan_active then
+					statusVars.last_valid = i
 				end
 				valid, roundValid = valid + 1, roundValid + 1
 			end
 			-- Reset invalid count
-			IS_status.items.invalid[i] = false
+			statusVars.invalid[i] = false
 		else
 			-- start with 3 if no previous value
-			IS_status.items.invalid[i] = (IS_status.items.invalid[i] or 2) + 1
+			statusVars.invalid[i] = (statusVars.invalid[i] or 2) + 1
 		end
 		numProcessed = numProcessed + 1
 		roundNumProcessed = roundNumProcessed + 1
-		while GetTime() - itemStartTime < VALID_DELAY do
+		while GetTime() - itemStartTime < delay do
 			coroutine.yield()
 		end

 		local currentTime = GetTime()
 		if currentTime - roundStartTime >= 120 then
 			local elapsedTime = currentTime - startTime
-			print(string.format("ItemScanner: at item %d, %d in %.1f sec. (%.2f/min.) (%d valid), %d this round (%d valid)", i, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid, roundNumProcessed, roundValid))
+			print(string.format("ItemScanner: at %s %d, %d in %.1f sec. (%.2f/min.) (%d valid), %d this round (%d valid)", name, i, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid, roundNumProcessed, roundValid))
 			roundStartTime = roundStartTime + 120
 			roundNumProcessed, roundValid = 0, 0
 		end
 	end

-	while IS_status.items.scan_active and not IS_status.items.finished do
-		local i = IS_status.items.last_scanned
+	while statusVars.scan_active and not statusVars.finished do
+		local i = statusVars.last_scanned
 		if i == nil then
 			break
 		end
 		repeat
 			i = i + 1
-			if i > chunkEnd or i > maxItem then
-				IS_status.items.finished = true
+			if i > max then
+				statusVars.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
+		until not statusVars.invalid[i] or (type(statusVars.invalid[i]) == "number" and statusVars.invalid[i] < CONSECUTIVE_INVALID_TO_IGNORE)
+		if statusVars.finished then
 			break
 		end
 		scan(i, false)
 	end

-	local chunkName = chunkStart .. "-" .. chunkEnd
-	while IS_status.items.scan_active do
-		local i = (IS_status.items.last_invalid[chunkName] or chunkStart - 1)
+	while statusVars.scan_active do
+		local i = (statusVars.last_invalid or min - 1)
 		repeat
 			i = i + 1
-			if i > chunkEnd or i > maxItem then
-				i = chunkStart
+			if i > max then
+				i = min
 			end
-		until IS_status.items.invalid[i] ~= true
-		IS_status.items.last_invalid[chunkName] = i
+		until statusVars.invalid[i] ~= true
+		statusVars.last_invalid = i
 		scan(i, true)
 	end

-	if threadNumber == 1 then
+	if not threadNumber or 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))
+		print(string.format("ItemScanner: scan stopped at %s %d, %d in %.1f sec. (%.2f/min.) (%d valid)", name, statusVars.last_scanned, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid))
 	end

 	return true
 end

-local function suffixParseCoroutine(start, tooltip)
-	IS_status.suffixes.scan_active = true
-	IS_status.suffixes.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 suffix %d", start))
-
-	local function scan(i, scanningAll)
-			IS_status.suffixes.last_scanned = i
-			local itemStartTime = GetTime()
-			if scanItemLink("item:11993:0:0:0:0:0:" .. i .. ":100:85", tooltip) then
-				if scanningAll then
-					if IS_status.suffixes.invalid[i] ~= false then
-						valid, roundValid = valid + 1, roundValid + 1
-					end
-				else
-					IS_status.suffixes.last_valid = i
-					valid, roundValid = valid + 1, roundValid + 1
-				end
-				-- Reset invalid count
-				IS_status.suffixes.invalid[i] = false
-			else
-				-- start with 3 if no previous value
-				IS_status.suffixes.invalid[i] = (IS_status.suffixes.invalid[i] or 2) + 1
-			end
-			numProcessed = numProcessed + 1
-			roundNumProcessed = roundNumProcessed + 1
-			while GetTime() - itemStartTime < VALID_DELAY do
-				coroutine.yield()
-			end
-
-			local currentTime = GetTime()
-			if currentTime - roundStartTime >= 120 then
-				local elapsedTime = currentTime - startTime
-				print(string.format("ItemScanner: at suffix %d, %d in %.1f sec. (%.2f/min.) (%d valid), %d this round (%d valid)", i, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid, roundNumProcessed, roundValid))
-				roundStartTime = roundStartTime + 120
-				roundNumProcessed, roundValid = 0, 0
-			end
-	end
-
-	for i = start, maxSuffix do
-		if not IS_status.suffixes.invalid[i] or (type(IS_status.suffixes.invalid[i]) == "number" and IS_status.suffixes.invalid[i] < CONSECUTIVE_INVALID_TO_IGNORE) then
-			if not IS_status.suffixes.scan_active then
-				break
-			end
-			scan(i, false)
-		end
-	end
-	IS_status.suffixes.finished = true
-	while IS_status.suffixes.scan_active do
-		local i = (IS_status.suffixes.last_invalid or minSuffix - 1)
-		repeat
-			i = i + 1
-			if i > maxSuffix then
-				i = minSuffix
-			end
-		until IS_status.suffixes.invalid[i] ~= true
-		IS_status.suffixes.last_invalid = i
-		scan(i, true)
-	end
-
-	local elapsedTime = GetTime() - startTime
-	print(string.format("ItemScanner: scan stopped at suffix %d, %d in %.1f sec. (%.2f/min.) (%d valid)", IS_status.suffixes.last_scanned, numProcessed, elapsedTime, numProcessed / elapsedTime * 60, valid))
-
-	return true
-end
-
 local function commandHandler(msg)
 	if string.match(msg, "^all") then
 		commandHandler(string.gsub(msg, "all", "items"))
@@ -339,9 +268,6 @@ local function commandHandler(msg)
 				print("Scanning known valid items with " .. #(frame.itemco) .. " threads")
 			end
 			print("Last item: " .. tostring(IS_status.items.last_scanned or "none"))
-			local currentTime = GetTime()
-			print("roundTime = " .. tostring(currentTime - roundStartTime))
-			print("elapsedTime = " .. tostring(currentTime - startTime))
 		elseif IS_status.suffixes.scan_active then
 			if IS_status.suffixes.finished then
 				print("Scanning all random suffixes")
@@ -353,6 +279,9 @@ local function commandHandler(msg)
 			print("Scanning not active")
 			return
 		end
+		local currentTime = GetTime()
+		print("roundTime = " .. tostring(currentTime - roundStartTime))
+		print("elapsedTime = " .. tostring(currentTime - startTime))
 	elseif string.match(msg, "^items") then
 		local start

@@ -395,14 +324,19 @@ local function commandHandler(msg)
 			return
 		end

+		local chunkStart, chunkEnd = chunkSize * math.floor(start / chunkSize), chunkSize * math.floor((start + chunkSize) / chunkSize) - 1
+		if chunkEnd > maxItem then
+			chunkEnd = maxItem
+		end
+
 		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)
+			table.insert(frame.itemco, coroutine.wrap(genericCoroutine))
+			frame.itemco[i](start + i - 1, chunkStart, chunkEnd, "item", "item:%d:0:0:0:0:0:0:0:85", VALID_DELAY * NUM_THREADS, IS_status.items[chunkStart .. "-" .. chunkEnd], tooltip, i)
 		end
 		frame:SetScript("OnUpdate", OnUpdate)
 	elseif string.match(msg, "^suffixes") then
@@ -433,12 +367,14 @@ local function commandHandler(msg)
 			print("  stop        stops scanning but leaves existing data intact")
 			return
 		end
-		frame.suffco = coroutine.wrap(suffixParseCoroutine)
+
 		local tooltip = _G["ItemScannerHiddenTooltip1"]
 		if not tooltip then
 			tooltip = CreateFrame("GameTooltip", "ItemScannerHiddenTooltip1", nil, "ItemScannerHiddenTooltip")
 		end
-		frame.suffco(start, tooltip)
+
+		frame.suffco = coroutine.wrap(genericCoroutine)
+		frame.suffco(start, minSuffix, maxSuffix, "suffix", "item:11993:0:0:0:0:0:%d:100:85", VALID_DELAY, IS_status.suffixes, tooltip)
 		frame:SetScript("OnUpdate", OnUpdate)
 	else
 		print("Usage: /is <arg> (or /itemscanner <arg>")
@@ -483,10 +419,29 @@ SlashCmdList["ITEMSCANNER"] = commandHandler

 -- Autoresumes on login
 local function resume()
-	if not IS_status.items.last_invalid then
-		IS_status.items.last_invalid = {}
+	if not IS_status.items then
+		IS_status.items = {}
 	end

+	local metatable = {
+		__index = IS_status.items,
+		__newindex = function(tbl, key, val)
+			if key == "last_invalid" then
+				rawset(tbl, key, val)
+			else
+				IS_status.items[key] = val
+			end
+		end,
+	}
+	local t = IS_status.items
+	for i = 0, maxItem, chunkSize do
+		local chunkStart, chunkEnd = chunkSize * math.floor(i / chunkSize), chunkSize * math.floor((i + chunkSize) / chunkSize) - 1
+		local chunkName = chunkStart .. "-" .. chunkEnd
+		t[chunkName] = setmetatable(t[chunkName] or { last_invalid = (IS_status.items.last_invalid or {})[chunkName] }, metatable)
+	end
+
+	IS_status.items.last_invalid = nil
+
 	if IS_status.items.scan_active then
 		if IS_status.items.finished then
 			commandHandler("items next-chunk")