WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   General Authoring Discussion (https://www.wowinterface.com/forums/forumdisplay.php?f=20)
-   -   VARIABLES_LOADED - except they're not (https://www.wowinterface.com/forums/showthread.php?t=18078)

kerrang 09-07-08 05:45 PM

Quote:

Originally Posted by Mikord (Post 101629)
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 :)

kerrang 09-07-08 07:48 PM

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!

Mikord 09-08-08 01:41 AM

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)


kerrang 09-08-08 05:45 AM

Quote:

Originally Posted by Mikord (Post 101685)
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...

Quote:

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?

Mikord 09-08-08 07:42 AM

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.

VincentSDSH 09-08-08 08:41 AM

Quote:

Originally Posted by slizen (Post 101609)
Quote:

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.

kerrang 09-08-08 02:04 PM

Quote:

Originally Posted by Mikord (Post 101695)
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?

kerrang 09-08-08 02:19 PM

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?

Mikord 09-08-08 02:44 PM

Quote:

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.

kerrang 09-08-08 03:08 PM

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()
  ...

??

Mikord 09-08-08 03:13 PM

Quote:

Originally Posted by kerrang (Post 101718)
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.

kerrang 09-08-08 03:23 PM

Quote:

Originally Posted by Mikord (Post 101729)
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? :)

Mikord 09-08-08 03:24 PM

Quote:

Originally Posted by kerrang (Post 101727)
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)


Tuhljin 09-08-08 03:50 PM

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.

kerrang 09-08-08 04:09 PM

Quote:

Originally Posted by Tuhljin (Post 101734)
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...

Mikord 09-08-08 04:17 PM

Quote:

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?

kerrang 09-08-08 04:28 PM

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!

kerrang 09-08-08 04:31 PM

Quote:

Originally Posted by Mikord (Post 101738)
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


Mikord 09-08-08 04:37 PM

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.

kerrang 09-08-08 05:17 PM

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? :)


All times are GMT -6. The time now is 09:32 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI