Thread Tools Display Modes
11-08-12, 11:18 AM   #1
Aanson
A Flamescale Wyrmkin
Join Date: Aug 2009
Posts: 124
separate modules within 1 addon

Hi there ladies / gents.

Over the last year or so, I've been self learning Lua and it's turned into a bit of a hobby where writing code has become more enjoyable than.. ehem.. playing the game!

I've written a UI with optional integration into different aspects of the game that the user can disable/enable at will (ie Chat, Auction House etc etc). For clarity's sake, modules.

Each module is a different Lua file within the one addon, so effectively, the TOC reads:

Core.lua
Auction_Module.lua
Chat_Module.lua
KeyBindButtonBox_Module.lua

While the Core.lua file contains many functions that other modules use, functions within each module are created solely for that module.

What I would very much like to do is this... If, say, the Chat module is shown as disabled in my DB on startup or reload, I'd like to be able to prevent any of that module's code from being read.

I've experimented in several ways to do this. My most recent attempt was to call a function in Core.lua which would send a addon message with the 2nd arg being "load Chat Module". The chat_Module.lua in turn has an OnEvent script that would listen for that call and read/call the functions when it gets it.

It works. But not very well. I've found that it never loads the chat script when I log in for the first time, but it always does when I reload.

I'm guessing that the reason for this is that by the time the game has read (and called) my "load Chat Module" function in Core.lua, it hasn't even started reading Chat_Module.lua yet. Like I say, that's just my guess, I might be well off.

Any suggestions for this would be greatly appreciated. I'm just very cautious about wasting memory if it's not required... especially with me being a beginner and all.

P.S. Phanx, if you read this, thanks very much for your suggestions and code regarding setting up defaults for the addon's DB.


Aanson.
__________________
__________________
  Reply With Quote
11-08-12, 11:35 AM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,322
I would suggest putting the modules in their own LoD addon with the core as a dependency. Then whenever you need to load them, you can use LoadAddOn() on them. If you really want to continue having them contained in the single addon, then you could either make a toggle function in the module and either make that global so you can call it or put a pointer to it in the addon's private table so the core has access to it.

Here's an example of the second method using the addon's private table. Note, this is only accessible in Lua files, there is no equivalent for XML.

In Chat_Module.lua
Lua Code:
  1. local name,addon=...;
  2. function addon.ChatModuleEnable()
  3. -- Enable your chat module here
  4. end

In Core.lua
Lua Code:
  1. local name,addon=...;
  2. addon.ChatModuleEnable();-- Calls function defined in Chat_Module.lua

This works because a vararg in the main chunk of your Lua files returns the Addon's string ID (the name of the folder your addon is contained in) and an empty local table generated on load that is shared among all of your Lua files.

Note you can put the function call anywhere, but the variables need to be defined in the main chunk as the vararg changes if put in a function.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 11-08-12 at 11:40 AM.
  Reply With Quote
11-08-12, 01:44 PM   #3
Aanson
A Flamescale Wyrmkin
Join Date: Aug 2009
Posts: 124
Cheers Phantom,

Yeah that's how I have it just now. Well, the only difference is that in your example code, I've split 'addon' into two separate tables with the code:

Lua Code:
  1. local addon, addonName = ...
  2.  
  3. core[1] = {};
  4. core[2] = {};
  5.  
  6. local F, V = unpack(select(2, ...));

But even with that, I still can't get it to work. The main reason I think is that I'd need to encompass all of my Chat_Module.lua functions within the ChatModuleEnable() function that you suggested in order to ensure that the code is only read if the module is enabled.

Can I dare ask?

On a scale of 1 to 10 on the bad practice scale, how bad would it be for the Chat_Module.lua functions to be read and stored to memory, but never called? (when the module is disabled, that is).

Hope I'm making sense here.


Aanson
__________________
__________________
  Reply With Quote
11-08-12, 03:50 PM   #4
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Aanson View Post
Lua Code:
  1. local addon, addonName = ...
  2.  
  3. core[1] = {};
  4. core[2] = {};
  5.  
  6. local F, V = unpack(select(2, ...));
I'm not really sure where you are going (or trying to go) with that code, and it should be dying with nil value errors because you're attempting to add values to a core table you never defined as a table (or anything else).

Also, the vararg passed to addon Lua files gives the addon's name first, and the private table second, so your order is backwards:

Code:
local addonName, addonPrivateTable = ...
Also also, since you already have a reference to the private table, there's no need to spend a select call to get it again:

Code:
local F, V = unpack(addonPrivateTable)
Originally Posted by Aanson View Post
how bad would it be for the Chat_Module.lua functions to be read and stored to memory, but never called?
Not bad at all. Even if your chat module is 100 KB of code, who cares? The static memory usage of a WoW addon is absolutely irrelevant on any machine capable of running WoW in the first place. If you are somehow eating up 100 MB then you will probably want to look into why your chat module was taking up that much memory, but otherwise, remember the two rules of program optimization:

First Rule of Program Optimization: Don’t do it.
Second Rule of Program Optimization (for experts only!): Don’t do it yet.
If you'd prefer to roll your own module system, I'd go with something like this:

Core file loads first:
Code:
addon.modules = {}

function addon:RegisterModule(name, object)
     -- Really, this function is not necessary, but it makes it much easier
     -- to add common features to all of your modules, like self:Print or
     -- other common methods. Also, an API feels cleaner than just adding
     -- stuff to your table from other files.

     self.modules[name] = object
end

function addon:ADDON_LOADED(name)
     if name ~= addonName then return end
     -- By the time this event fires for this addon, all of
     -- this addon's files and saved variables have loaded.

     -- Initialize your saved variables here.

     -- Do core initialization stuff here.

     for moduleName, moduleObject in pairs(self.modules) do
          if self.db.modules[moduleName].enable then
               moduleObject:Enable()
          end
     end
end
And in each module file, which loads after the core file:
Code:
function module:Enable()
          [color="Silver"][i]-- Do stuff here that should happen when the module loads.
end

addon:RegisterModule("SomeModule", module)
Then, set up your addon's saved variables to include something like this:
Code:
addon.db = {
     modules = {
          SomeModule = {
               enable = true, -- This module is enabled.
               someOtherSetting = 9000,
          },
          OtherModule = {
               enable = false, -- This module is NOT enabled.
               foo = "bar",
          },
     }
}
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
11-08-12, 06:32 PM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,322
Whenever I need to have a set of functions specific to a module in the private table, I usually have a table specifically for the module and store it within the private table. This would produce something like the following.

Module1
Code:
local name,addon=...;
local module={};-- Create module table
addon.Module1=module;-- This stores our local module table as "Module1" of the addon table

module.Variable="Some value";

function module.Enable()
-- Enable module here
end

function module.Disable()
-- Disable module here (if you want to be able to)
end

function module.DoSomething()
-- Any other stuff you want to do
end
Core
Code:
local name,addon=...;
addon.Module1.Enable();-- Enable Module1


Note you can use the private table like any other table and being shared among all Lua files of your addon opens it up to easily be used to create an internal API in whatever structure you want.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 11-08-12 at 06:38 PM.
  Reply With Quote
11-09-12, 10:21 AM   #6
Aanson
A Flamescale Wyrmkin
Join Date: Aug 2009
Posts: 124
Thank you

That sounds great. Phanx, apologies, I wrote that from memory yesterday and obviously didn't pay attention to what I was doing, I've copy / pasted now which might explain where I'm going with that first bit of code you quoted there:

Lua Code:
  1. local addon, addonName = ...
  2.  
  3. addonName[1] = {};
  4. addonName[2] = {};
  5.  
  6. EarthernUI = addonName;
  7. EarthernDB = {};
  8.  
  9. local F, V = unpack(select(2, ...));

... but clearly from what you've said there, I'll be changing it to ...

Lua Code:
  1. local addonName, addonTable = ...
  2.  
  3. addonTable[1] = {};
  4. addonTable[2] = {};
  5.  
  6. EarthernUI = addonTable;
  7. EarthernDB = {};
  8.  
  9. local F, V = unpack(select(2, ...));

Thank you for taking the time out to help guys. I think I'll def go with the format you provided and I like the idea of using separate tables within 'F' for each module.

Thanks again!


Aanson
__________________
__________________

Last edited by Aanson : 11-09-12 at 10:35 AM.
  Reply With Quote
11-09-12, 05:26 PM   #7
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
What I'm not understanding is why you're going with an array-style table (using addonTable[1] and addonTable[2])...
__________________
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
11-09-12, 06:38 PM   #8
Aanson
A Flamescale Wyrmkin
Join Date: Aug 2009
Posts: 124
Yeah, that's likely because I didn't mention why. It's solely for the purpose of separating functions from variables/constants/defaults.

F stores only functions
V.Defaults = {}
V.Media = {}
V.Colours = {}

It's just my preference, nothing more.


Aanson
__________________
__________________
  Reply With Quote
11-09-12, 08:09 PM   #9
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
I think what he means is why are you calling it addonTable[1] instead of addonTable.Functions or whatever.

Also, "local F, V = unpack(select(2, ...))" is a very bizarre way of assigning those variables. You've already assigned "addonTable" to what you're pulling out with "select(2,...)" and using unpack could potentially be dangerous unless you're quite sure of the order of things.

Last edited by semlar : 11-09-12 at 08:22 PM.
  Reply With Quote
11-09-12, 08:32 PM   #10
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,322
Could be that he doesn't know tables can have indicies of different types too.

The type table implements associative arrays, that is, arrays that can be indexed not only with numbers, but with any value (except nil). Tables can be heterogeneous; that is, they can contain values of all types (except nil). Tables are the sole data structuring mechanism in Lua; they can be used to represent ordinary arrays, symbol tables, sets, records, graphs, trees, etc. To represent records, Lua uses the field name as an index. The language supports this representation by providing a.name as syntactic sugar for a["name"]. There are several convenient ways to create tables in Lua (see §2.5.7).

Like indices, the value of a table field can be of any type (except nil). In particular, because functions are first-class values, table fields can contain functions. Thus tables can also carry methods (see §2.5.9).
Lua 5.1 Reference Manual §2.2
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » separate modules within 1 addon

Thread Tools
Display Modes

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