vanilla-wow-addons – Rev 1
?pathlinks?

--[[
Main.lua
This module does all the work. It hooks every OnEvent and OnUpdate function in existance.
]]
-- table setup
local mod = thismod
local me = { }
mod.main = me
me.ishooking = false
--[[
me.hookall()
Hooks all the OnUpdate() and OnEvent() methods running.
First thing is to gather all frames. We make a table, key = the frame itself, value = {onevent = <true or nil>, onupdate = <true or nil>}
]]
me.hookall = function()
-- this method can only be run once - check!
if me.ishooking == true then
mod.out.print("The monitor has already been loaded, and is actively recording data.")
return
end
local frame, script
local numonevents = 0
local numonupdates = 0
local numframes = 0
local numemptyframes = 0
-- first let's find all the frames
local allframes = { }
local hasonupdate, hasonevent
while true do
-- get next frame
frame = EnumerateFrames(frame)
if frame == nil then
break
end
numframes = numframes + 1
allframes[frame] = {me = frame, numchildren = 0, children = { }}
-- Determine whether the frame has onupdates or onevents
hasonupdate = frame:GetScript("OnUpdate")
hasonevent = frame:GetScript("OnEvent")
if hasonevent then
numonevents = numonevents + 1
allframes[frame].onevent = true
end
if hasonupdate then
numonupdates = numonupdates + 1
allframes[frame].onupdate = true
end
-- add the name property
allframes[frame].name = frame:GetName()
end
-- print
mod.out.print(string.format("Found %s frames, %s onupdates, %s onevents.", numframes, numonupdates, numonevents))
-- Now, find anonymous frames whose parents have known parents (not uiparent)
local numanonymous, numnamed, parent
local key, value, parentvalue
-- for loop as a safety limit, for now
for x = 1, 10 do
numanonymous = 0
numnamed = 0
for key, value in allframes do
if value.name == nil then
numanonymous = numanonymous + 1
-- check for parent
parent = key:GetParent()
parentvalue = allframes[parent]
if parentvalue and parentvalue.name and parentvalue.name ~= "UIParent" then
-- we've found a good parent
parentvalue.numchildren = parentvalue.numchildren + 1
numnamed = numnamed + 1
value.name = parentvalue.name .. "$child" .. parentvalue.numchildren
table.insert(parentvalue.children, value)
elseif x == 10 then
-- we just can't find the name for this frame's parents, no matter what. Better scrap it
allframes[key] = nil
end
end
end
--mod.out.print(string.format("x = %d, found %d anonymous, named %d.", x, numanonymous, numnamed))
if numnamed == 0 then
break
end
end
local totalframes, framesdropped
for x = 1, 10 do
totalframes = 0
framesdropped = 0
for key, value in allframes do
totalframes = totalframes + 1
-- 1) check 0 children
if value.numchildren == 0 then
-- check no event on onupdate
if value.onupdate == nil and value.onevent == nil then
framesdropped = framesdropped + 1
-- check for anonymous frame
if key:GetName() == nil then
-- remove name from parent's list
parentvalue = allframes[key:GetParent()]
-- parentvalue might not exist now
if parentvalue then
for y = 1, parentvalue.numchildren do
if parentvalue.children[y] == value then
table.remove(parentvalue.children, y)
break
end
end
parentvalue.numchildren = parentvalue.numchildren - 1
end
end
allframes[key] = nil
end
end
end
-- print
--mod.out.print(string.format("stage %s, found %s frames, dropped %s.", x, totalframes, framesdropped))
if framesdropped == 0 then
break
end
end
-- Group related holdings into families
local headers = { }
local header
local numfamilies = 0
local framesleft = 0
for key, value in allframes do
framesleft = framesleft + 1
-- somehow, value.name can be nil here
if value.name then
local header = me.getnameheader(value.name)
-- does this family already exist?
if headers[header] == nil then
-- create new
headers[header] = { value }
numfamilies = numfamilies + 1
else
-- add
table.insert(headers[header], value)
end
-- set my family ref
value.family = headers[header]
end
end
-- make a file level variable
me.families = headers
me.frames = allframes
-- print
mod.out.print(string.format("Combined the %s active frames into %s groups.", framesleft, numfamilies))
-- now let's hook them!
numonevents = 0
numonupdates = 0
for key, value in allframes do
-- some unknowable frames are left in - don't hook them.
if value.name then
if value.onevent then
me.overridescript(key, "OnEvent", value)
numonevents = numonevents + 1
end
if value.onupdate then
me.overridescript(key, "OnUpdate", value)
numonupdates = numonupdates + 1
end
end
end
-- set as loaded
me.ishooking = true
-- print
--mod.out.print(string.format("Found %s onevent, %s onupdate.", numonevents, numonupdates))
end
me.getnameheader = function(name)
-- still nil? return nil
if name == nil then
return "<nil>"
end
local length = string.len(name)
-- really short stuff, to stop annoying cases
if length < 6 then
return name
end
-- check 4th char
local current, previous, next_
local position = 3
previous = string.sub(name, position - 1, position - 1)
current = string.sub(name, position, position)
next_ = string.sub(name, position + 1, position + 1)
local x = 0
while true do
x = x + 1
if x > 20 then
mod.out.print("emergency stop!")
return name
end
-- iterate
position = position + 1
previous = current
current = next_
next_ = string.sub(name, position + 1, position + 1)
-- check for end of string
if next_ == "" then
return name
end
-- check for underscore
if current == "_" then
return string.sub(name, 1, position)
end
-- check for UUl
if string.find(previous, "[A-Z]") and string.find(current, "[A-Z]") then
-- if the string is 5 or longer, that's enough
if position >= 5 then
return string.sub(name, 1, position)
end
end
-- check for lU
if string.find(current, "[a-z]") and string.find(next_, "[A-Z]") then
return string.sub(name, 1, position)
end
end
end
me.print2 = function(datatype, property)
if me.ishooking == false then
mod.out.print(string.format("The monitor is not loaded, so there's no data to print. Run the command |cffffff00%s load|r to start the monitor.", mod.string.get("print", "console", "short")))
return
end
-- default values
if datatype == nil then
datatype = "time" -- or "memory"
end
if property == nil then
property = "total" -- or recent
end
-- let's just get the total values for all the families, and print off a random few
local header, family, index, framedata
local total, prints
prints = 0
local values = { }
for header, family in me.families do
total = 0
if family == nil then
mod.out.print("Nil family for header = " .. header)
end
for index = 1, table.getn(family) do
framedata = family[index]
if framedata.onupdate then
total = total + framedata.onupdate[datatype][property]
end
if framedata.onevent then
total = total + framedata.onevent[datatype][property]
end
end
-- add to values
table.insert(values, {header, total})
end
-- sort
table.sort(values, function(a, b) return a[2] > b[2] end)
for index = 1, 10 do
--[[ the values are
1) The rank, from 1 to 10
2) The header / name of the family
3) An example of a frame name in that family
4) The actual value
]]
mod.out.print(string.format("%d %s (%s): %s", index, values[index][1],
me.families[values[index][1]][1].name, math.floor(0.5 + values[index][2])))
end
end
-- script = "OnUpdate" or "OnEvent"
me.overridescript = function(frame, script, value)
local framename = value.name
local varname = string.lower(script)
local original = frame:GetScript(script)
if original == nil then
mod.out.print("There's no " .. script .. " script for the frame " .. frame)
return
end
me.nexthook = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
me.hookproc(frame, varname, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
end
me.frames[frame][varname] = { }
me.frames[frame][varname].original = original
me.frames[frame][varname].replacement = me.nexthook
me.frames[frame][varname].memory = mod.dataset.createnewdataset(5.0, 6)
me.frames[frame][varname].time = mod.dataset.createnewdataset(5.0, 6)
-- set the new script
frame:SetScript(script, me.nexthook)
end
-- frame is the name of a frame. script = "onupdate" or "onevent"
me.hookproc = function(frame, script, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
local gcbefore = gcinfo()
local timebefore = GetTime()
me.frames[frame][script].original(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
local timetaken = 1000 * (GetTime() - timebefore)
local memory = gcinfo() - gcbefore
if memory < 0 then
memory = 0
end
mod.dataset.adddatapoint(me.frames[frame][script].memory, memory)
mod.dataset.adddatapoint(me.frames[frame][script].time, timetaken)
end