You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
8.6 KiB

--[[
ODTables: On-Demand Tables
There are times when data needs to be gathered in a table but the data
doesn't need to hang around, such as cache tables; or it doesn't need
gathered upon login but it should automatically gather when the table
becomes relevant, such as sourceIDs.
ODTables are intended to maximize table reuse, minimize garbage creation,
and avoid filling tables with data until the data is actually needed.
When created, an ODTable is empty. Once activated, via indexing or via
table:Activate(), it will become active and run its populateFunc if one
was defined when the table was created. After 1/4 of a second, or when the
table is invalidated by table:Invalidate() or table:Deactivate(), the
ODTable is wiped and returned to an inactive state.
Persistent tables will not automatically invalidate over time, so the data
remains; but it should be invalidated when info may change, such as when
a new team is pushed.
To create an ODTable:
odTable = rematch:CreateODTable(populateFunc,persist)
populateFunc (function) function to run when the table is activated
persist (bool) to only wipe the table when it's explicitly invalidated
To activate an ODTable, either:
odTable:Activate() -- directly activating the table
or
local x = odTable[petID] -- activating by indexing it
(if an inactive table is indexed, it will be activated automatically)
To invalidate an ODTable, one of:
odTable:Deactivate()
or
odTable:Invalidate()
or
if not persistent, let 1/4 second pass and it will wipe on its own
Note: If iterating over a ODTable without indexing it, make sure to
table:Activate() it first. It's ok if it was already activated.
]]
local rematch = Rematch
local tableDuration = 0.25 -- time (in seconds) before an active ODTable goes inactive
local timerIndex = 1 -- used for naming timers, incremented for each ODTable created
-- this table is indexed by ODTable table references, and contains tables of
-- attributes: isActive, populateFunc, isPersistent, timerName
local tableAttributes = {}
-- local functions defined later
local lookup, activateTable, deactivateTable
-- a few keys have special meaning to activate or deactivate a table
local ODTableAPI = {
Activate = "activateFunc",
Deactivate = "deactivateFunc",
Invalidate = "deactivateFunc",
}
-- define a few global ODTables:
rematch:InitModule(function()
-- petsInTeams is a persistent table indexed by petIDs (can be a speciesID)
-- that equals number of teams the petID belongs to, or nil if none.
-- note: when teams are pushed, this table should be invalidated
rematch.petsInTeams = rematch:CreateODTable(function(self)
for _,team in pairs(RematchSaved) do
for i=1,3 do
local petID = team[i][1]
if petID then
self[petID] = (self[petID] or 0) + 1
end
end
end
end,true)
-- sourceIDs is a persistent table indexed by speciesIDs of the source
-- for each species (1=Drop, 2=Quest, 3=Vendor, etc)
-- because it's relatively expensive to populate the table (need to set 11 different
-- filters and gather results) this one doesn't invalidate
-- but because it's not used for most sessions, it's only created on demand
rematch.sourceIDs = rematch:CreateODTable(function(self)
Rematch.Roster:ExpandJournalFilters()
for sourceID=1,C_PetJournal.GetNumPetSources() do -- going through all pet sources
-- set source filter to single category
for i=1,C_PetJournal.GetNumPetSources() do
C_PetJournal.SetPetSourceChecked(i,i==sourceID)
end
-- fill sourceIDs with the source-filtered results
for i=1,C_PetJournal.GetNumPets() do
local _,speciesID = C_PetJournal.GetPetInfoByIndex(i)
if not self[speciesID] then
self[speciesID] = sourceID
end
end
end
rematch.Roster:RestoreJournalFilters() -- put everything back how it was
end,true)
-- speciesAt25 is a temporary table indexed by speciesIDs, true if the species
-- has at least one level 25 pet.
rematch.speciesAt25 = rematch:CreateODTable(function(self)
for petID in rematch.Roster:AllOwnedPets() do
local speciesID,_,level = C_PetJournal.GetPetInfoByPetID(petID)
if level==25 then
self[speciesID] = true
end
end
end)
-- movesetsBySpecies is a persistent table indexed by speciesID, that contains
-- the moveset for each species. a moveset is all abilities concatenated into a
-- string such as "429,492,538,535,357,536" for a black tabby cat. note that a
-- moveset is defined as the same abilities in the same order; which makes it
-- different than "similar" abilities.
rematch.movesetsBySpecies = rematch:CreateODTable(function(self)
for speciesID in rematch.Roster:AllSpecies() do
local petInfo = rematch.petInfo:Fetch(speciesID,true)
self[speciesID] = table.concat(petInfo.abilityList,",")
end
end,true)
-- movesetsAt25 is a temporary table indexed by movesets, true if the moveset is
-- used by at least one species at level 25.
rematch.movesetsAt25 = rematch:CreateODTable(function(self)
rematch.speciesAt25:Activate()
for speciesID in pairs(rematch.speciesAt25) do
self[rematch.movesetsBySpecies[speciesID]] = true
end
end)
-- uniqueMovesets is a temporary table indexed by movesets, true if the moveset
-- is only used by one species.
rematch.uniqueMovesets = rematch:CreateODTable(function(self)
rematch.movesetsBySpecies:Activate() -- movesetsBySpecies needs activated since it's not indexed
-- first count the number of times each moveset is used
for speciesID,moveset in pairs(rematch.movesetsBySpecies) do
self[moveset] = (self[moveset] or 0) + 1
end
-- next remove any that have more than 1
for moveset,count in pairs(self) do
if count>1 then
self[moveset] = nil
end
end
end,true)
end)
-- creates a ODTable, sets up its attributes, and returns the new table
function rematch:CreateODTable(populateFunc,isPersistent)
local odTable = {}
tableAttributes[odTable] = {}
local attributes = tableAttributes[odTable]
attributes.populateFunc = populateFunc
attributes.isPersistent = isPersistent
attributes.timerName = "ODTable"..timerIndex
attributes.activateFunc = function() return activateTable(odTable) end
attributes.deactivateFunc = function() return deactivateTable(odTable) end
timerIndex = timerIndex + 1
setmetatable(odTable,{__index=lookup})
return odTable
end
-- __index function when a table's key value is nil
function lookup(self,key)
local attributes = tableAttributes[self]
-- if doing a table:Activate(), table:Deactivate() or table:Invalidate() return appropriate function
local api = ODTableAPI[key]
if api then
return attributes[api]
end
-- otherwise first activate table if not already activated
if not attributes.isActive then
activateTable(self)
end
-- and return raw value of table (filled in after activation or still nil)
return rawget(self,key)
end
-- called either directly via table:Activate() or indexing a nil entry
-- flags a table active, runs its populateFunc if it exists, and starts timer to deactivate
function activateTable(self)
local attributes = tableAttributes[self]
if not attributes.isActive then -- only activate if it wasn't activated
attributes.isActive = true
if attributes.populateFunc then -- if table has a function to populate on activation
attributes.populateFunc(self) -- run it
end
end
-- if table is not persistent, start timer to deactivate
-- (this will restart the timer in the event an Activate() happens while already activated)
if not attributes.isPersistent then
rematch:StartTimer(attributes.timerName,tableDuration,attributes.deactivateFunc)
end
return self
end
-- called either directly via table:Deactivate()/Invalidate() or after 1/4 seconds pass after table activates
-- wipes table, flags it as inactive, and stops a timer if one is running
function deactivateTable(self)
local attributes = tableAttributes[self]
wipe(self)
attributes.isActive = nil
if not attributes.isPersistent then
rematch:StopTimer(attributes.timerName)
end
return self
end