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
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
|
|
|