Thread Tools Display Modes
05-23-11, 08:46 AM   #1
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
hooksecurefunc & memory use

Hello, I recently made a button border AddOn, the first version of which took this approach:
  • main function (create border & apply settings)
    • if object is regular action button do stuff
    • if object is special action button do other stuff
    • if object is aura button do other other stuff
  • hooksecurefunc 1
    • for all objects in group A do main function
  • hooksecurefunc 2
    • for all objects in group B do main function
  • hooksecurefunc 3
    • for all objects in group C do main function
This "worked", the trouble was sporadic memory usage spikes & consequent FPS drops...usually when doing certain things (like shapshifting). I would run through Elwynn doing nothing but stealthing/unstealthing, and would drop to zero FPS both ways for a second as this AddOn's memory use grew from 10 KB to 2 MB momentarily (no other AddOns loaded).

My second version took an approach I've seen in a few other AddOns (notably nMainbar):
  • hooksecurefunc 1
    • create border & apply settings
  • hooksecurefunc 2
    • create border & apply settings
  • hooksecurefunc 3
    • create border & apply settings
The result is dramatically better performance with no lag issues whatsoever. I'm trying to understand why, as I still like the idea of one "do stuff" function that is referenced in all subsequent hooked secure functions.

first version | second version

Last edited by Aprikot : 05-23-11 at 09:37 AM. Reason: i r spel gud
  Reply With Quote
05-23-11, 09:34 AM   #2
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
If you're creating textures and frames every time DoStuff() is called, expanded memory usage is to be expected. If, however, you create the textures and frames exactly once and apply them when they're needed, you're only ever growing the memory once.

Without seeing code, I can't rightly say what's happening.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
05-23-11, 09:43 AM   #3
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
I have pastebin links in the op, but here's the meat of the two versions:

First version:

lua Code:
  1. local BorderBeautification = function(object)
  2.        
  3.                 if IsAddOnLoaded("FacePaint") and FacePaint then
  4.                         tc = {r = topcolor.r, g = topcolor.g, b = topcolor.b, a = topalpha}
  5.                         bc = {r = bottomcolor.r, g = bottomcolor.g, b = bottomcolor.b, a = bottomalpha}
  6.                 else
  7.                         tc = {r = BorderColors.top.r, g = BorderColors.top.g, b = BorderColors.top.b, a = BorderColors.top.a}
  8.                         bc = {r = BorderColors.bot.r, g = BorderColors.bot.g, b = BorderColors.bot.b, a = BorderColors.bot.a}
  9.                 end
  10.        
  11.                 -- Make border texture
  12.                 for number = 1, 12 do
  13.                         local button = _G[object..number];
  14.                         local icon = _G[object..number.."Icon"];
  15.                         if button then
  16.                                 button:SetNormalTexture("Interface\\BUTTONS\\UI-Quickslot2")
  17.                         end
  18.                         local border
  19.                        
  20.                         -- Shapeshift & Pet bars
  21.                         if (object == "ShapeshiftButton" or object == "PetActionButton") then
  22.                                 border = _G[object..number.."NormalTexture2"]
  23.                                 if border then
  24.                                         border:ClearAllPoints()
  25.                                         border:SetPoint("TOPLEFT", button, -15, 15)
  26.                                         border:SetPoint("BOTTOMRIGHT", button, 15, -15)
  27.                                         border:SetGradientAlpha("VERTICAL", bc.r, bc.g, bc.b, ba, tc.r, tc.g, tc.b, ta)
  28.                                 end
  29.                                 if icon then
  30.                                         icon:SetTexCoord(.05, .95, .05, .95);
  31.                                         icon:SetPoint("TOPLEFT", button, -1, 1);
  32.                                         icon:SetPoint("BOTTOMRIGHT", button, 1, -1);
  33.                                 end
  34.                                
  35.                         -- Totem bar                          
  36.                         elseif (object == "MultiCastActionButton") then
  37.                                 border = _G[object..number.."NormalTexture"]
  38.                                 if border then
  39.                                         border:ClearAllPoints()
  40.                                         border:SetPoint("TOPLEFT", button, -15, 15)
  41.                                         border:SetPoint("BOTTOMRIGHT", button, 15, -15)
  42.                                         border:SetGradientAlpha("VERTICAL", bc.r, bc.g, bc.b, ba, tc.r, tc.g, tc.b, ta)
  43.                                 end
  44.                                 if icon then
  45.                                         icon:SetTexCoord(.05, .95, .05, .95);
  46.                                         icon:SetPoint("TOPLEFT", button, 1, -1);
  47.                                         icon:SetPoint("BOTTOMRIGHT", button, -1, 1);
  48.                                 end
  49.                                
  50.                         -- Action bars
  51.                         else
  52.                                 border = _G[object..number.."NormalTexture"]
  53.                                 if border then
  54.                                         border:ClearAllPoints()
  55.                                         border:SetPoint("TOPLEFT", button, -14, 14)
  56.                                         border:SetPoint("BOTTOMRIGHT", button, 14, -14)
  57.                                         border:SetGradientAlpha("VERTICAL", bc.r, bc.g, bc.b, ba, tc.r, tc.g, tc.b, ta)
  58.                                 end
  59.                                 if icon then
  60.                                         icon:SetTexCoord(.05, .95, .05, .95);
  61.                                         icon:SetPoint("TOPLEFT", button, 0, 0);
  62.                                         icon:SetPoint("BOTTOMRIGHT", button, 0, 0);
  63.                                 end                            
  64.                         end
  65.                 end
  66.         end
  67.        
  68. --      McBlastOff
  69.         ----------
  70.        
  71.         hooksecurefunc("ActionButton_Update", function(self)
  72.                 for i, v in pairs({"ActionButton", "MultiBarBottomLeftButton", "MultiBarBottomRightButton", "MultiBarLeftButton", "MultiBarRightButton", "MultiCastActionButton", "MultiCastSummonSpellButton", "MultiCastRecallSpellButton"}) do
  73.                         BorderBeautification(v)
  74.                 end
  75.         end);
  76.        
  77.         hooksecurefunc("ActionButton_UpdateUsable", function(self)
  78.                 for i, v in pairs({"ActionButton", "MultiBarBottomLeftButton", "MultiBarBottomRightButton", "MultiBarLeftButton", "MultiBarRightButton", "MultiCastActionButton", "MultiCastSummonSpellButton", "MultiCastRecallSpellButton"}) do
  79.                         BorderBeautification(v)
  80.                 end
  81.         end);
  82.  
  83.         hooksecurefunc("ActionButton_ShowGrid", function(self)
  84.                 for i, v in pairs({"ActionButton", "MultiBarBottomLeftButton", "MultiBarBottomRightButton", "MultiBarLeftButton", "MultiBarRightButton", "MultiCastActionButton", "MultiCastSummonSpellButton", "MultiCastRecallSpellButton"}) do
  85.                         BorderBeautification(v)
  86.                 end
  87.         end);
  88.        
  89.         hooksecurefunc("PetActionBar_Update",  function(self)
  90.                 for i, v in pairs({"PetActionButton", "ShapeshiftButton"}) do
  91.                         BorderBeautification(v)
  92.                 end
  93.         end);
Second version:

lua Code:
  1. local colorit = function(object)
  2.                 if IsAddOnLoaded("FacePaint") and FacePaint.enable and not FacePaint.invert then
  3.                         tc = {r = topcolor.r, g = topcolor.g, b = topcolor.b, a = topalpha}
  4.                         bc = {r = bottomcolor.r, g = bottomcolor.g, b = bottomcolor.b, a = bottomalpha}
  5.                 elseif IsAddOnLoaded("FacePaint") and FacePaint.enable and FacePaint.invert then
  6.                         tc = {r = bottomcolor.r, g = bottomcolor.g, b = bottomcolor.b, a = bottomalpha}
  7.                         bc = {r = topcolor.r, g = topcolor.g, b = topcolor.b, a = topalpha}
  8.                 else
  9.                         tc = {r = bordercolor.top.r, g = bordercolor.top.g, b = bordercolor.top.b, a = bordercolor.top.a}
  10.                         bc = {r = bordercolor.bot.r, g = bordercolor.bot.g, b = bordercolor.bot.b, a = bordercolor.bot.a}
  11.                 end
  12.                 object:SetGradientAlpha("VERTICAL", bc.r, bc.g, bc.b, bc.a, tc.r, tc.g, tc.b, tc.a)
  13.         end
  14.        
  15.         hooksecurefunc("ActionButton_Update", function(self)
  16.                 local button = _G[self:GetName()]
  17.                 local icon = _G[self:GetName().."Icon"]
  18.                 local border = _G[self:GetName().."NormalTexture"]
  19.                 button:SetNormalTexture("Interface\\BUTTONS\\UI-Quickslot2")
  20.                 icon:SetTexCoord(.05, .95, .05, .95);
  21.                 icon:SetPoint("TOPLEFT", button, 0, 0);
  22.                 icon:SetPoint("BOTTOMRIGHT", button, 0, 0);
  23.                 border:ClearAllPoints()
  24.                 border:SetPoint("TOPLEFT", button, -14, 14)
  25.                 border:SetPoint("BOTTOMRIGHT", button, 14, -14)
  26.                 colorit(border)
  27.         end);
  28.        
  29.         hooksecurefunc("ActionButton_UpdateUsable", function(self)
  30.                 colorit(_G[self:GetName().."NormalTexture"])
  31.         end);
  32.        
  33.         hooksecurefunc("ActionButton_ShowGrid", function(self)
  34.                 colorit(_G[self:GetName().."NormalTexture"])
  35.         end);
  36.        
  37.         hooksecurefunc("PetActionBar_Update",  function()
  38.                 for i, object in pairs({"PetActionButton", "ShapeshiftButton", "PossessButton"}) do
  39.                         for number = 1, 12 do
  40.                                 local button = _G[object..number];
  41.                                 local icon = _G[object..number.."Icon"];
  42.                                 if button then
  43.                                         button:SetNormalTexture("Interface\\BUTTONS\\UI-Quickslot2")
  44.                                 end
  45.                                 local border
  46.                                 if (object == "ShapeshiftButton" or object == "PetActionButton") then
  47.                                         border = _G[object..number.."NormalTexture2"]
  48.                                         if border then
  49.                                                 border:ClearAllPoints()
  50.                                                 border:SetPoint("TOPLEFT", button, -15, 15)
  51.                                                 border:SetPoint("BOTTOMRIGHT", button, 15, -15)
  52.                                                 colorit(border)
  53.                                         end
  54.                                         if icon then
  55.                                                 icon:SetTexCoord(.05, .95, .05, .95);
  56.                                                 icon:SetPoint("TOPLEFT", button, -1, 1);
  57.                                                 icon:SetPoint("BOTTOMRIGHT", button, 1, -1);
  58.                                         end            
  59.                                 else
  60.                                         border = _G[object..number.."NormalTexture"]
  61.                                         if border then
  62.                                                 border:ClearAllPoints()
  63.                                                 border:SetPoint("TOPLEFT", button, -14, 14)
  64.                                                 border:SetPoint("BOTTOMRIGHT", button, 14, -14)
  65.                                                 colorit(border)
  66.                                         end
  67.                                         if icon then
  68.                                                 icon:SetTexCoord(.05, .95, .05, .95);
  69.                                                 icon:SetPoint("TOPLEFT", button, 0, 0);
  70.                                                 icon:SetPoint("BOTTOMRIGHT", button, 0, 0);
  71.                                         end                            
  72.                                 end
  73.                         end
  74.                 end
  75.         end);
  76.  
  77.         hooksecurefunc("AuraButton_Update", function(self, index)
  78.                 if auraborder then
  79.                         local button = _G[self..index]
  80.                         local icon = _G[self..index.."Icon"]
  81.                         local border = _G[self..index.."Border"]
  82.                         if icon then
  83.                                 icon:SetTexCoord(.07, .93, .07, .93);
  84.                                 --icon:SetPoint("TOPLEFT", button, -1, 1);
  85.                                 --icon:SetPoint("BOTTOMRIGHT", button, 1, -1);
  86.                         end
  87.                         if border then
  88.                                 border:SetTexture("Interface\\BUTTONS\\UI-Quickslot2")
  89.                                 border:ClearAllPoints()
  90.                                 border:SetPoint("TOPLEFT", button, -12, 12)
  91.                                 border:SetPoint("BOTTOMRIGHT", button, 12, -12)
  92.                                 border:SetTexCoord(0, 1, 0, 1)
  93.                                 colorit(border)
  94.                         end
  95.                         if button and not border then
  96.                                 newborder = button:CreateTexture("$parentOverlay", "ARTWORK")
  97.                                 newborder:SetParent(button)
  98.                                 newborder:SetTexture("Interface\\BUTTONS\\UI-Quickslot2")
  99.                                 newborder:SetPoint("TOPLEFT", button, -12, 12)
  100.                                 newborder:SetPoint("BOTTOMRIGHT", button, 12, -12)
  101.                                 colorit(newborder)
  102.                         end
  103.                 end
  104.         end);
  105.  
  106.         for i = 1, 3 do
  107.                 local button = _G["TempEnchant"..i]
  108.                 local icon = _G["TempEnchant"..i.."Icon"]
  109.                 icon:SetTexCoord(.07, .93, .07, .93);
  110.                 --icon:SetPoint("TOPLEFT", button, 0, 0);
  111.                 --icon:SetPoint("BOTTOMRIGHT", button, 0, 0);
  112.                 local border = _G["TempEnchant"..i.."Border"]
  113.                 border:SetTexture("Interface\\BUTTONS\\UI-Quickslot2")
  114.                 border:ClearAllPoints()
  115.                 border:SetPoint("TOPLEFT", button, -12, 12)
  116.                 border:SetPoint("BOTTOMRIGHT", button, 12, -12)
  117.                 border:SetTexCoord(0, 1, 0, 1)
  118.                 colorit(border)
  119.         end
  Reply With Quote
05-23-11, 10:16 AM   #4
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Ah - I missed the links in the OP...I must have thought it was part of a sig.

Anyway...it looks like the original method iterates over everything every time one of the hooked functions is called, whereas the new method specifically targets the appropriate UI widget.

If the hooked functions are being called many times in a short period, the excessive iterations (and thereby all of the function calls per iteration) will definitely have a huge impact on your FPS, whereas the specific targeting may still have minor performance issues (if not throttled) but definitely nothing of the magnitude of the "let's change everything" approach.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
05-23-11, 01:29 PM   #5
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
I guess I'm trying to determine if the first approach is fundementally unsound, or just poorly implemented.

The content of my "main" function is all conditional on what objects are being passed to it, and in my hooksecurefunc calls I'm trying to say "hey go check that main function, and if it knows who you are, do what it tells you".

It seems like this would be fairly efficient resource-wise, but I'm largely ignorant of best practices & optimization (but learning ).
  Reply With Quote
05-23-11, 02:17 PM   #6
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
Your original code creates at least three garbage tables every time one of the hooked functions is called. Some of those hooked functions, like ActionButton_UpdateUsable, get called a lot: once per visible action button for each action that becomes usable/unusable. Every action button is registered for ACTIONBAR_UPDATE_USABLE, and each one responds by calling ActionButton_UpdateUsable separately.



Imagine you have 20 action buttons visible, and 10 of them become unusable when you unstealth. If you unstealth, ACTIONBAR_UPDATE_USABLE fires 10 times, and ActionButton_UpdateUsable gets called 10*20 = 200 times. Then, your hook creates one garbage table each call:
Code:
hooksecurefunc("ActionButton_UpdateUsable", function(self)
                for i, v in pairs({"ActionButton", "MultiBarBottomLeftButton", "MultiBarBottomRightButton", "MultiBarLeftButton", "MultiBarRightButton", "MultiCastActionButton", "MultiCastSummonSpellButton", "MultiCastRecallSpellButton"}) do
Then, BorderBeautification gets called for each of those 8 object types, totaling 200*8 = 1600 calls. Each of those calls creates two more garbage tables for top and bottom color:
Code:
local BorderBeautification = function(object)
       
                if IsAddOnLoaded("FacePaint") and FacePaint then
                        tc = {r = topcolor.r, g = topcolor.g, b = topcolor.b, a = topalpha}
                        bc = {r = bottomcolor.r, g = bottomcolor.g, b = bottomcolor.b, a = bottomalpha}
                else
                        tc = {r = BorderColors.top.r, g = BorderColors.top.g, b = BorderColors.top.b, a = BorderColors.top.a}
                        bc = {r = BorderColors.bot.r, g = BorderColors.bot.g, b = BorderColors.bot.b, a = BorderColors.bot.a}
                end
In total, 3400 garbage tables created by unstealthing in this case--200 object list tables plus 1600*2 color tables.

That explains why your memory usage ballooned when you spammed stealth/unstealth. The other technique in your second version is more efficient because it doesn't recolor the same button multiple times per event like the first did. It still would still create a fair number of garbage color tables though.



I suggest using the preexisting topcolor/bottomcolor and BorderColors tables instead of copying them each time. Using the second version of your code, here's an updated colorit function that doesn't create any new tables:
lua Code:
  1. local colorit = function(object)
  2.     local tc, bc, ta, ba;
  3.     if IsAddOnLoaded("FacePaint") and FacePaint.enable and not FacePaint.invert then
  4.         tc, ta = topcolor, topalpha;
  5.         bc, ba = bottomcolor, bottomalpha;
  6.     elseif IsAddOnLoaded("FacePaint") and FacePaint.enable and FacePaint.invert then
  7.         tc, ta = bottomcolor, bottomalpha;
  8.         bc, ba = topcolor, topalpha;
  9.     else
  10.         tc, bc = bordercolor.top, bordercolor.bot;
  11.         ta, ba = tc.a, bc.a;
  12.     end
  13.     object:SetGradientAlpha("VERTICAL", bc.r, bc.g, bc.b, ba, tc.r, tc.g, tc.b, ta)
  14. end
  Reply With Quote
05-23-11, 03:05 PM   #7
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
@Saiket: very eyeopening & helpful (& humbling ), ty!

I think I've reread your post & reexamined my code x100 so far, and am nearing comprehension.

I'll be trying out the table-less colorit function. Somewhat related, I hadn't been able to capture/transfer color values from FacePaint's variables (topcolor, topalpha, bottomcolor, bottomalpha) without using global variables in both that AddOn and this. I'm now wondering if it was due to my use of tables, and I should probably revisit that in FacePaint.

In general is there an accepted practice of sharing varibles between AddOns (or at least one reading the other) which avoids the global namespace? Saved variables I'm guessing?
  Reply With Quote
05-23-11, 03:09 PM   #8
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,860
Saved variables are in the global namespace.
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

  Reply With Quote
05-23-11, 03:42 PM   #9
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
Originally Posted by Aprikot View Post
In general is there an accepted practice of sharing varibles between AddOns (or at least one reading the other) which avoids the global namespace? Saved variables I'm guessing?
Most addons have a main table they use for all of their storage, so that no matter how many variables and functions they use, the "namespace" main table only takes up one global. For example if FacePaint had one global table named FacePaint, you could save and access the color tables to/from FacePaint.topcolor and FacePaint.bottomcolor. You could also read the other mod's saved variables table as Seerah suggested.
  Reply With Quote
05-23-11, 04:12 PM   #10
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
Originally Posted by Seerah View Post
Saved variables are in the global namespace.
D'oh!

Originally Posted by Saiket View Post
Most addons have a main table they use for all of their storage, so that no matter how many variables and functions they use, the "namespace" main table only takes up one global. For example if FacePaint had one global table named FacePaint, you could save and access the color tables to/from FacePaint.topcolor and FacePaint.bottomcolor. You could also read the other mod's saved variables table as Seerah suggested.
I see, so would this:

lua Code:
  1. -- FacePaint Config
  2. local class = true
  3. local gradient = true
  4. topcolor = {
  5.     r = 0.9,
  6.     g = 0.9,
  7.     b = 0.9,
  8. }
  9. bottomcolor = {
  10.     r = 0.1,
  11.     g = 0.1,
  12.     b = 0.1,
  13. }
  14. topalpha = 1.0
  15. bottomalpha = 1.0

become this?

lua Code:
  1. -- FacePaint Config
  2. FacePaintTable = {
  3.     class = true
  4.     gradient = true
  5.     topcolor = {
  6.         r = 0.9,
  7.         g = 0.9,
  8.         b = 0.9,
  9.     }
  10.     bottomcolor = {
  11.         r = 0.1,
  12.         g = 0.1,
  13.         b = 0.1,
  14.     }
  15.     topalpha = 1.0
  16.     bottomalpha = 1.0
  17. }

I've been curious for awhile about namspace declarations at the beginning of AddOns (local _, ns = ...), but if I understand correctly, this is to use multiple Lua files under the same AddOn?
  Reply With Quote
05-23-11, 06:17 PM   #11
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
Originally Posted by Aprikot View Post
...snip...
lua Code:
  1. -- FacePaint Config
  2. FacePaintTable = {
  3.     class = true
  4.     gradient = true
  5.     topcolor = {
  6.         r = 0.9,
  7.         g = 0.9,
  8.         b = 0.9,
  9.     }
  10.     bottomcolor = {
  11.         r = 0.1,
  12.         g = 0.1,
  13.         b = 0.1,
  14.     }
  15.     topalpha = 1.0
  16.     bottomalpha = 1.0
  17. }

I've been curious for awhile about namspace declarations at the beginning of AddOns (local _, ns = ...), but if I understand correctly, this is to use multiple Lua files under the same AddOn?
Yep, the only thing special about that "ns" table is that it gets passed to each file. Your snippet above would work, or you could use the ns table instead.

I suggest creating "getter" functions instead though, to simplify how other addons interact with FacePaint. Basically, instead of requiring other mods to reach into the guts of FacePaint to get color values, FacePaint could provide a few standard, reliable functions to return color settings. If it had two functions, FacePaint.GetColorTop and FacePaint.GetColorBottom, they could handle the logic of when colors are flipped, or if class coloring is enabled, or any other feature FacePaint gets in the future. Here's one way you could set something like that up, with the ns table:
lua Code:
  1. local ns = select(2, ...)
  2. FacePaint = ns
  3.  
  4.  
  5. -- FacePaint Config
  6. local class = true
  7. local gradient = true
  8. local topcolor = {
  9.     r = 0.9,
  10.     g = 0.9,
  11.     b = 0.9,
  12. }
  13. local bottomcolor = {
  14.     r = 0.1,
  15.     g = 0.1,
  16.     b = 0.1,
  17. }
  18. local topalpha = 1.0
  19. local bottomalpha = 1.0
  20.  
  21.  
  22. function FacePaint.GetColorTop ()
  23.     if FacePaint.enable then
  24.         if FacePaint.invert then
  25.             return bottomcolor.r, bottomcolor.g, bottomcolor.b, bottomtalpha
  26.         else
  27.             return topcolor.r, topcolor.g, topcolor.b, topalpha
  28.         end
  29.     end
  30. end
  31. function FacePaint.GetColorBottom ()
  32.     if FacePaint.enable then
  33.         if FacePaint.invert then
  34.             return topcolor.r, topcolor.g, topcolor.b, topalpha
  35.         else
  36.             return bottomcolor.r, bottomcolor.g, bottomcolor.b, bottomalpha
  37.         end
  38.     end
  39. end

If FacePaint were set up like this, your colorit function could be simplified:
lua Code:
  1. local colorit = function(object)
  2.     local br, bg, bb, ba, tr, tg, tb, ta
  3.     if IsAddOnLoaded("FacePaint") and FacePaint
  4.         and FacePaint.GetColorTop and FacePaint.GetColorTop()
  5.     then -- Compatible version of FacePaint enabled
  6.         tr, tg, tb, ta = FacePaint.GetColorTop()
  7.         br, bg, bb, ba = FacePaint.GetColorBottom()
  8.     else
  9.         local top, bot = bordercolor.top, bordercolor.bot
  10.         tr, tg, tb, ta = top.r, top.g, top.b, top.a
  11.         br, bg, bb, ba = bot.r, bot.g, bot.b, bot.a
  12.     end
  13.     object:SetGradientAlpha("VERTICAL", br, bg, bb, ba, tr, tg, tb, ta)
  14. end
The extra checks for "if FacePaint and FacePaint.GetColorTop" just ensure things don't break when someone uses an older version of FacePaint.

Last edited by Saiket : 05-23-11 at 06:19 PM. Reason: Snip
  Reply With Quote
05-24-11, 12:25 PM   #12
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
Originally Posted by Saiket View Post
I suggest creating "getter" functions instead though, to simplify how other addons interact with FacePaint. Basically, instead of requiring other mods to reach into the guts of FacePaint to get color values, FacePaint could provide a few standard, reliable functions to return color settings. If it had two functions, FacePaint.GetColorTop and FacePaint.GetColorBottom, they could handle the logic of when colors are flipped, or if class coloring is enabled, or any other feature FacePaint gets in the future.
This is an excellent idea (soon to be adopted ).

Regarding namespace: at the top of the code you posted I'm having trouble understanding how a table is being created:

lua Code:
  1. local ns = select(2, ...)
  2. FacePaint = ns
  3.  
  4.  -- as opposed to
  5. FacePaint = {}
  6.  
  7. -- or am I to make a table after?
  8. local ns = select(2, ...)
  9. FacePaint = ns
  10. FacePaint.Test = {
  11. -- this is a table called "Test" in the "FacePaint" namespace?
  12. }

Also, with a namespace of "FacePaint" defined, does this mean that other AddOns could then reference FacePaint's tables, variables, and functions by name as FacePaint."name", or is this only intended for cross-file sharing within the FacePaint AddOn directory?
  Reply With Quote
05-24-11, 12:51 PM   #13
Starinnia
Ninja Code Monkey
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 84
Blizzard passes two variables in through the vararg for every file in your addon. The first is the name of the addon (as defined by the containing folder) and the second is a table that is unique to the addon.

So the code
Code:
local ns = select(2, ...)
FacePaint = ns
takes the table Blizzard provides and uses it for your addon's namespace. The namespace is then given the global name of FacePaint so it can be accessed externally from your addon.
  Reply With Quote
05-24-11, 02:20 PM   #14
Aprikot
A Frostmaul Preserver
 
Aprikot's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2010
Posts: 284
Originally Posted by Starinnia View Post
Blizzard passes two variables in through the vararg for every file in your addon. The first is the name of the addon (as defined by the containing folder) and the second is a table that is unique to the addon.

So the code
Code:
local ns = select(2, ...)
FacePaint = ns
takes the table Blizzard provides and uses it for your addon's namespace. The namespace is then given the global name of FacePaint so it can be accessed externally from your addon.
Ahhhh...all is made clear, thanks!
  Reply With Quote
05-24-11, 03:58 PM   #15
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Originally Posted by Starinnia View Post
Blizzard passes two variables in through the vararg for every file in your addon. The first is the name of the addon (as defined by the containing folder) and the second is a table that is unique to the addon.

So the code
Code:
local ns = select(2, ...)
FacePaint = ns
takes the table Blizzard provides and uses it for your addon's namespace. The namespace is then given the global name of FacePaint so it can be accessed externally from your addon.
Though that defeats the whole point of the AddOn-local table - at that rate, you could just declare a global variable and assign an empty table to it.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
05-24-11, 05:38 PM   #16
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
Originally Posted by Torhal View Post
Though that defeats the whole point of the AddOn-local table - at that rate, you could just declare a global variable and assign an empty table to it.
And waste a perfectly good table?!
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » hooksecurefunc & memory use


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off