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.
309 lines
9.0 KiB
309 lines
9.0 KiB
|
2 years ago
|
local addonName, addon = ...
|
||
|
|
|
||
|
|
local eventHandler = CreateFrame('Frame')
|
||
|
|
local callbacks = {}
|
||
|
|
|
||
|
|
local IsEventValid
|
||
|
|
if addon:IsRetail() then
|
||
|
|
IsEventValid = C_EventUtils.IsEventValid
|
||
|
|
else
|
||
|
|
local eventValidator = CreateFrame('Frame')
|
||
|
|
function IsEventValid(event)
|
||
|
|
local isValid = pcall(eventValidator.RegisterEvent, eventValidator, event)
|
||
|
|
if isValid then
|
||
|
|
eventValidator:UnregisterEvent(event)
|
||
|
|
end
|
||
|
|
return isValid
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local unitEventValidator = CreateFrame('Frame')
|
||
|
|
local function IsUnitEventValid(event, unit)
|
||
|
|
-- C_EventUntils.IsEventValid doesn't cover unit events, so we'll have to do this the old fashioned way
|
||
|
|
local isValid = pcall(unitEventValidator.RegisterUnitEvent, unitEventValidator, event, unit)
|
||
|
|
if isValid then
|
||
|
|
unitEventValidator:UnregisterEvent(event)
|
||
|
|
end
|
||
|
|
return isValid
|
||
|
|
end
|
||
|
|
|
||
|
|
local unitValidator = CreateFrame('Frame')
|
||
|
|
local function IsUnitValid(unit)
|
||
|
|
if unitValidator:RegisterUnitEvent('UNIT_HEALTH', unit) then
|
||
|
|
local _, registeredUnit = unitValidator:IsEventRegistered('UNIT_HEALTH')
|
||
|
|
unitValidator:UnregisterEvent('UNIT_HEALTH')
|
||
|
|
return not not registeredUnit -- it will be nil if the registered unit is invalid
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local eventMixin = {}
|
||
|
|
function eventMixin:RegisterEvent(event, callback)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
assert(type(callback) == 'function', 'arg2 must be a function')
|
||
|
|
|
||
|
|
if not callbacks[event] then
|
||
|
|
callbacks[event] = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
table.insert(callbacks[event], {
|
||
|
|
callback = callback,
|
||
|
|
owner = self,
|
||
|
|
})
|
||
|
|
|
||
|
|
if not eventHandler:IsEventRegistered(event) then
|
||
|
|
eventHandler:RegisterEvent(event)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:UnregisterEvent(event, callback)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
assert(type(callback) == 'function', 'arg2 must be a function')
|
||
|
|
|
||
|
|
if callbacks[event] then
|
||
|
|
for index, data in next, callbacks[event] do
|
||
|
|
if data.owner == self and data.callback == callback then
|
||
|
|
callbacks[event][index] = nil
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if #callbacks[event] == 0 then
|
||
|
|
eventHandler:UnregisterEvent(event)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:IsEventRegistered(event, callback)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
assert(type(callback) == 'function', 'arg2 must be a function')
|
||
|
|
|
||
|
|
if callbacks[event] then
|
||
|
|
for _, data in next, callbacks[event] do
|
||
|
|
if data.callback == callback then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:TriggerEvent(event, ...)
|
||
|
|
if callbacks[event] then
|
||
|
|
for _, data in next, callbacks[event] do
|
||
|
|
local successful, ret = pcall(data.callback, data.owner, ...)
|
||
|
|
if not successful then
|
||
|
|
-- ret contains the error
|
||
|
|
error(ret)
|
||
|
|
elseif ret then
|
||
|
|
-- callbacks can unregister themselves by returning positively,
|
||
|
|
-- ret contains the boolean
|
||
|
|
eventMixin.UnregisterEvent(data.owner, event, data.callback)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
eventHandler:SetScript('OnEvent', function(_, event, ...)
|
||
|
|
eventMixin:TriggerEvent(event, ...)
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- special handling for unit events
|
||
|
|
local unitEventHandlers = {}
|
||
|
|
local function getUnitEventHandler(unit)
|
||
|
|
if not unitEventHandlers[unit] then
|
||
|
|
local unitEventHandler = CreateFrame('Frame')
|
||
|
|
unitEventHandler:SetScript('OnEvent', function(_, event, ...)
|
||
|
|
eventMixin:TriggerUnitEvent(event, unit, ...)
|
||
|
|
end)
|
||
|
|
unitEventHandlers[unit] = unitEventHandler
|
||
|
|
end
|
||
|
|
return unitEventHandlers[unit]
|
||
|
|
end
|
||
|
|
|
||
|
|
local unitEventCallbacks = {}
|
||
|
|
function eventMixin:RegisterUnitEvent(event, ...)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
local callback = select(select('#', ...), ...)
|
||
|
|
assert(type(callback) == 'function', 'last argument must be a function')
|
||
|
|
|
||
|
|
for i = 1, select('#', ...) - 1 do
|
||
|
|
local unit = select(i, ...)
|
||
|
|
assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit')
|
||
|
|
assert(IsUnitEventValid(event, unit), 'event "' .. event .. '" is not valid for the given unit')
|
||
|
|
|
||
|
|
if not unitEventCallbacks[unit] then
|
||
|
|
unitEventCallbacks[unit] = {}
|
||
|
|
end
|
||
|
|
if not unitEventCallbacks[unit][event] then
|
||
|
|
unitEventCallbacks[unit][event] = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
table.insert(unitEventCallbacks[unit][event], {
|
||
|
|
callback = callback,
|
||
|
|
owner = self,
|
||
|
|
})
|
||
|
|
|
||
|
|
local unitEventHandler = getUnitEventHandler(unit)
|
||
|
|
local isRegistered, registeredUnit = unitEventHandler:IsEventRegistered(event)
|
||
|
|
if not isRegistered then
|
||
|
|
unitEventHandler:RegisterUnitEvent(event, unit)
|
||
|
|
elseif registeredUnit ~= unit then
|
||
|
|
error('unit event somehow registered with the wrong unit')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:UnregisterUnitEvent(event, ...)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
local callback = select(select('#', ...), ...)
|
||
|
|
assert(type(callback) == 'function', 'last argument must be a function')
|
||
|
|
|
||
|
|
for i = 1, select('#', ...) - 1 do
|
||
|
|
local unit = select(i, ...)
|
||
|
|
assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit')
|
||
|
|
assert(IsUnitEventValid(event, unit), 'event is not valid for the given unit')
|
||
|
|
|
||
|
|
if unitEventCallbacks[unit] and unitEventCallbacks[unit][event] then
|
||
|
|
for index, data in next, unitEventCallbacks[unit][event] do
|
||
|
|
if data.owner == self and data.callback == callback then
|
||
|
|
unitEventCallbacks[unit][event][index] = nil
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if #unitEventCallbacks[unit][event] == 0 then
|
||
|
|
getUnitEventHandler(unit):UnregisterEvent(event)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:IsUnitEventRegistered(event, ...)
|
||
|
|
assert(IsEventValid(event), 'arg1 must be an event')
|
||
|
|
local callback = select(select('#', ...), ...)
|
||
|
|
assert(type(callback) == 'function', 'last argument must be a function')
|
||
|
|
|
||
|
|
for i = 1, select('#', ...) - 1 do
|
||
|
|
local unit = select(i, ...)
|
||
|
|
assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit')
|
||
|
|
assert(IsUnitEventValid(event, unit), 'event is not valid for the given unit')
|
||
|
|
|
||
|
|
if unitEventCallbacks[unit] and unitEventCallbacks[unit][event] then
|
||
|
|
for index, data in next, unitEventCallbacks[unit][event] do
|
||
|
|
if data.callback == callback then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:TriggerUnitEvent(event, unit, ...)
|
||
|
|
if unitEventCallbacks[unit] and unitEventCallbacks[unit][event] then
|
||
|
|
for _, data in next, unitEventCallbacks[unit][event] do
|
||
|
|
local successful, ret = pcall(data.callback, data.owner, ...)
|
||
|
|
if not successful then
|
||
|
|
error(ret)
|
||
|
|
elseif ret then
|
||
|
|
-- callbacks can unregister themselves by returning positively
|
||
|
|
eventMixin.UnregisterUnitEvent(data.owner, event, unit, data.callback)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- special handling for combat events
|
||
|
|
local combatEventCallbacks = {}
|
||
|
|
do
|
||
|
|
local function internalTrigger(_, event, _, ...)
|
||
|
|
if combatEventCallbacks[event] then
|
||
|
|
for _, data in next, combatEventCallbacks[event] do
|
||
|
|
local successful, ret = pcall(data.callback, data.owner, ...)
|
||
|
|
if not successful then
|
||
|
|
error(ret)
|
||
|
|
elseif ret then
|
||
|
|
eventMixin.UnregisterCombatEvent(data.owner, event, data.callback)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:TriggerCombatEvent()
|
||
|
|
internalTrigger(CombatLogGetCurrentEventInfo())
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:RegisterCombatEvent(event, callback)
|
||
|
|
assert(type(event) == 'string', 'arg1 must be a string')
|
||
|
|
assert(type(callback) == 'function', 'arg2 must be a function')
|
||
|
|
|
||
|
|
if not combatEventCallbacks[event] then
|
||
|
|
combatEventCallbacks[event] = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
table.insert(combatEventCallbacks[event], {
|
||
|
|
callback = callback,
|
||
|
|
owner = self,
|
||
|
|
})
|
||
|
|
|
||
|
|
if not self:IsEventRegistered('COMBAT_LOG_EVENT_UNFILTERED', self.TriggerCombatEvent) then
|
||
|
|
self:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED', self.TriggerCombatEvent)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function eventMixin:UnregisterCombatEvent(event, callback)
|
||
|
|
assert(type(event) == 'string', 'arg1 must be a string')
|
||
|
|
assert(type(callback) == 'function', 'arg2 must be a function')
|
||
|
|
|
||
|
|
if combatEventCallbacks[event] then
|
||
|
|
for index, data in next, combatEventCallbacks[event] do
|
||
|
|
if data.owner == self and data.callback == callback then
|
||
|
|
combatEventCallbacks[event][index] = nil
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- expose mixin
|
||
|
|
addon.eventMixin = eventMixin
|
||
|
|
|
||
|
|
-- anonymous event registration
|
||
|
|
addon = setmetatable(addon, {
|
||
|
|
__index = function(t, key)
|
||
|
|
if IsEventValid(key) then
|
||
|
|
-- addon:EVENT_NAME([arg1[, ...]])
|
||
|
|
return function(_, ...)
|
||
|
|
eventMixin.TriggerEvent(t, key, ...)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
-- default table behaviour
|
||
|
|
return rawget(t, key)
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
__newindex = function(t, key, value)
|
||
|
|
if key == 'OnLoad' then
|
||
|
|
-- addon:OnLoad() = function() end
|
||
|
|
-- shorthand for ADDON_LOADED
|
||
|
|
addon:RegisterEvent('ADDON_LOADED', function(self, name)
|
||
|
|
if name == addonName then
|
||
|
|
local successful, ret = pcall(value, self)
|
||
|
|
if not successful then
|
||
|
|
error(ret)
|
||
|
|
else
|
||
|
|
return true -- unregister event
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
elseif IsEventValid(key) then
|
||
|
|
-- addon:EVENT_NAME(...) = function() end
|
||
|
|
eventMixin.RegisterEvent(t, key, value)
|
||
|
|
else
|
||
|
|
-- default table behaviour
|
||
|
|
rawset(t, key, value)
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
})
|
||
|
|
|
||
|
|
-- mixin to namespace
|
||
|
|
Mixin(addon, eventMixin)
|