Thread Tools Display Modes
01-11-15, 11:16 PM   #1
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Using variables across multiple files

Hello,

I've been trying to figure this out for a while now. I've googled everything I could possibly think of to try and find an answer I can understand. Also, let it be known that I'm still a very novice lua programmer; so any complex answers might go over my head

I'm fully aware that avoiding the usage of global variables is recommended, but is there a way to access variables across multiple addon files without the usage of global variables? Is it done by passing varargs? If so, I'm not even sure how that works either, despite seeing it in many addons.

Thanks!
  Reply With Quote
01-11-15, 11:26 PM   #2
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,860
The vararg (...) merely represents a variable number of arguments. The game client passes through two arguments to each Lua file in an addon. Think of it like the Lua file is contained in a function that executes it, and it looks like this:
Lua Code:
  1. function ExecuteFile(...)
  2.      --all Lua file contents, functions, etc, etc
  3. end
  4.  
  5. ExecuteFile()

That means that if you assign the vararg to variables in your file in the main scope (outside of any functions or other blocks of code, usually done at the top of your file) then you can have access to what Blizzard is passing through to your file.

The two arguments are 1. the name of your addon, and 2. a table that is local to all files in your addon's folder. Think of this table as being a hybrid of a global and a local. It's global in that you can access it across all your files, but it's local in that nothing outside of your addon knows about it. So now, imagine that the game is doing this:
Lua Code:
  1. local addonName, addonTable = "Name of Addon", {}
  2.  
  3. function ExecuteFile1(addonName, addonTable)
  4.      --your file contents
  5. end
  6.  
  7. function ExecuteFile2(addonName, addonTable)
  8.      --this file's contents
  9. end
  10.  
  11. ExecuteFile1()
  12. ExecuteFile2()

So, at the top of each of your Lua files, put this line (note, you can use whatever variable names you want, and they can even be called different things in each file - they'll still reference the same things):
Lua Code:
  1. local addonName, addonTable = ...

Now, anything stored in the addonTable table can be accessed in each file, and the table may be added to, etc. from each file.
__________________
"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
01-11-15, 11:50 PM   #3
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Thanks for the reply!

So I'm starting to understand but I'm not quite there yet it seems.

In the first file that's loaded (init.lua), I put this at the top
Lua Code:
  1. local AddOnName, AddOnTable = "My AddOn", {};
  2.  
  3. AddOnTable.myVar = "TESTING"

In my second file that's loaded (core.lua), I put
Lua Code:
  1. local AddOnName, AddOnTable = ...;
  2.  
  3. print(AddOnName); -- Correctly printed addon name
  4. print(AddOnTable.myVar); -- Printed nil :(

Last edited by Sweetsour : 01-11-15 at 11:56 PM.
  Reply With Quote
01-12-15, 12:03 AM   #4
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
The table already exists in all files. You don't initialize them. Seerah was describing what the game does internally when processing each Lua file in your addon, but this might've been confusing.

At the base of your code, what is known as the Main Chunk, the vararg contains the basic arguments passed to every Lua file in your addon. The contents of these arguments are the ID of your addon (literally the name of your addon's folder) and an empty table for your Lua files to share.

At the top of every Lua file you want access to these:
Code:
local name,addon=...;


For example, if your first file:
Code:
local name,addon=...;
addon.myvar="test";
In your second file:
Code:
local name,addon=...;
print(addon.myvar);


This will make your addon print test to the chat frame.
__________________
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
01-12-15, 12:08 AM   #5
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Oooooh, I clearly missed that part he was describing, thanks for the clarification! It works and makes a lot more sense!

So I'm guessing I want to add "AddOnTable." before every variable that I intend to access between files? Otherwise I just declare them as local, correct?
  Reply With Quote
01-12-15, 12:20 AM   #6
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Unless the entry you're referring to in the addon table is another table, you'll just get a copy of the value, not a link to the entry. This is just how Lua stores tables and other complex value types as opposed to the basic types (numbers, strings, boolean, and nil).



To further explain this, I'll break down how Lua process something like this:
Code:
MyAddOnTable.MyVar
  1. Lua sees the name MyAddOnTable and finds it as a table in the list of registered locals.
  2. Lua finds the indexing operator "." and expects the following text to be an index.
  3. Lua sees MyVar and looks at the table entry which that string index points to.



What this means is MyVar isn't a variable in the way we usually see one. It's a table entry while MyAddOnTable is the actual variable in the statement.
__________________
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 : 01-12-15 at 12:29 AM.
  Reply With Quote
01-12-15, 12:30 AM   #7
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Hmm, interesting.

So for example, in my init.lua file, I created the frame with "f = CreateFrame('Frame');". I should add the f variable to the addon table for use across other files (ie. registering and unregistering events), correct?

Then I would access the frame by coding the following?
lua Code:
  1. AddonTable.f:RegisterEvent("PLAYER_LOGIN");

Last edited by Sweetsour : 01-12-15 at 12:33 AM.
  Reply With Quote
01-12-15, 12:33 AM   #8
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
I think Seerah's post was slightly confusing because she forgot to actually pass any values when calling the functions, and also actually named the variables in the function definition (which means a vararg inside the function wouldn't contain any values). Here's a fixed version:

Code:
local addonName, addonTable = "Name of Addon", {}

function ExecuteFile1(...) -- vararg!
     --your file contents
end

function ExecuteFile2(...) -- vararg!
     --this file's contents
end

ExecuteFile1(addonName, addonTable)
ExecuteFile2(addonName, addonTable)
__________________
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
01-12-15, 12:38 AM   #9
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
It depends on what your overall goal is. If you think you need an event frame that your other addon files need to access, you can store a pointer to it in the addon table. I would suggest using more meaningful names than just f. It will help when you decide to look at this code again later in the future.



This is what I suggest:
lua Code:
  1. local name,addon=...;
  2. addon.EventFrame=CreateFrame("Frame");

- and -
lua Code:
  1. local name,addon=...;
  2. addon.EventFrame:RegisterEvent("PLAYER_LOGIN");
  3. addon.EventFrame:HookScript("OnEvent",function(self,event,...)
  4.     if event=="PLAYER_LOGIN" then
  5. --      More stuff here
  6.     end
  7. end);

The key points are using Frame:HookScript() since this might not be your only file using the frame. As such, it would be wise to check if we're working on the event we want. Since you're sharing this frame with your other files, the frame will also run the script for events your other files register for.
__________________
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 : 01-12-15 at 12:51 AM.
  Reply With Quote
01-12-15, 01:06 AM   #10
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Very cool, thanks!

One other question about HookScript(); is there a difference with SetScript()?
  Reply With Quote
01-12-15, 01:14 AM   #11
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
The only difference is if there's already a function set to that script. Frame:SetScript() overwrites the original function while Frame:HookScript() runs your function after the original. They both do the same if there is no function already there.
__________________
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
01-12-15, 01:21 AM   #12
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Alright, makes sense, very good to know!

Last question for the evening. What's the best way to call a function across files? Would it be just as simple as:

Lua Code:
  1. local name, addon = ...;
  2.  
  3. --file1.lua
  4. addon.myFunction();
  5.  
  6. --file 2.lua
  7. function addon.myFunction()
  8.      --do stuff
  9. end
  Reply With Quote
01-12-15, 01:24 AM   #13
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Looks good as long as the function gets defined before you try to call it. Files load in top-down order as listed in the TOC file.
__________________
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
01-12-15, 01:26 AM   #14
Sweetsour
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2014
Posts: 130
Cool, thanks for all the help everyone! Hopefully this'll give me some good traction to pick up on minor uncertainties.
  Reply With Quote
01-12-15, 03:37 AM   #15
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Same as above but using the ":" syntax.

Lua Code:
  1. local addonName, addonTable = ...;
  2.  
  3. local addon = CreateFrame("Frame");
  4.  
  5. function addon:Execute(...)
  6.   print(...)
  7. end
  8.  
  9. addon:Execute("onload")
  10.  
  11. addon:RegisterEvent("PLAYER_LOGIN");
  12. addon:HookScript("OnEvent",function(self,event,...)
  13.   self:Execute(event,...)
  14. end)

You can even mix it up by using different kind of variables. You could have a global variable that you open up to the global namespace.
Another variable for addon namespace. And a last one for local file access only.

Lua Code:
  1. local addonName, addonTable = ...;
  2.  
  3. local G,A,L, = {},{},{}
  4.  
  5. function G:Execute(...)
  6.   print(...)
  7. end
  8.  
  9. function A:Execute(...)
  10.   print(...)
  11. end
  12.  
  13. function L:Execute(...)
  14.   print(...)
  15. end
  16.  
  17. --open G to global scope
  18. MyGlobalAddonNameVariable = G
  19.  
  20. --open A to addon scope
  21. addonTable.A = A
  22.  
  23. --keep L local
  24. --nothing

Since you can bind functions by reference you could even use local functions and distribute them that way.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 01-12-15 at 03:45 AM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Using variables across multiple files

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