Files
Brues c8bc4c134d Filter UNIT_AURA by unit token and drop the unit streams in realtime mode
Two handlers opened by testing arg1 against a fixed set of unit tokens, so
every other unit's copy of UNIT_AURA reached Lua only to be compared away
-- in a raid that is the bulk of them. ClassicAPI's RegisterUnitEvent
pushes that filter into the client, so the handler only runs for the units
named. Filtering is per (frame, event), so the other events sharing the
libdebuff frame are unaffected.

  Utility.lua      UNIT_AURA -> "target"            (libdebuff seeding)
  ComboPointTracker UNIT_AURA -> "target", "player"

Extensions register through CleveRoids.RegisterEvent, so ExtensionsManager
gains a RegisterUnitEvent alongside it.

Separately, the main frame's unit state streams (UNIT_AURA / UNIT_HEALTH /
UNIT_POWER, or the _GUID variants under Nampower v2.39+) are now registered
only in event-driven mode. Their handlers are wholly wrapped in
`realtime == 0`, so with realtime on every one of them crossed into Lua and
returned immediately while the OnUpdate did the refreshing. Registration is
applied at load, again at VARIABLES_LOADED once the saved setting is
readable, and whenever /cleveroid realtime flips it.

Those three stay plain RegisterEvent calls: their handlers ignore the unit
and refresh every macro, and a conditional may name any unit
([@party3,hp:<50], @focus, @mouseover), so narrowing the token set would
leave those icons stale. The _GUID variants could not be filtered anyway --
their first argument is a GUID, not a unit token.
2026-09-11 00:42:42 -05:00

263 lines
9.3 KiB
Lua

--[[
Author: Dennis Werner Garske (DWG) / brian / Mewtiny
License: MIT License
]]
local _G = _G or getfenv(0)
local CleveRoids = _G.CleveRoids or {}
-- A list of all registered extensions
CleveRoids.Extensions = CleveRoids.Extensions or {}
-- Calls the "OnLoad" function of every extension
function CleveRoids.InitializeExtensions()
for k, v in pairs(CleveRoids.Extensions) do
local func = v["OnLoad"]
if not func then
CleveRoids.Print("CleveRoids.InitializeExtensions - Couldn't find 'OnLoad' function for extension "..k)
else
func()
end
end
end
-- Removes the given hook
-- hook: The hook to remove
function CleveRoids.RemoveHook(hook)
_G[hook.name] = hook.original
end
-- Removes the given hook from the given object
-- object: The object to remove the hook from
-- hook: The hook to remove
function CleveRoids.RemoveMethodHook(object, hook)
object[hook.name] = hook.original
end
-- Clears all previously declared hooks
function CleveRoids.ClearHooks()
for k, v in pairs(CleveRoids.Extensions) do
for k2, v2 in pairs(v.internal.memberHooks) do
for k3, v3 in pairs(v2) do
CleveRoids.RemoveMethodHook(k2, v3)
end
end
for k2, v2 in pairs(v.internal.hooks) do
CleveRoids.RemoveHook(v2)
end
end
end
-- Creates a new extension with the given name
-- name: the name of the extension
function CleveRoids.RegisterExtension(name)
local extension = {
internal =
{
frame = CreateFrame("FRAME"),
eventHandlers = {},
hooks = {},
memberHooks = {},
activeHooks = {}, -- Re-entrancy guard: tracks hooks currently executing
},
}
extension.RegisterEvent = function(eventName, callbackName)
CleveRoids.RegisterEvent(name, eventName, callbackName)
end
extension.RegisterUnitEvent = function(eventName, callbackName, ...)
CleveRoids.RegisterUnitEvent(name, eventName, callbackName, unpack(arg))
end
extension.Hook = function(functionName, callbackName, dontCallOriginal)
CleveRoids.RegisterHook(name, functionName, callbackName, dontCallOriginal)
end
extension.HookMethod = function(object, functionName, callbackName, dontCallOriginal)
CleveRoids.RegisterMethodHook(name, object, functionName, callbackName, dontCallOriginal)
end
extension.UnregisterEvent = function(eventName, callbackName)
CleveRoids.UnregisterEvent(name, eventName, callbackName)
end
extension.internal.OnEvent = function()
local callbackName = extension.internal.eventHandlers[event]
if callbackName then
extension[callbackName]()
end
end
-- This is a function wrapper that we swap with the function that we want to hook
-- @return Value of Callback() or Origininal() if dontCallOriginal is false
extension.internal.OnHook = function(object, functionName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
-- Generate a unique key for this hook (handles both global and method hooks)
local hookKey = tostring(object or "global") .. ":" .. functionName
-- Re-entrancy guard: if this hook is already executing, call original directly
if extension.internal.activeHooks[hookKey] then
local hook
if object then
hook = extension.internal.memberHooks[object][functionName]
else
hook = extension.internal.hooks[functionName]
end
if hook and hook.original then
return hook.original(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
end
return
end
local hook
if object then
hook = extension.internal.memberHooks[object][functionName]
else
hook = extension.internal.hooks[functionName]
end
-- Mark this hook as active
extension.internal.activeHooks[hookKey] = true
local retval = hook.callback(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
if not hook.dontCallOriginal then
retval = hook.original(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
end
-- Mark this hook as inactive
extension.internal.activeHooks[hookKey] = nil
return retval
end
extension.internal.frame:SetScript("OnEvent", extension.internal.OnEvent)
CleveRoids.Extensions[name] = extension
return extension
end
-- Registers a callback function for a given event name
-- extensionName: The name of the extension trying to register the callback
-- eventName: The event's name we'd like to register a callback for
-- callbackName: The name of the callback that get's called when the event fires
function CleveRoids.RegisterEvent(extensionName, eventName, callbackName)
local extension = CleveRoids.Extensions[extensionName]
extension.internal.eventHandlers[eventName] = callbackName
extension.internal.frame:RegisterEvent(eventName)
end
-- Registers a callback for a UNIT_* event, filtered to the given unit tokens.
-- The callback then only runs for those units: the client drops every other
-- unit's copy, instead of all of them reaching Lua to be compared away. Use
-- this over RegisterEvent whenever the handler starts by testing arg1.
-- extensionName: The name of the extension trying to register the callback
-- eventName: The UNIT_* event to register
-- callbackName: The name of the callback that gets called when the event fires
-- ...: the unit tokens to accept (e.g. "player", "target")
function CleveRoids.RegisterUnitEvent(extensionName, eventName, callbackName, ...)
local extension = CleveRoids.Extensions[extensionName]
extension.internal.eventHandlers[eventName] = callbackName
extension.internal.frame:RegisterUnitEvent(eventName, unpack(arg))
end
-- Hooks the given function by it's name
-- extensionName: The name of the extension trying to register the callback
-- functionName: The name of the function that'll be hooked
-- callbackName: The name of the callback that get's called when the hooked function is called
-- dontCallOriginal: Set to true when the original function should not be called
function CleveRoids.RegisterHook(extensionName, functionName, callbackName, dontCallOriginal)
local orig = _G[functionName]
local extension = CleveRoids.Extensions[extensionName]
if not orig then
CleveRoids.Print("CleveRoids.RegisterHook - Invalid function to hook: "..functionName)
end
if not extension then
CleveRoids.Print("CleveRoids.RegisterHook - Invalid extension: "..extension)
return
end
if not extension[callbackName] then
CleveRoids.Print("CleveRoids.RegisterHook - Couldn't find callback: "..callbackName)
return
end
extension.internal.hooks[functionName] = {
original = orig,
callback = extension[callbackName],
name = functionName,
dontCallOriginal = dontCallOriginal
}
_G[functionName] = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
return extension.internal.OnHook(nil, functionName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
end
end
-- Hooks the given object's function
-- object: The object you want to hook
-- extensionName: The name of the extension trying to register the callback
-- functionName: The name of the function that'll be hooked
-- callbackName: The name of the callback that get's called when the hooked function is called
function CleveRoids.RegisterMethodHook(extensionName, object, functionName, callbackName, dontCallOriginal)
if not object then
CleveRoids.Print("CleveRoids.RegisterMethodHook - The object could not be found!")
return
end
if type(object) ~= "table" then
CleveRoids.Print("CleveRoids.RegisterMethodHook - The object needs to be a table!")
return
end
local orig = object[functionName]
if not orig then
CleveRoids.Print("CleveRoids.RegisterMethodHook - The object doesn't have a function named "..functionName)
return
end
local extension = CleveRoids.Extensions[extensionName]
if not extension then
CleveRoids.Print("CleveRoids.RegisterMethodHook - Invalid extension: "..extension)
return
end
if not extension[callbackName] then
CleveRoids.Print("CleveRoids.RegisterMethodHook - Couldn't find callback: "..callbackName)
return
end
if not extension.internal.memberHooks[object] then
extension.internal.memberHooks[object] = {}
end
extension.internal.memberHooks[object][functionName] = {
original = orig,
callback = extension[callbackName],
name = functionName,
dontCallOriginal = dontCallOriginal
}
object[functionName] = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
return extension.internal.OnHook(object, functionName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
end
end
function CleveRoids.UnregisterEvent(extensionName, eventName, callbackName)
local extension = CleveRoids.Extensions[extensionName]
if extension.internal.eventHandlers[eventName] then
extension.internal.eventHandlers[eventName] = nil
extension.internal.frame:UnregisterEvent(eventName)
end
end
_G["CleveRoids"] = CleveRoids