mckenziemc@0: -- Addon mckenziemc@0: -- Represents individual addon modules mckenziemc@0: mckenziemc@0: mckenziemc@0: local addonName, addonTable = ... mckenziemc@0: mckenziemc@0: mckenziemc@0: -- NOTE: I assume that the API addon functions are mckenziemc@0: -- slightly quicker with an index than with a number. mckenziemc@0: mckenziemc@0: -- TODO: modify the dependency stuff to use the Errata module if available mckenziemc@0: mckenziemc@0: local Addon, addon = addonTable:NewClass("Addon") mckenziemc@0: mckenziemc@8: mckenziemc@8: -- load ability masks mckenziemc@8: Addon.loadMasks = { mckenziemc@8: reload = bit.lshift(1, 0), -- can load after reloadui mckenziemc@8: ondemand = bit.lshift(1, 1), -- can load on demand mckenziemc@8: forceafter = bit.lshift(1, 2), -- force load after it would normally be loaded mckenziemc@8: forcebefore = bit.lshift(1, 3), -- force load before it would normally be loaded mckenziemc@8: } mckenziemc@8: mckenziemc@8: mckenziemc@8: Addon.addons = {} mckenziemc@8: Addon.nameToIndex = {} mckenziemc@8: mckenziemc@8: mckenziemc@8: -- Internal function mckenziemc@8: -- Creates a new Addon object mckenziemc@8: -- @param id Name or index of the addon. mckenziemc@0: function Addon:New(id) mckenziemc@0: local instance = {} mckenziemc@0: setmetatable(instance, self.instanceMetatable) mckenziemc@0: mckenziemc@0: if type(id) == "number" then mckenziemc@0: -- TODO: make sure it's in range mckenziemc@0: instance.index = id mckenziemc@0: instance.name = GetAddOnInfo(id) mckenziemc@0: else mckenziemc@0: -- FIXME: allow blizzard addons? mckenziemc@0: local index mckenziemc@0: mckenziemc@0: for i=1,GetNumAddOns() do mckenziemc@0: if GetAddOnInfo(i) == id then mckenziemc@0: index = i mckenziemc@0: break mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@0: if index then mckenziemc@0: instance.name = GetAddOnInfo(id) mckenziemc@0: instance.index = index mckenziemc@0: else mckenziemc@0: error("Addon not found") mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@0: return instance mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@8: --- Retrieves an Addon object. mckenziemc@8: -- @param id Name or index of the addon to retrieve. mckenziemc@8: -- @return The Addon object, or nil if not found. mckenziemc@8: function Addon:Get(id) mckenziemc@8: if not self:Exists(id) then mckenziemc@8: return nil mckenziemc@8: end mckenziemc@8: mckenziemc@8: if type(id) == "number" then mckenziemc@8: if self.addons[id] ~= nil then mckenziemc@8: return self.addons[id] mckenziemc@8: end mckenziemc@8: mckenziemc@8: local new = self:New(id) mckenziemc@8: self.addons[new:GetIndex()] = new mckenziemc@8: self.nameToIndex[new:GetName()] = id mckenziemc@8: mckenziemc@8: return new mckenziemc@8: elseif type(id) == "string" then mckenziemc@8: if self.nameToIndex[id] then mckenziemc@8: return self.addons[self.nameToIndex[id] ] mckenziemc@8: end mckenziemc@8: mckenziemc@8: local new = self:New(id) mckenziemc@8: self.addons[new:GetIndex()] = new mckenziemc@8: self.nameToIndex[id] = new:GetIndex() mckenziemc@8: mckenziemc@8: return new mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@0: -- Checks if an addon exists with the specified name. mckenziemc@0: -- @param addon Name of the addon. mckenziemc@0: -- @return True if the addon is present, false otherwise. mckenziemc@0: function Addon:Exists(addon) mckenziemc@0: if type(addon) == "number" then mckenziemc@0: if addon >= 1 and addon <= GetNumAddOns() then mckenziemc@0: return true mckenziemc@0: else mckenziemc@0: return false mckenziemc@0: end mckenziemc@0: elseif type(addon) == "string" then mckenziemc@0: local status = select(6, GetAddOnInfo(addon)) mckenziemc@0: mckenziemc@0: if status == "MISSING" then mckenziemc@0: return false mckenziemc@0: else mckenziemc@0: return true mckenziemc@0: end mckenziemc@0: else mckenziemc@0: error() mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@8: function Addon:GetLoadMasks() mckenziemc@8: return self.masks mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@0: function addon:GetName() mckenziemc@0: return self.name mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@0: function addon:GetIndex() mckenziemc@0: return self.index mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@0: function addon:IsEnabled() mckenziemc@0: -- FIXME: written while tired; review later mckenziemc@0: local status = select(6, GetAddOnInfo(self.index)) mckenziemc@0: mckenziemc@0: if status == "DISABLED" then mckenziemc@0: return false mckenziemc@0: else mckenziemc@0: return true mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@8: function addon:GetLoadBitfield() mckenziemc@8: local bitfield = 0 mckenziemc@8: mckenziemc@8: if self:CanLoad() then mckenziemc@8: bitfield = bitfield + self.masks.reload mckenziemc@8: end mckenziemc@8: mckenziemc@8: if self:CanLoD() then mckenziemc@8: bitfield = bitfield + self.masks.ondemand mckenziemc@8: end mckenziemc@8: mckenziemc@8: if self:CanForceLoadAfter() then mckenziemc@8: bitfield = bitfield + self.masks.forceafter mckenziemc@8: end mckenziemc@8: mckenziemc@8: if self:CanForceLoadBefore() then mckenziemc@8: bitfield = bitfield + self.masks.forcebefore mckenziemc@8: end mckenziemc@8: mckenziemc@8: return bitfield mckenziemc@0: end mckenziemc@0: mckenziemc@8: mckenziemc@8: --- Checks if the addon is loadable. mckenziemc@8: -- Considers interface issues, missing, etc. (NYI) mckenziemc@8: -- Does not check dependencies. mckenziemc@8: function addon:CanLoad() mckenziemc@8: -- FIXME: an addon may be present but unloadable if loading out of date addons is disabled. mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@0: function addon:CanLoD() mckenziemc@0: -- FIXME: what will the client say about addons using LoadManagers if the LM was force-loaded? mckenziemc@0: if IsAddOnLoadOnDemand(self.name) then mckenziemc@0: return true mckenziemc@0: else mckenziemc@0: return false mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@8: mckenziemc@8: function addon:CanForceLoadAfter() mckenziemc@8: -- TODO: check Errata module mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function addon:CanForceLoadBefore() mckenziemc@8: -- TODO: check Errata module mckenziemc@8: return false -- TODO: check if there's any reason addons can't be forceloaded mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function addon:Enable() mckenziemc@8: -- FIXME: delay this till after PLAYER_LOGIN or it'll get force-loaded mckenziemc@8: EnableAddOn(self.name) mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@0: -- NOTE: only call for LoD, not force-loading mckenziemc@0: function addon:Load() mckenziemc@0: assert(self:CanLoD()) mckenziemc@0: mckenziemc@0: EnableAddOn(self.name) mckenziemc@0: LoadAddOn(self.name) mckenziemc@0: end mckenziemc@0: mckenziemc@8: mckenziemc@0: function addon:ForceLoad() mckenziemc@0: assert(self:CanForceLoad()) mckenziemc@0: -- TODO: make sure force-loading is available at this time mckenziemc@0: mckenziemc@0: EnableAddOn(self.name) -- This should cause the game to also load this addon mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@0: function addon:GetDependencies() mckenziemc@8: -- TODO: consider no-lib embeds as deps? mckenziemc@0: return GetAddOnDependencies(self.index) mckenziemc@0: end mckenziemc@0: mckenziemc@0: mckenziemc@0: function addon:GetEmbeds() mckenziemc@0: local embeds = {} mckenziemc@0: mckenziemc@0: local embedString = GetAddOnMetadata(self.name, "X-Embeds") mckenziemc@0: mckenziemc@0: if embedString then mckenziemc@0: for match in string.gmatch(embedString, "[^,%s]+") do mckenziemc@0: table.insert(embeds, match) mckenziemc@0: end mckenziemc@0: end mckenziemc@0: mckenziemc@0: return unpack(embeds) mckenziemc@0: end mckenziemc@0: mckenziemc@0: function addon:IsLoaded() mckenziemc@0: if IsAddOnLoaded(self.index) then mckenziemc@0: return true mckenziemc@0: else mckenziemc@0: return false mckenziemc@0: end mckenziemc@0: end