PHP код:
-- Получаем текущее окружение , в котором выполняется наш аддон
-- первый аргумент возвращается название аддона в строчном формате
-- второй аргумент возвращает пространство ивент (массив)
local addon, ns = ...
-- простенький обработчик ошибок
local error = function(...) print("|cffff0000Error:|r "..string.format(...)) end
-- будущий обработчик обратных вызовов
local frame = CreateFrame"frame"
-- создаем прототип нашего будущего модуля и массив в котором будем хранить ссылки на модули
local Modules, module = {}, {__index = {}}
-- сократим время жизни некоторых переменных которые ниже не понадобятся
do
-- небольшая перегрузка, вызывается, когда луа запускает функцию.
local events, event_meta = {}, {
__call = function(funcs, self, ...)
for _, func in next, funcs do
func(self, ...)
end
end,
}
-- нужен небольшой перехват и переопределение функции регистрации событий
local RegisterEvent = frame.RegisterEvent
-- переопределяем метод RegisterEvent для обьекта frame
function frame:RegisterEvent(event, module, func)
-- небольшое исключение для методов модуля
if(type(func) == 'string' and type(module[event]) == 'function') then
func = module[func]
end
-- создаем массив обратных вызовов вызовов для регистрируемого события, если его нет
if not events[event] then events[event] = {} end
local curev = module[event]
-- в случае если для этого события необходимо обработать несколько функций делаем перегрузку
if(curev and func) then
-- если таблица уже существует нет смысла создавать ее заново, просто регистрируем событие
if type(curev)=="table" and not self:IsEventRegistered(event) then
RegisterEvent(self, event)
else
-- в противпном случае делаем перегрузку
if(type(curev) == 'function') then
module[event] = setmetatable({curev, func}, event_meta)
else
for _, infunc in next, curev do
if(infunc == func) then return end
end
-- заносим функцию в массив обратных вызовов
table.insert(curev, func)
end
end
else
-- если функция всего одна , перегрузка не нужна, добавляем ее в массив обратных вызовов и регистрируем событие
if(func) then
module[event] = func
table.insert(events[event], module)
elseif(not module[event]) then
return error("Обработчик событий [%s] не существует.", event)
end
RegisterEvent(self, event)
end
end
-- метод для регистрации события для прототипа модуля
function module.__index:RegisterEvent(event, func)
return frame:RegisterEvent(event, self, func)
end
local UnregisterEvent = frame.UnregisterEvent
function frame:UnregisterEvent(event, module, func)
local curev = module[event]
if(type(curev) == 'table' and func) then
for k, infunc in next, curev do
if(infunc == func) then
curev[k] = nil
if(#curev == 0) then
table.remove(curev, k)
events[event] = nil
UnregisterEvent(self, event)
end
break
end
end
else
module[event] = nil
events[event] = nil
UnregisterEvent(self, event)
end
end
--метод для удаления ивента и обратных вызовов для него с модуля
function module.__index:UnregisterEvent(event, func)
return frame:UnregisterEvent(event, self, func)
end
-- рукурсивная обработка массива обратных вызовов
local function burst(i, modules, event, ...)
local module = modules[i]
if module then
module[event](module, event, ...)
return burst(i+1, modules, event, ...)
end
end
-- обработка событий
frame:SetScript("OnEvent", function(self, event, ...)
return burst(1, events[event], event, ...)
end)
end
-- создаем API при вызове которого будет создан модуль с обработчик событий и тд
function ns:NewModule(name, dependency)
-- если модуль существует, возвращаем ошибку
if Modules[name] then
return error("Модуль с именем [%s] уже существует.", name)
end
-- наследуем мету из прототипа модуля
local Module = setmetatable({}, module)
-- создаем очередь загрузки
Module.Queue = {}
Modules[name] = Module
if dependency then
local Parent = Modules[dependency]
if Parent then
Parent.Queue[name] = Module
Module.HasDependency = true
end
end
return Module
end
-- получаем ссылку на наш модуль по имени модуля
function ns:GetModule(name)
if Modules[name] then
return Modules[name]
else
return error("Модуль с именем [%s] не существует.", name)
end
end
-- "Инициализация" наших модулей
-- Ивент PLAYER_LOGIN необходим, иначе наши модули будут созданы позже, чем будет выполнена их загрузка
local f = CreateFrame"frame"
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function()
-- загрузка
for _, Module in pairs(Modules) do
-- инициализация для модулей без очереди загрузки
if (Module.Init and not Module.HasDependency) then
Module:Init()
end
-- инициализация очереди загрузки
for _, ChildModule in pairs(Module.Queue) do
if ChildModule.Init then
ChildModule:Init()
end
end
end
end)
Вот собственно и все.