1) Added "eager" refresh mode to allow ASAP identification of newly acquired

items.  The previous refresh "on" mode works the same as before.
2) Made the plugin less verbose.  Some info notifications were downgraded to
   notes and some notes were downgraded to debug messages.
3) Optimized refresh code to skip notifications and checks in some cases where
   there are no known unidentified items in your inventory
master
Durel 7 years ago
parent 79a50f45de
commit 96a38525fc

@ -123,7 +123,7 @@ Usage
Inventory table access Inventory table access
dinv build confirm dinv build confirm
dinv refresh [on | off | all] <minutes> dinv refresh [on | off | eager | all] <minutes>
dinv search [id | full] <query> dinv search [id | full] <query>
Item management Item management
@ -260,7 +260,7 @@ Feature Wishlist
<alias <alias
script="inv.cli.refresh.fn" script="inv.cli.refresh.fn"
match="^[ ]*dinv[ ]+refresh[ ]*(on)[ ]*([0-9]+)?$" match="^[ ]*dinv[ ]+refresh[ ]*(on|eager)[ ]*([0-9]+)?$"
enabled="y" enabled="y"
regexp="y" regexp="y"
send_to="12" send_to="12"
@ -1067,8 +1067,8 @@ function inv.init.atActiveCR()
-- or added and removed items. Ideally, we would do this at every login. However, some -- or added and removed items. Ideally, we would do this at every login. However, some
-- users may have refreshes disabled because they want to handle things manually. That's -- users may have refreshes disabled because they want to handle things manually. That's
-- fine too. If refreshes are disabled (their period is 0 minutes) then we skip this. -- fine too. If refreshes are disabled (their period is 0 minutes) then we skip this.
if (inv.items.refreshGetPeriod() > 0) then if (inv.items.refreshGetPeriods() > 0) then
dbot.info("Running a full scan to check if anything was moved outside of this client") dbot.info("Running initial full scan to check if your inventory was modified outside of this plugin")
retval = inv.items.refresh(0, invItemsRefreshLocAll, nil, nil) retval = inv.items.refresh(0, invItemsRefreshLocAll, nil, nil)
if (retval ~= DRL_RET_SUCCESS) and (retval ~= DRL_RET_UNINITIALIZED) then if (retval ~= DRL_RET_SUCCESS) and (retval ~= DRL_RET_UNINITIALIZED) then
dbot.info("Initial full inventory rescan could not complete: " .. dbot.retval.getString(retval)) dbot.info("Initial full inventory rescan could not complete: " .. dbot.retval.getString(retval))
@ -1348,8 +1348,8 @@ function inv.config.load()
local retval = dbot.storage.loadTable(dbot.backup.getCurrentDir() .. inv.config.stateName, inv.config.reset) local retval = dbot.storage.loadTable(dbot.backup.getCurrentDir() .. inv.config.stateName, inv.config.reset)
if (retval ~= DRL_RET_SUCCESS) then if (retval ~= DRL_RET_SUCCESS) then
dbot.warn("inv.config.load: Failed to load table from file \"@R" .. dbot.warn("inv.config.load: Failed to load table from file \"@R" .. dbot.backup.getCurrentDir() ..
dbot.backup.getCurrentDir() .. inv.config.stateName .. "@W\": " .. dbot.retval.getString(retval)) inv.config.stateName .. "@W\": " .. dbot.retval.getString(retval))
end -- if end -- if
if (inv.config.table == nil) or (inv.config.table.tableFormat == nil) then if (inv.config.table == nil) or (inv.config.table.tableFormat == nil) then
@ -1390,7 +1390,8 @@ function inv.config.new()
isPromptEnabled = true, isPromptEnabled = true,
isBackupEnabled = true, isBackupEnabled = true,
isBuildExecuted = false, isBuildExecuted = false,
refreshPeriod = 0 refreshPeriod = 0,
refreshEagerSec = 0
} }
end -- inv.config.new end -- inv.config.new
@ -1671,10 +1672,15 @@ function inv.cli.refresh.fn(name, line, wildcards)
inv.tags.stop(invTagsRefresh, line, retval) inv.tags.stop(invTagsRefresh, line, retval)
elseif (command == "on") then elseif (command == "on") then
retval = inv.items.refreshOn(refreshPeriod) retval = inv.items.refreshOn(refreshPeriod, 0)
dbot.note("Inventory refresh is enabled") dbot.note("Inventory refresh is enabled")
inv.tags.stop(invTagsRefresh, line, retval) inv.tags.stop(invTagsRefresh, line, retval)
elseif (command == "eager") then
retval = inv.items.refreshOn(refreshPeriod, inv.items.timer.refreshEagerSec or 0)
dbot.note("Inventory refresh is enabled and uses eager refreshes after acquiring items")
inv.tags.stop(invTagsRefresh, line, retval)
elseif (command == "") or (command == "all") then elseif (command == "") or (command == "all") then
if (inv.state == invStatePaused) then if (inv.state == invStatePaused) then
inv.state = invStateIdle inv.state = invStateIdle
@ -1693,7 +1699,7 @@ end -- inv.cli.refresh.fn
function inv.cli.refresh.usage() function inv.cli.refresh.usage()
dbot.print("@W " .. pluginNameCmd .. " refresh @Y[on | off | all] <minutes>@w") dbot.print("@W " .. pluginNameCmd .. " refresh @Y[on | off | eager | all] <minutes>@w")
end -- inv.cli.refresh.usage end -- inv.cli.refresh.usage
@ -1719,9 +1725,13 @@ There are two types of refreshes: manual and automatic. A manual refresh simply
refresh when the user requests one. An automatic refresh occurs when a timer expires after a refresh when the user requests one. An automatic refresh occurs when a timer expires after a
specified period of time. Automatic refreshes are disabled by default on a new installation. specified period of time. Automatic refreshes are disabled by default on a new installation.
If automatic refreshes are turned on ("@Gdinv refresh on <minutes>@W") then an automatic refresh If automatic refreshes are turned on ("@Gdinv refresh on <minutes>@W") then an automatic refresh
refresh will trigger 5 seconds after an item is added to your inventory and every N minutes since runs every N minutes since the previous automatic refresh (if N is not supplied, it will default
the previous automatic refresh (if N is not supplied, it will default to 5 minutes.) If nothing to 5 minutes.) If nothing has changed since the last refresh, the refresh simply returns.
has changed since the last refresh, the refresh simply returns.
If you really *really* like your inventory to always be up-to-date, you should use the "eager"
refresh mode ("@Gdinv refresh eager <minutes>@W"). This is identical to the "refresh on" mode
described above but it will also schedule a refresh to run 5 seconds after an item is added to
your inventory.
The plugin will skip a refresh or halt it early if you go to sleep, go AFK, enter combat, or hit The plugin will skip a refresh or halt it early if you go to sleep, go AFK, enter combat, or hit
a paging prompt. In this case, any changes that were missed will be picked up the next time a a paging prompt. In this case, any changes that were missed will be picked up the next time a
@ -1738,12 +1748,14 @@ Examples:
"@Gdinv refresh@W" "@Gdinv refresh@W"
2) Disable automatic refreshes 2) Disable automatic refreshes
"@Gdinv refresh off@W" "@Gdinv refresh off@W"
3) Enable automatic refreshes with default times (5 seconds since adding an item or 5 minutes 3) Enable automatic refreshes with the default period (5 minutes since the last refresh)
since the last refresh)
"@Gdinv refresh on@W" "@Gdinv refresh on@W"
4) Enable automatic refreshes with a 10 minute delay between refreshes 4) Enable automatic refreshes with a 10-minute delay between refreshes
"@Gdinv refresh on 10@W" "@Gdinv refresh on 10@W"
5) Perform a manual full refresh scan 5) Enable automatic refreshes with a 7-minute delay between refreshes and an "eager" refresh
a few seconds after a new item is added to your inventory
"@Gdinv refresh eager 7@W"
6) Perform a manual full refresh scan
"@Gdinv refresh all@W" "@Gdinv refresh all@W"
]]) ]])
end -- inv.cli.refresh.examples end -- inv.cli.refresh.examples
@ -4523,10 +4535,11 @@ end -- inv.cli.debug.fn
-- inv.items.refreshCR -- inv.items.refreshCR
-- inv.items.refreshAtTime -- inv.items.refreshAtTime
-- inv.items.refreshDefault() -- inv.items.refreshDefault()
-- inv.items.refreshGetPeriod() -- inv.items.refreshGetPeriods()
-- inv.items.refreshSetPeriod(numMinutes) -- inv.items.refreshSetPeriods(autoMin, eagerSec)
-- inv.items.refreshOn(minutes) -- inv.items.refreshOn(autoMin, eagerSec)
-- inv.items.refreshOff() -- inv.items.refreshOff()
-- inv.items.isDirty()
-- --
-- inv.items.build -- inv.items.build
-- --
@ -4607,7 +4620,7 @@ inv.items.stateName = "inv-items.state"
inv.items.mainState = invItemsRefreshDirty -- state for the main inventory (as detected by invdata) inv.items.mainState = invItemsRefreshDirty -- state for the main inventory (as detected by invdata)
inv.items.wornState = invItemsRefreshDirty -- state for items you are wearing (as detected by eqdata) inv.items.wornState = invItemsRefreshDirty -- state for items you are wearing (as detected by eqdata)
inv.items.keyringState = invItemsRefreshDirty -- state for items on your keyring (as detected by keyring data) inv.items.keyringState = invItemsRefreshDirty -- state for keyring items (as detected by keyring data)
inv.items.burstSize = 20 -- max # of items that can be moved in one atomic operation inv.items.burstSize = 20 -- max # of items that can be moved in one atomic operation
@ -4828,7 +4841,7 @@ function inv.items.init.atActive()
-- If automatic refreshes are enabled (i.e., the period is > 0 minutes), kick off the -- If automatic refreshes are enabled (i.e., the period is > 0 minutes), kick off the
-- refresh timer to periodically scan our inventory and update the inventory table -- refresh timer to periodically scan our inventory and update the inventory table
local refreshPeriod = inv.items.refreshGetPeriod() or inv.items.timer.refreshMin local refreshPeriod = inv.items.refreshGetPeriods() or inv.items.timer.refreshMin
if (refreshPeriod > 0) then if (refreshPeriod > 0) then
inv.items.refreshAtTime(refreshPeriod, 0) inv.items.refreshAtTime(refreshPeriod, 0)
else else
@ -5241,7 +5254,7 @@ function inv.items.discoverCR(maxNumItems, refreshLocations)
if (refreshLocation == invItemsRefreshLocAll) or if (refreshLocation == invItemsRefreshLocAll) or
((refreshLocation == invItemsRefreshLocDirty) and ((refreshLocation == invItemsRefreshLocDirty) and
(not dbot.isWordInString(invItemsRefreshClean, keywordField))) then (not dbot.isWordInString(invItemsRefreshClean, keywordField))) then
dbot.note("Discovering contents of container " .. objId .. ": " .. v[invFieldColorName]) dbot.debug("Discovering contents of container " .. objId .. ": " .. v[invFieldColorName])
-- Discover items in the container -- Discover items in the container
retval = inv.items.discoverLocation(objId) retval = inv.items.discoverLocation(objId)
@ -5799,7 +5812,7 @@ function inv.items.refresh(maxNumItems, refreshLocations, endTag, tagProxy)
-- If we aren't in the "active" character state (sleeping, running, AFK, writing a note, etc.) -- If we aren't in the "active" character state (sleeping, running, AFK, writing a note, etc.)
-- then we wait a bit and try again -- then we wait a bit and try again
elseif (charState ~= dbot.stateActive) then elseif (charState ~= dbot.stateActive) then
dbot.note("Skipping refresh request: char is in state \"" .. dbot.gmcp.getStateString(charState) .. "\"") dbot.debug("Skipping refresh request: char is in state \"" .. dbot.gmcp.getStateString(charState) .. "\"")
retval = DRL_RET_NOT_ACTIVE retval = DRL_RET_NOT_ACTIVE
-- If the char is in the active state (e.g., not AFK, in a note, in combat, etc.) refresh now -- If the char is in the active state (e.g., not AFK, in a note, in combat, etc.) refresh now
@ -5825,9 +5838,9 @@ function inv.items.refresh(maxNumItems, refreshLocations, endTag, tagProxy)
end -- if end -- if
-- Schedule the next refresh if automatic refreshes are enabled (i.e., the period is > 0 minutes) -- Schedule the next refresh if automatic refreshes are enabled (i.e., the period is > 0 minutes)
local refreshMin = inv.items.refreshGetPeriod() or 0 local refreshMin = inv.items.refreshGetPeriods() or 0
if (refreshMin > 0) and (inv.state ~= nil) then if (refreshMin > 0) and (inv.state ~= nil) then
dbot.note("Scheduling automatic inventory refresh in " .. refreshMin .. " minutes") dbot.debug("Scheduling automatic inventory refresh in " .. refreshMin .. " minutes")
inv.items.refreshAtTime(refreshMin, 0) inv.items.refreshAtTime(refreshMin, 0)
end -- if end -- if
@ -5843,9 +5856,19 @@ end -- inv.items.refresh
function inv.items.refreshCR() function inv.items.refreshCR()
local retval local retval = DRL_RET_SUCCESS
dbot.info("Refreshing inventory: START") -- We can skip the refresh if we've already done a full scan, there are no known "dirty"
-- locations or containers, and the user didn't explicitly request a full scan
if inv.items.fullScanCompleted and
(inv.items.refreshPkg.refreshLocations ~= invItemsRefreshLocAll) and
(not inv.items.isDirty()) then
dbot.debug("Skipping refresh because there are no known unidentified items")
inv.state = invStateIdle
return inv.tags.stop(inv.items.refreshPkg.tagModule, inv.items.refreshPkg.endTag, retval)
end -- if
dbot.note("Refreshing inventory: START")
-- Disable the prompt to avoid confusing output during the refresh -- Disable the prompt to avoid confusing output during the refresh
dbot.prompt.hide() dbot.prompt.hide()
@ -5909,7 +5932,7 @@ function inv.items.refreshCR()
resultString = "ERROR! (" .. dbot.retval.getString(retval) .. ")" resultString = "ERROR! (" .. dbot.retval.getString(retval) .. ")"
end -- if end -- if
dbot.info("Refreshing inventory: " .. resultString) dbot.note("Refreshing inventory: " .. resultString)
inv.state = invStateIdle inv.state = invStateIdle
@ -5970,7 +5993,7 @@ function inv.items.refreshDefault()
-- By default, refresh only dirty locations and skip item locations that don't contain any -- By default, refresh only dirty locations and skip item locations that don't contain any
-- unidentified items. -- unidentified items.
if (inv.items.refreshGetPeriod() > 0) then if (inv.items.refreshGetPeriods() > 0) then
retval = inv.items.refresh(0, invItemsRefreshLocDirty, nil, nil) retval = inv.items.refresh(0, invItemsRefreshLocDirty, nil, nil)
end -- if end -- if
@ -5978,38 +6001,74 @@ function inv.items.refreshDefault()
end -- inv.items.refreshDefault end -- inv.items.refreshDefault
function inv.items.refreshGetPeriod() function inv.items.refreshGetPeriods()
return inv.config.table.refreshPeriod return inv.config.table.refreshPeriod, inv.config.table.refreshEagerSec
end -- inv.items.refreshGetPeriod end -- inv.items.refreshGetPeriods
function inv.items.refreshSetPeriods(autoMin, eagerSec)
inv.config.table.refreshPeriod = tonumber(autoMin) or inv.items.timer.refreshMin
inv.config.table.refreshEagerSec = tonumber(eagerSec) or inv.items.timer.refreshEagerSec
function inv.items.refreshSetPeriod(numMinutes)
inv.config.table.refreshPeriod = tonumber(numMinutes) or inv.items.timer.refreshMin
return inv.config.save() return inv.config.save()
end -- inv.items.refreshSetPeriod end -- inv.items.refreshSetPeriods
function inv.items.refreshOn(numMinutes) function inv.items.refreshOn(autoMin, eagerSec)
numMinutes = tonumber(numMinutes or "") or inv.items.timer.refreshMin autoMin = tonumber(autoMin or "") or inv.items.timer.refreshMin
if (numMinutes < 1) then if (autoMin < 1) then
dbot.warn("inv.items.refreshOn: Automatic refreshes must have a period of at least one minute") dbot.warn("inv.items.refreshOn: Automatic refreshes must have a period of at least one minute")
return DRL_RET_INVALID_PARAM return DRL_RET_INVALID_PARAM
end -- if end -- if
inv.items.refreshSetPeriod(numMinutes) inv.items.refreshSetPeriods(autoMin, eagerSec or 0)
inv.state = invStateIdle
-- Schedule the next refresh -- Schedule the next refresh
return inv.items.refreshAtTime(numMinutes, 0) return inv.items.refreshAtTime(autoMin, 0)
end -- inv.items.refreshOn end -- inv.items.refreshOn
function inv.items.refreshOff() function inv.items.refreshOff()
inv.state = invStatePaused inv.state = invStatePaused
dbot.deleteTimer(inv.items.timer.refreshName) dbot.deleteTimer(inv.items.timer.refreshName)
return inv.items.refreshSetPeriod(0) return inv.items.refreshSetPeriods(0, 0)
end -- inv.items.refreshOff end -- inv.items.refreshOff
-- This checks all locations and determines if there are any known unidentified items. If there
-- is at least one unidentified item, isDirty() returns true. Otherwise, it returns false.
function inv.items.isDirty()
local isDirty = false
-- Check the easy locations first. If something unidentified is worn, on your keyring, or in
-- your main inventory, return true. We don't even need to look at containers.
if (inv.items.wornState == invItemsRefreshDirty) or
(inv.items.mainState == invItemsRefreshDirty) or
(inv.items.keyringState == invItemsRefreshDirty) then
isDirty = true
-- Check containers to see if any are "dirty" and hold at least one unidentified item
else
-- For every item in your inventory, check if it's a container. If it is a container we must
-- next check if it has the "clean" keyword indicating that it hasn't had any unidentified
-- items added to it since its last scan. If it's not "clean", then it's "dirty".
for objId,_ in pairs(inv.items.table) do
if (inv.items.getStatField(objId, invStatFieldType) == invmon.typeStr[invmonTypeContainer]) then
local keywordField = inv.items.getStatField(objId, invStatFieldKeywords) or ""
if (not dbot.isWordInString(invItemsRefreshClean, keywordField)) then
isDirty = true
break
end -- if
end -- if
end -- for
end -- if
return isDirty
end -- inv.items.isDirty
function inv.items.build(endTag) function inv.items.build(endTag)
local retval local retval
@ -7826,8 +7885,10 @@ function inv.items.displayItem(objId, verbosity, wearableLoc)
end end
if (#strip_colours(formattedName) < maxNameLen - #formattedId) then if (#strip_colours(formattedName) < maxNameLen - #formattedId) then
formattedName = formattedName .. string.rep(" ", maxNameLen - #strip_colours(formattedName) - #formattedId) formattedName = formattedName ..
string.rep(" ", maxNameLen - #strip_colours(formattedName) - #formattedId)
end -- if end -- if
-- The trimmed name could end on an "@" which messes up color codes and spacing -- The trimmed name could end on an "@" which messes up color codes and spacing
formattedName = string.gsub(formattedName, "@$", " ") .. " " .. DRL_XTERM_GREY formattedName = string.gsub(formattedName, "@$", " ") .. " " .. DRL_XTERM_GREY
formattedName = formattedName .. colorizedId formattedName = formattedName .. colorizedId
@ -9370,8 +9431,9 @@ function inv.items.trigger.invmon(action, objId, containerId, wearLoc)
-- inventory refresh a few seconds from now. That will give some time to buffer up a few items -- inventory refresh a few seconds from now. That will give some time to buffer up a few items
-- if we picked up several things. -- if we picked up several things.
local idLevel = inv.items.getField(objId, invFieldIdentifyLevel) local idLevel = inv.items.getField(objId, invFieldIdentifyLevel)
if (idLevel == invIdLevelNone) and (inv.state == invStateIdle) then local eagerRefreshSec = tonumber(inv.config.table.refreshEagerSec or 0)
inv.items.refreshAtTime(0, 5) if (idLevel == invIdLevelNone) and (inv.state == invStateIdle) and (eagerRefreshSec > 0) then
inv.items.refreshAtTime(0, eagerRefreshSec)
end -- if end -- if
if (action == invmonActionRemoved) then if (action == invmonActionRemoved) then
@ -9511,6 +9573,7 @@ inv.items.timer = {}
inv.items.timer.refreshName = "drlInvItemsTimerRefresh" inv.items.timer.refreshName = "drlInvItemsTimerRefresh"
inv.items.timer.refreshMin = 5 -- by default, run the item refresh timer every 5 minutes, 0 seconds inv.items.timer.refreshMin = 5 -- by default, run the item refresh timer every 5 minutes, 0 seconds
inv.items.timer.refreshSec = 0 inv.items.timer.refreshSec = 0
inv.items.timer.refreshEagerSec = 5 -- If enabled, run a refresh 5 seconds after acquiring a new item
inv.items.timer.idTimeoutName = "drlInvItemsTimerIdTimeout" inv.items.timer.idTimeoutName = "drlInvItemsTimerIdTimeout"
inv.items.timer.idTimeoutThresholdSec = 15 -- timeout the id request if it doesn't complete in this # sec inv.items.timer.idTimeoutThresholdSec = 15 -- timeout the id request if it doesn't complete in this # sec

Loading…
Cancel
Save