Thread Tools Display Modes
09-07-08, 05:45 PM   #21
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Mikord View Post
GetSpellInfo exists before addons are loaded. You don't need to wait for any events.

Tested the following and it prints Mortal Strike as expected:

TestGSI.toc
Code:
## Interface: 20400
TestGSI.lua
TestGSI.lua
Code:
 ChatFrame1:AddMessage(GetSpellInfo(12294))
You see that's VERY interesting in itself - because any attempt I make to write to ChatFrames directly in the .LUA (e.g. not in a function) or even on the VL event fails (no error - but nothing written into chatframes either) - if I do it in PL and PEW is works MOST (but not all) of the time.

I this a lot of the problems I'm having here relate to my PC - it's not amazingly quick (it's 2 years old and wasn't state-of-the-art then!!) - but things like this and the issues I raised with 'losing' macros etc. (in another thread) would be explained by my PC just being a bit slow...

Not a problem for you guys I'm guessing BUT you might want to bear-in-mind that some of our USERS may have simiarly slow PCs

Example

My NowCarrying addon wants to ignore all the initial BAG_UPDATE events that people are spammed with on login - so I put in a manual delay of 0.5 sec before it starts to check them.

Worked fine for me - (a test suggested I only needed to wait about 0.2sec but I added a bit anyway) but a few people said they were seeing the 'spam' caused by those events so I increased it to 1sec

Then I get WOTLK and I started to see the spam myself so I increased it to 2 secs!!

So far so good - no-one has reported the spam since that change - but it just shows that your addons can behave differently on slower PCs/newer builds of WoW in ways you don't expect

p.s. Mik - thanks for the Frame example code - I've nicked it wholesale

Last edited by kerrang : 09-07-08 at 05:48 PM.
  Reply With Quote
09-07-08, 07:48 PM   #22
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Carrying on the savedvariables thing...

I have to initialise my SVs at some point - where the user hasn't previously 'saved' them.

All my addons upto now have done this at the highest level - e.g.

Code:
MYSV = ""

function MYVARIABLESLOADEDEVENT ()
  -- do something with MYSV
end
I'd assumed that MYSV is set to "" when the addon is loaded and then to whatever it's been saved to before VARIABLES_LOADED is called - but either way it won't be 'nil' when the addon tries to access it.

What someone suggested here earlier is that what I SHOULD be doing is more like

Code:
function MYVARIABLESLOADEDEVENT ()
  if not MYSV then
     MYSV = ""
  end
  -- do something with MYSV
end
Is that a better way to deal with it?

I ask mainly because I have a VERY rare bug whereby MYSV is being reset to "" by something - and the ONLY place it's reset is at the top of the .LUA file (as in the first example).

Why on EARTH this would happen anytime other than login/reload I've no idea - but as MYSV is never set to anything else, anywhere else - I don't know what else it could be!
  Reply With Quote
09-08-08, 01:41 AM   #23
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
There are definitely some timing consideration to keep in mind, but I'm fairly certain GetSpellInfo is valid before the first addon is loaded.

Also, I know you're just posting pseudocode to a degree, but you should try to avoid using global functions unless you really need to, which in the case of your event callbacks you no longer need to since you are passing them directly via :SetScript instead of calling them globally out of an XML file.

Anyways on to the saved variables.

There really isn't a need to initialize them at the highest level. Just construct your code such that the functions that rely on saved variables aren't called until they are valid. If you're using event callbacks that make use of your SV, then don't register them until after your SV are loaded or initialized via the ADDON_LOADED.

There are many ways to do it, but here is an example of one way.


Code:
local function OnAddonLoaded(self)
 -- Initialize your SV if they don't already exist...
 if not MyModSV then
  MyModSV = {
   showThatSpecialSomething = true,
   anotherSetting = "Yada",
  }
 end

 -- Register your bag updates or do whatever else requires your SV to be loaded.
 self:RegisterEvent("BAG_UPDATE")
end


local function OnBagUpdate(container)
 -- Do whatever on your bag update which you can be sure will
 -- not be called until after you SV are loaded or initialized.
 if MyModSV.showThatSpecialSomething then message("It's special!") end
end


local function OnEvent(self, event, arg1)
 if event == "ADDON_LOADED" then
  if arg1 ~= "MyMod" then return end
  self:UnregisterEvent("ADDON_LOADED")
  OnAddonLoaded(self)

 elseif event == "BAG_UPDATE" then
  OnBagUpdate(arg1)
 end
end

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:SetScript("OnEvent", OnEvent)
  Reply With Quote
09-08-08, 05:45 AM   #24
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Mikord View Post
There are definitely some timing consideration to keep in mind, but I'm fairly certain GetSpellInfo is valid before the first addon is loaded.
This whole issue kicked-off when I found that you (or at least I) can't use GetSpellInfo(SPELLNAME) at .LUA level or in the VL event - you can use GetSpellInfo(SPELLID) but for the usual 'rank' reasons I save SPELLNAMES and not IDs...

Also, I know you're just posting pseudocode to a degree, but you should try to avoid using global functions unless you really need to, which in the case of your event callbacks you no longer need to since you are passing them directly via :SetScript instead of calling them globally out of an XML file.
I tend to use a lot of global functions but I'm slowly moving towards assigning them to the frame as in

function MyFrame.onevent(...

rather than

function MYMOD_onevent(...

which I assume is seen as being the proper way?
  Reply With Quote
09-08-08, 07:42 AM   #25
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Ah yes, I didn't know you were trying to use GetSpellInfo with a spell name. In that case, it won't work until your spell book is updated (which is signaled with SPELLS_CHANGED). Also, it also won't retrieve information about a spell name that doesn't exist in your spell book at any time.

On the functions, yes it's better to create a "namespace" for your mod and define all publicly accessible functions/data as a part of it.

MyMod = {}
function MyMod.PubliclyAccessibleFunction(args...)
-or-
function MyMod:PubliclyAccessibleFunction(args...)

Event handlers however rarely need to be called outside of in response to an event and therefore can almost always by a completely local function like I demonstrated in the previous code.
  Reply With Quote
09-08-08, 08:41 AM   #26
VincentSDSH
Non-Canadian Luzer!
 
VincentSDSH's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2006
Posts: 350
Originally Posted by slizen View Post
Originally Posted by VincentSDSH
Didn't PEW use to be triggered when you zoned? (I know they mucked about with it a few years ago, can't recall how it's triggered now)
Why not use it and then just unregister it when it has run once?
Try asking the author that question; I was only attempting to relay a possibility as to why the author felt that PEW was unusable.
  Reply With Quote
09-08-08, 02:04 PM   #27
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Mikord View Post
On the functions, yes it's better to create a "namespace" for your mod and define all publicly accessible functions/data as a part of it.

MyMod = {}
function MyMod.PubliclyAccessibleFunction(args...)
-or-
function MyMod:PubliclyAccessibleFunction(args...)

Event handlers however rarely need to be called outside of in response to an event and therefore can almost always by a completely local function like I demonstrated in the previous code.
I've noticed many authors use this approach now - what, exactly is bad about using global functions - or assigning functions into your Frame - or using entirely local functions?
  Reply With Quote
09-08-08, 02:19 PM   #28
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Back on the SV thing - taking this code that Mik posted earlier

Code:
local function OnAddonLoaded(self)
 -- Initialize your SV if they don't already exist...
 if not MyModSV then
  MyModSV = {
   showThatSpecialSomething = true,
   anotherSetting = "Yada",
  }
 end

local function OnBagUpdate(container)
 -- Do whatever on your bag update which you can be sure will
 -- not be called until after you SV are loaded or initialized.
 if MyModSV.showThatSpecialSomething then message("It's special!") end
end
Surely, the first time a user runs that addon, they will get an error because MyModSV isn't declared as a global variable - only within a local function???

Once it's been saved and reloaded it will exist - but until that time it doesn't and thus you'll get an error?
  Reply With Quote
09-08-08, 02:44 PM   #29
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Originally Posted by Kerrang
Surely, the first time a user runs that addon, they will get an error because MyModSV isn't declared as a global variable - only within a local function???
Wrong. It will work just fine because it checks that it doesn't exist, and if it doesn't, it creates it. Variables (and functions) in lua are automatically global if they aren't specifically defined to be local.

Pay attention to the fact that the OnBagUpdate will NEVER be called prior to the loading or initialization of the SV because it's not registered until AFTER they are already loaded or initialized.

What I posted is fully functional as is. Try it out. Open your bags and move something. You'll get a message.

MyMod.toc
Code:
## Interface: 20400
## SavedVariables: MyModSV
MyMod.lua
MyMod.lua
Use all the code I posted earlier in post #23.

Last edited by Mikord : 09-08-08 at 02:57 PM.
  Reply With Quote
09-08-08, 03:08 PM   #30
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
So if we take this function

Code:
function test()
  if IS_WRATH then
    name,_,_,stack,_,duration,timeleft = UnitBuff(target,n) 
  else
    name,_,_,stack,duration,timeleft = UnitBuff(target,n)
  end
  return name,stack,duration,timeleft
end
name,stack,duration,timeleft are all declared as global variables?

I assume this would not be the case if we declared

Code:
local function test()
  ...
??
  Reply With Quote
09-08-08, 03:13 PM   #31
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Originally Posted by kerrang View Post
I've noticed many authors use this approach now - what, exactly is bad about using global functions - or assigning functions into your Frame - or using entirely local functions?
One simple reason is to avoid global namespace pollution and drastically reduce chances of collisions.

You decide to create an event handler called OW_OnEvent. Another mod author comes along and decides to create a mod that has the initials OW and predictably create his event handler to be called....you guessed it, OW_OnEvent. Oops whoever loaded first is now hosed because both XML files try to call OW_OnEvent which just got overwritten.

On the other hand, had they both been namespaced under their mod's name, there would be no problem.

There are also performance reasons too, but that would required an explanation of how lua works internally, and that is better left to a book.
  Reply With Quote
09-08-08, 03:23 PM   #32
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Mikord View Post
You decide to create an event handler called OW_OnEvent. Another mod author comes along and decides to create a mod that has the initials OW and predictably create his event handler to be called....you guessed it, OW_OnEvent. Oops whoever loaded first is now hosed because both XML files try to call OW_OnEvent which just got overwritten.
Surely if I declare a namespace (which is just a global table variable?)

e.g. OW = {}; OW.myfunc = .....

and someone else declares the same namespace/table - same problem?
  Reply With Quote
09-08-08, 03:24 PM   #33
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Originally Posted by kerrang View Post
So if we take this function
...
name,stack,duration,timeleft are all declared as global variables?
Yes, unless they were previously declared as locals and being accessed as upvalues. Try it:

Code:
local function test()
 name = "MyTest"
end

test()
print(name)
  Reply With Quote
09-08-08, 03:50 PM   #34
Tuhljin
A Flamescale Wyrmkin
 
Tuhljin's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2008
Posts: 106
This was mentioned a couple of times, but I haven't seen the OP address it and I think it may be the key to a solution here:

Does PLAYER_ENTERING_WORLD work?

Yes, you only want the initialization to happen once and PEW can be called multiple times per session, but you can simply unregister the event as part of the initialization.

Example:
Code:
MyAddon = {}

local function OnEvent()
  MyAddon.MainFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
  -- Do stuff
end

MyAddon.MainFrame = CreateFrame("Frame")
MyAddon.MainFrame:Hide()
MyAddon.MainFrame:SetScript("OnEvent", OnEvent)
MyAddon.MainFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
If you need to watch for other events, you can of course add in the requisite event == "WHATEVER" checks or even use SetScript to have a different function handle events after the PLAYER_ENTERING_WORLD initialization.

Last edited by Tuhljin : 09-08-08 at 03:52 PM.
  Reply With Quote
09-08-08, 04:09 PM   #35
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Tuhljin View Post
This was mentioned a couple of times, but I haven't seen the OP address it and I think it may be the key to a solution here:

Does PLAYER_ENTERING_WORLD work?
In the end I'm using PLAYER_LOGIN and unregistering it after it's first call - PLAYER_ENTERING_WORLD should work in the say way but as PL worked I didn't try it...
  Reply With Quote
09-08-08, 04:17 PM   #36
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Originally Posted by kerrang
Surely if I declare a namespace (which is just a global table variable?)

e.g. OW = {}; OW.myfunc = .....

and someone else declares the same namespace/table - same problem?
Naturally, but chances are you'd name it more descriptive like OWUberBagMod. What is more likely, having a collision with one global element, or having a collision with one of many global variables and functions in your mod?

Last edited by Mikord : 09-08-08 at 04:23 PM.
  Reply With Quote
09-08-08, 04:28 PM   #37
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
So a variable declared in a LOCAL function is still GLOBAL!? That's surprising and it means my function from earlier needs to be messy...

Code:
function test()
  local name,stack,duration,timeleft
  if IS_WRATH then
    name,_,_,stack,_,duration,timeleft = UnitBuff(target,n) 
  else
    name,_,_,stack,duration,timeleft = UnitBuff(target,n)
  end
  return name,stack,duration,timeleft
end
because
Code:
function test()
  if IS_WRATH then
    local name,_,_,stack,_,duration,timeleft = UnitBuff(target,n) 
  else
    local name,_,_,stack,duration,timeleft = UnitBuff(target,n)
  end
  return name,stack,duration,timeleft
end
wouldn't work of course (those variables being 'local' to that if statement only!
  Reply With Quote
09-08-08, 04:31 PM   #38
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Originally Posted by Mikord View Post
Naturally, but chances are you'd name it more descriptive like OWUberBagMod. What is more likely, having a collision with one global element, or having a collision with one of many global variables and functions in your mod?
I've noticed some mods declare their functions within their Frame - I assume this is just another way of having a 'namespace' (as a frame is just a table too?)

e.g.

Code:
local eventFrame = CreateFrame("Frame","OBI")
eventFrame.myfunc = function() ... end
or
Code:
local eventFrame = CreateFrame("Frame","OBI")
function eventFrame.myotherfunc() 
...
end
  Reply With Quote
09-08-08, 04:37 PM   #39
Mikord
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 61
Yes. That's correct. Although I don't know how adding one additional line of code to make your variables local to the function (as they should be) makes it messy.

On the frame question yes, but realize that nothing outside of the current file would then be able to access those functions because the frame is local to the chunk.

Last edited by Mikord : 09-08-08 at 04:39 PM.
  Reply With Quote
09-08-08, 05:17 PM   #40
kerrang
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Oct 2006
Posts: 109
Thanks for the time mik - very informative

On the Frame thing - and using it as your 'namespace' - surely you could define your frame as a global for that purpose?

Then that means ALL your frame's events etc are global i guess and you're back where you didn't want to be?
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » VARIABLES_LOADED - except they're not

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