?? dongle.lua
字號:
--[[------------------------------------------------------------------------- Copyright (c) 2006-2007, Dongle Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dongle Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.---------------------------------------------------------------------------]]local major = "DongleStub"local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1)local g = getfenv(0)if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then local lib = setmetatable({}, { __call = function(t,k) if type(t.versions) == "table" and t.versions[k] then return t.versions[k].instance else error("Cannot find a library with name '"..tostring(k).."'", 2) end end }) function lib:IsNewerVersion(major, minor) local versionData = self.versions and self.versions[major] -- If DongleStub versions have differing major version names -- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second -- instance will be loaded, with older logic. This code attempts -- to compensate for that by matching the major version against -- "^DongleStub", and handling the version check correctly. if major:match("^DongleStub") then local oldmajor,oldminor = self:GetVersion() if self.versions and self.versions[oldmajor] then return minor > oldminor else return true end end if not versionData then return true end local oldmajor,oldminor = versionData.instance:GetVersion() return minor > oldminor end local function NilCopyTable(src, dest) for k,v in pairs(dest) do dest[k] = nil end for k,v in pairs(src) do dest[k] = v end end function lib:Register(newInstance, activate, deactivate) assert(type(newInstance.GetVersion) == "function", "Attempt to register a library with DongleStub that does not have a 'GetVersion' method.") local major,minor = newInstance:GetVersion() assert(type(major) == "string", "Attempt to register a library with DongleStub that does not have a proper major version.") assert(type(minor) == "number", "Attempt to register a library with DongleStub that does not have a proper minor version.") -- Generate a log of all library registrations if not self.log then self.log = {} end table.insert(self.log, string.format("Register: %s, %s", major, minor)) if not self:IsNewerVersion(major, minor) then return false end if not self.versions then self.versions = {} end local versionData = self.versions[major] if not versionData then -- New major version versionData = { ["instance"] = newInstance, ["deactivate"] = deactivate, } self.versions[major] = versionData if type(activate) == "function" then table.insert(self.log, string.format("Activate: %s, %s", major, minor)) activate(newInstance) end return newInstance end local oldDeactivate = versionData.deactivate local oldInstance = versionData.instance versionData.deactivate = deactivate local skipCopy if type(activate) == "function" then table.insert(self.log, string.format("Activate: %s, %s", major, minor)) skipCopy = activate(newInstance, oldInstance) end -- Deactivate the old libary if necessary if type(oldDeactivate) == "function" then local major, minor = oldInstance:GetVersion() table.insert(self.log, string.format("Deactivate: %s, %s", major, minor)) oldDeactivate(oldInstance, newInstance) end -- Re-use the old table, and discard the new one if not skipCopy then NilCopyTable(newInstance, oldInstance) end return oldInstance end function lib:GetVersion() return major,minor end local function Activate(new, old) -- This code ensures that we'll move the versions table even -- if the major version names are different, in the case of -- DongleStub if not old then old = g.DongleStub end if old then new.versions = old.versions new.log = old.log end g.DongleStub = new end -- Actually trigger libary activation here local stub = g.DongleStub or lib lib = stub:Register(lib, Activate)end--[[------------------------------------------------------------------------- Begin Library Implementation---------------------------------------------------------------------------]]local major = "Dongle-1.0"local minor = tonumber(string.match("$Revision: 371 $", "(%d+)") or 1) + 500-- ** IMPORTANT NOTE **-- Due to some issues we had previously with Dongle revision numbers-- we need to artificially inflate the minor revision number, to ensure-- we load sequentially.assert(DongleStub, string.format("%s requires DongleStub.", major))if not DongleStub:IsNewerVersion(major, minor) then return endlocal Dongle = {}local methods = { "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered", "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered", "EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF", "InitializeDB", "InitializeSlashCommand", "NewModule", "HasModule", "IterateModules",}local registry = {}local lookup = {}local loadqueue = {}local loadorder = {}local events = {}local databases = {}local commands = {}local messages = {}local frame--[[------------------------------------------------------------------------- Message Localization---------------------------------------------------------------------------]]local L = { ["ADDMESSAGE_REQUIRED"] = "The frame you specify must have an 'AddMessage' method.", ["ALREADY_REGISTERED"] = "A Dongle with the name '%s' is already registered.", ["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)", ["BAD_ARGUMENT_DB"] = "bad argument #%d to '%s' (DongleDB expected)", ["CANNOT_DELETE_ACTIVE_PROFILE"] = "You cannot delete your active profile. Change profiles, then attempt to delete.", ["DELETE_NONEXISTANT_PROFILE"] = "You cannot delete a non-existant profile.", ["MUST_CALLFROM_DBOBJECT"] = "You must call '%s' from a Dongle database object.", ["MUST_CALLFROM_REGISTERED"] = "You must call '%s' from a registered Dongle.", ["MUST_CALLFROM_SLASH"] = "You must call '%s' from a Dongle slash command object.", ["PROFILE_DOES_NOT_EXIST"] = "Profile '%s' doesn't exist.", ["REPLACE_DEFAULTS"] = "You are attempting to register defaults with a database that already contains defaults.", ["SAME_SOURCE_DEST"] = "Source/Destination profile cannot be the same profile.", ["EVENT_REGISTER_SPECIAL"] = "You cannot register for the '%s' event. Use the '%s' method instead.", ["Unknown"] = "Unknown", ["INJECTDB_USAGE"] = "Usage: DongleCmd:InjectDBCommands(db, ['copy', 'delete', 'list', 'reset', 'set'])", ["DBSLASH_PROFILE_COPY_DESC"] = "profile copy <name> - Copies profile <name> into your current profile.", ["DBSLASH_PROFILE_COPY_PATTERN"] = "^profile copy (.+)$", ["DBSLASH_PROFILE_DELETE_DESC"] = "profile delete <name> - Deletes the profile <name>.", ["DBSLASH_PROFILE_DELETE_PATTERN"] = "^profile delete (.+)$", ["DBSLASH_PROFILE_LIST_DESC"] = "profile list - Lists all valid profiles.", ["DBSLASH_PROFILE_LIST_PATTERN"] = "^profile list$", ["DBSLASH_PROFILE_RESET_DESC"] = "profile reset - Resets the current profile.", ["DBSLASH_PROFILE_RESET_PATTERN"] = "^profile reset$", ["DBSLASH_PROFILE_SET_DESC"] = "profile set <name> - Sets the current profile to <name>.", ["DBSLASH_PROFILE_SET_PATTERN"] = "^profile set (.+)$", ["DBSLASH_PROFILE_LIST_OUT"] = "Profile List:",}--[[------------------------------------------------------------------------- Utility functions for Dongle use---------------------------------------------------------------------------]]local function assert(level,condition,message) if not condition then error(message,level) endendlocal function argcheck(value, num, ...) if type(num) ~= "number" then error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1) end for i=1,select("#", ...) do if type(value) == select(i, ...) then return end end local types = strjoin(", ", ...) local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]") error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3)endlocal function safecall(func,...) local success,err = pcall(func,...) if not success then geterrorhandler()(err) endend--[[------------------------------------------------------------------------- Dongle constructor, and DongleModule system---------------------------------------------------------------------------]]function Dongle:New(name, obj) argcheck(name, 2, "string") argcheck(obj, 3, "table", "nil") if not obj then obj = {} end if registry[name] then error(string.format(L["ALREADY_REGISTERED"], name)) end local reg = {["obj"] = obj, ["name"] = name} registry[name] = reg lookup[obj] = reg lookup[name] = reg for k,v in pairs(methods) do obj[v] = self[v] end -- Add this Dongle to the end of the queue table.insert(loadqueue, obj) return obj,nameendfunction Dongle:NewModule(name, obj) local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "NewModule")) argcheck(name, 2, "string") argcheck(obj, 3, "table", "nil") obj,name = Dongle:New(name, obj) if not reg.modules then reg.modules = {} end reg.modules[obj] = obj reg.modules[name] = obj return obj,nameendfunction Dongle:HasModule(module) local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "HasModule")) argcheck(module, 2, "string", "table") return reg.modules and reg.modules[module]endlocal function ModuleIterator(t, name) if not t then return end local obj repeat name,obj = next(t, name) until type(name) == "string" or not name return name,objendfunction Dongle:IterateModules() local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IterateModules")) return ModuleIterator, reg.modulesend--[[------------------------------------------------------------------------- Event registration system---------------------------------------------------------------------------]]local function OnEvent(frame, event, ...) local eventTbl = events[event] if eventTbl then for obj,func in pairs(eventTbl) do if type(func) == "string" then if type(obj[func]) == "function" then safecall(obj[func], obj, event, ...) end else safecall(func, event, ...) end end endendlocal specialEvents = { ["PLAYER_LOGIN"] = "Enable", ["PLAYER_LOGOUT"] = "Disable",}function Dongle:RegisterEvent(event, func) local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "RegisterEvent")) argcheck(event, 2, "string") argcheck(func, 3, "string", "function", "nil") local special = (self ~= Dongle) and specialEvents[event] if special then error(string.format(L["EVENT_REGISTER_SPECIAL"], event, special), 3) end -- Name the method the same as the event if necessary if not func then func = event end if not events[event] then events[event] = {} frame:RegisterEvent(event) end events[event][self] = funcendfunction Dongle:UnregisterEvent(event) local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterEvent")) argcheck(event, 2, "string") local tbl = events[event] if tbl then tbl[self] = nil if not next(tbl) then events[event] = nil frame:UnregisterEvent(event) end endendfunction Dongle:UnregisterAllEvents() local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterAllEvents")) for event,tbl in pairs(events) do tbl[self] = nil if not next(tbl) then events[event] = nil frame:UnregisterEvent(event) end endendfunction Dongle:IsEventRegistered(event) local reg = lookup[self] assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IsEventRegistered")) argcheck(event, 2, "string") local tbl = events[event] return tblend--[[------------------------------------------------------------------------- Inter-Addon Messaging System---------------------------------------------------------------------------]]function Dongle:RegisterMessage(msg, func) argcheck(self, 1, "table") argcheck(msg, 2, "string") argcheck(func, 3, "string", "function", "nil") -- Name the method the same as the message if necessary if not func then func = msg end if not messages[msg] then messages[msg] = {} end messages[msg][self] = funcendfunction Dongle:UnregisterMessage(msg) argcheck(self, 1, "table") argcheck(msg, 2, "string") local tbl = messages[msg] if tbl then
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -