Mercurial > wow > dependencyloader
changeset 8:930871e163bc
created a new Tree class and started rearranging code
author | mckenziemc |
---|---|
date | Sat, 04 Dec 2010 23:05:34 -0800 |
parents | f4ab9d7b97b0 |
children | 5362e308c3eb |
files | DependencyLoader_Core/Addon.lua DependencyLoader_Core/Core.lua DependencyLoader_Core/Tree.lua DependencyLoader_Core/class.lua |
diffstat | 4 files changed, 393 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/DependencyLoader_Core/Addon.lua Sat Dec 04 14:45:36 2010 -0800 +++ b/DependencyLoader_Core/Addon.lua Sat Dec 04 23:05:34 2010 -0800 @@ -4,7 +4,6 @@ local addonName, addonTable = ... -print("running Addon.lua") -- NOTE: I assume that the API addon functions are -- slightly quicker with an index than with a number. @@ -13,11 +12,25 @@ local Addon, addon = addonTable:NewClass("Addon") + +-- load ability masks +Addon.loadMasks = { + reload = bit.lshift(1, 0), -- can load after reloadui + ondemand = bit.lshift(1, 1), -- can load on demand + forceafter = bit.lshift(1, 2), -- force load after it would normally be loaded + forcebefore = bit.lshift(1, 3), -- force load before it would normally be loaded +} + + +Addon.addons = {} +Addon.nameToIndex = {} + + +-- Internal function +-- Creates a new Addon object +-- @param id Name or index of the addon. function Addon:New(id) - assert(type(id) == "number" or type(id) == "string") - local instance = {} - setmetatable(instance, self.instanceMetatable) if type(id) == "number" then @@ -47,6 +60,37 @@ end +--- Retrieves an Addon object. +-- @param id Name or index of the addon to retrieve. +-- @return The Addon object, or nil if not found. +function Addon:Get(id) + if not self:Exists(id) then + return nil + end + + if type(id) == "number" then + if self.addons[id] ~= nil then + return self.addons[id] + end + + local new = self:New(id) + self.addons[new:GetIndex()] = new + self.nameToIndex[new:GetName()] = id + + return new + elseif type(id) == "string" then + if self.nameToIndex[id] then + return self.addons[self.nameToIndex[id] ] + end + + local new = self:New(id) + self.addons[new:GetIndex()] = new + self.nameToIndex[id] = new:GetIndex() + + return new + end +end + -- Checks if an addon exists with the specified name. -- @param addon Name of the addon. -- @return True if the addon is present, false otherwise. @@ -71,6 +115,11 @@ end +function Addon:GetLoadMasks() + return self.masks +end + + function addon:GetName() return self.name end @@ -92,13 +141,39 @@ end end --- FIXME: an addon may be present but unloadable if loading out of date addons is disabled. --- NOTE: CanForceLoad and CanLoD don't check the status of dependencies -function addon:CanForceLoad() - return true -- TODO: check if there's any reason addons can't be forceloaded +function addon:GetLoadBitfield() + local bitfield = 0 + + if self:CanLoad() then + bitfield = bitfield + self.masks.reload + end + + if self:CanLoD() then + bitfield = bitfield + self.masks.ondemand + end + + if self:CanForceLoadAfter() then + bitfield = bitfield + self.masks.forceafter + end + + if self:CanForceLoadBefore() then + bitfield = bitfield + self.masks.forcebefore + end + + return bitfield end + +--- Checks if the addon is loadable. +-- Considers interface issues, missing, etc. (NYI) +-- Does not check dependencies. +function addon:CanLoad() + -- FIXME: an addon may be present but unloadable if loading out of date addons is disabled. + return true +end + + function addon:CanLoD() -- FIXME: what will the client say about addons using LoadManagers if the LM was force-loaded? if IsAddOnLoadOnDemand(self.name) then @@ -108,6 +183,25 @@ end end + +function addon:CanForceLoadAfter() + -- TODO: check Errata module + return false +end + + +function addon:CanForceLoadBefore() + -- TODO: check Errata module + return false -- TODO: check if there's any reason addons can't be forceloaded +end + + +function addon:Enable() + -- FIXME: delay this till after PLAYER_LOGIN or it'll get force-loaded + EnableAddOn(self.name) +end + + -- NOTE: only call for LoD, not force-loading function addon:Load() assert(self:CanLoD()) @@ -116,6 +210,7 @@ LoadAddOn(self.name) end + function addon:ForceLoad() assert(self:CanForceLoad()) -- TODO: make sure force-loading is available at this time @@ -125,6 +220,7 @@ function addon:GetDependencies() + -- TODO: consider no-lib embeds as deps? return GetAddOnDependencies(self.index) end
--- a/DependencyLoader_Core/Core.lua Sat Dec 04 14:45:36 2010 -0800 +++ b/DependencyLoader_Core/Core.lua Sat Dec 04 23:05:34 2010 -0800 @@ -1,16 +1,11 @@ + local addonName, addonTable = ... -print("running Core.lua") - --- TODO: prevent infinite loops in the recursive functions - - local Core, core = addonTable:NewClass("Core") -local Addon = addonTable.classes.Addon function Core:New() local instance = {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DependencyLoader_Core/Tree.lua Sat Dec 04 23:05:34 2010 -0800 @@ -0,0 +1,287 @@ +-- Tree +-- Represents a recursive "tree" of addon +-- dependencies rooted at a specific addon. + + +local addonName, addonTable = ... + + +-- TODO: prevent infinite loops in the recursive functions + + +local Tree, tree = addonTable:NewClass("Tree") + + +Tree.trees = {} + +-- internal +-- Creates a new tree object +-- @param root Name, index, or Addon object of the root addon. +function Tree:New(root) + if type(root) ~= "table" then + root = addonTable.classes.Addon:Get(root) + end + + local instance = {} + setmetatable(instance, self.instanceMetatable) + + instance.root = root + + return instance +end + + +--- Retrieves the tree rooted at the specified addon +-- @param root Name, index, or Addon object of the root. +function Tree:Get(root) + if type(root) ~= "table" then + root = addonTable.classes.Addon:Get(root) + end + + local tree = self.trees[root] + + if tree then + return tree + else + tree = self:New(root) + self.trees[root] = tree + return tree + end +end + + +function tree:GetLoadBitfield() + local bitfield = self.root:GetLoadBitfield() + + local dependencies = {self.root:GetDependencies()} + for i, depName in pairs(dependencies) do + local depTree = Tree:Get(depName) + + if depTree == nil then + return 0 + end + + bitfield = bit.band(bitfield, depTree:GetLoadBitfield()) + end + + return bitfield +end + + +-- Checks if the tree rooted at the specified addon can be force-loaded. +-- @param root The name or index of the root addon. +function core:CanForceLoadTree(root) + -- TODO: if some addons have already loaded, we have to check + -- forceafter for those and forcebefore for the others + return false +end + + +-- NOTE: any tree that can be loaded on demand is also eligible for force-loading +-- Checks if the tree rooted at the specified addon can be loaded on demand. +-- @param root The name or index of the root addon. +function core:CanLoDTree(root) + local bitfield = self:GetLoadBitfield() + root = self:GetAddon(root) + assert(root) + + -- since this will be used recursively, return true if + -- this is already loaded. + if root:IsLoaded() then + return true + end + + -- true if all dependencies are loaded or LoD + + if not root:CanLoD() then + return false + end + + -- now check dependencies recursively + local dependencies = {root:GetDependencies()} + for i, depName in pairs(dependencies) do + local dep = self:GetAddon(depName) + + if not dep then + return false -- missing + end + + if not dep:IsLoaded() then + if not self:CanLoDTree(depName) then + return false + end + end + end + + return true +end + + +-- Checks if the tree rooted at the specified addon +-- can be loaded if all dependencies are enabled +-- the UI is reloaded. +-- Basically makes sure all dependencies are installed. +function core:CanReloadTree(root) + -- convert root to an Addon object + root = self:GetAddon(root) + assert(root) + + if root:IsLoaded() then + return true + -- FIXME: deps may have been disabled + end + + -- TODO: make sure the root isn't incompatible + + -- now check dependencies recursively + -- FIXME: prevent infinite recursion + + local dependencies = {root:GetDependencies()} + for i, depName in pairs(dependencies) do + local dep = self:GetAddon(depName) + + if dep == nil then + return false -- missing dependency + end + + -- TODO: make sure it's compatible + end + + return true +end + +-- Loads the tree rooted at the specified addon. +-- FIXME: load the root too or not? +-- Supports both LoD addons and those that require force-loading. +function core:LoadTree(addon) + -- don't check if the tree can actually be loaded. + -- external code should do that itself to check if it + -- should even call this at all. + + if self:ForceLoadAvailable() then + -- LoD trees can also be force-loaded + self:ForceLoadTree(addon) + else + self:LoadLoDTree(addon) + end +end + + +-- load the root too, since it may actually be a leaf +function core:ForceLoadTree(root) + root = self:GetAddon(root) + assert(root) + + -- load dependencies + local dependencies = {root:GetDependencies(addon)} + for i, depName in pairs(dependencies) do + self:ForceLoadTree(depName) + end + + -- load embeds, if they are available separately + local embeds = {root:GetEmbeds(addon)} + for i, embedName in pairs(embeds) do + if Addon:Exists(embedName) then + self:ForceLoadTree(embedName) + end + end + + root:ForceLoad() +end + + +function core:LoadLoDTree(root) + root = self:GetAddon(root) + assert(root) + + -- load dependencies + local dependencies = {root:GetDependencies(addon)} + for i, depName in pairs(dependencies) do + self:LoadLoDTree(depName) + end + + -- load embeds, if they are available separately + local embeds = {root:GetEmbeds(addon)} + for i, embedName in pairs(embeds) do + if Addon:Exists(embedName) then + self:LoadLoDTree(embedName) + end + end + + root:LoD() +end + + +-- I think the problem this solves is a major issue with +-- migrating to separate libs. think about it more and document +-- here and in project description +function core:PrepareLoDTree(root) + root = self:GetAddon(root) + assert(root) + + -- assume root is LoD + + -- check dependencies + local dependencies = {root:GetDependencies(addon)} + for i, depName in pairs(dependencies) do + local dep = self:GetAddon(depName) + + -- assume external code made sure it exists + + if dep:CanLoD() then + -- don't load it now but make sure its dependencies are prepared + self:PrepareLoDTree(depName) + else + -- FIXME: if it's already loaded + -- force-load it now so we can load the parent on demand + self:ForceLoadTree(depName) + end + end + + -- prepare embeds, if they are available separately + local embeds = {root:GetEmbeds(addon)} -- FIXME: addon? + for i, embedName in pairs(embeds) do + local embed = self:GetAddon(embedName) + + if embed then + if embed:CanLoD() then + -- don't load it now but make sure its dependencies are prepared + self:PrepareLoDTree(embedName) + else + -- FIXME: if it's already loaded + -- force-load it now so we can load the parent on demand + self:ForceLoadTree(depName) + end + end + end +end + + +function Core:PrepareReloadTree(addon) + root = self:GetAddon(root) + assert(root) + + root:Enable() + + -- check dependencies + local dependencies = {root:GetDependencies()} + for i, depName in pairs(dependencies) do + self:PrepareReloadTree(depName) + end + + -- prepare embeds, if they are available separately + local embeds = {root:GetEmbeds(addon)} + for i, embedName in pairs(embeds) do + local embed = self:GetAddon(embedName) + + if embed then + self:PrepareReloadTree(embedName) + end + end +end + + +function Core:ForceLoadAvailable() + return true + -- FIXME: use field and a frame registered for PLAYER_LOGIN +end