Thread Tools Display Modes
02-22-09, 07:39 AM   #1
ZikO
An Aku'mai Servant
Join Date: Jun 2008
Posts: 36
Can't load default values :/

Hi.

I have a one question.

I have built simple AddOn base on this tutorial
http://www.tentonhammer.com/node/26414

I have a BatleCrySv variable which is saved between every session and it looks like below:

Code:
BattleCrySv = {
    Cries = {},
    OutputType
}
I have also a BattleCrySvDefaults variable as below:
Code:
BattleCrySvDefaults = {
    Cries = {
        "Not in the face!",
        "Not in the groin!",
        "Not in the kneecaps!"
    },
    OutputType = "SAY"
}
Now, when AddOn is run first time it should copy default details from BattleCrySvDefaults to BattleCrySv. Unfortunately my script does not do that. I have used the code below:

Code:
function BattleCry:Event()
	if(event == "ADDON_LOADED") then
		if(arg1 == self.ADDON_NAME) then
			-- Handle saved variables
			for key,value in pairs(BattleCrySvDefaults) do
				if(not BattleCrySv[key]) then
					BattleCrySv[key] = value;
				end
			end
			BattleCryBC:AddOnLoaded();
		end
	elseif(event == "PLAYER_REGEN_DISABLED") then
		BattleCryBC:SendRandomCry();
	end
end
I have double checked if script at least reach the second if block (that with arg1 == ...) and it does but the loop for either does not seem to run or details are not copied into BattleCrySv variable.

What do you think is wrong here, thanks

Regards.
  Reply With Quote
02-23-09, 06:43 AM   #2
ZikO
An Aku'mai Servant
Join Date: Jun 2008
Posts: 36
Hi. It's me again

Well, I have found the problem, which was besically with wrong definition of the variable BattleCrySv. Since I have defined it as

Code:
BattleCrySv = {
    Cries = {},
    OutputType
}
these valius have already existed. Consequrntly, if(not BattleSv[key]) was always false as these fields existed and if block was always skipped.

One little change

Code:
BattleCrySv = {}
got it right immediately. Now my defualts values are loaded correctly if AddOn is run first time. ....... just incase if someone else has or has had the same problem.

regards.
  Reply With Quote
02-23-09, 06:52 AM   #3
Mera
Retired of WoW, In ESO :)
 
Mera's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Apr 2008
Posts: 331
I dont see why do you use 2 SavedVariables nor 2 tables, I think it uses extra memory for nothing, if you want a recommandation use only 1 savedvariable and in your code you do "if not savedvariables then savedvariables = blablablahardocded" its better than "if not savedvariables then copy to savedvariables something from another savedvariables", SavedVariables are mainly useful for saving user options but default values are coder options you could hardcode direclty. For an interactive sample you could check at my latest code Broker_CPU in my sig, it does exacltly what you do with just 1 savedvariable BROKERCPUDB which should be a table, if not or the options insides does not match the types boolean, numbers etc then revert them back to defaults.
__________________
If you need to reach me I'm in ESO, @class101 or "Fathis Ules i"
addons: SpamBayes, BrokerCPU
projects: ThunderBayes
Mera[xeh]? - La CroisadeEcarlate (wow)

Last edited by Mera : 02-23-09 at 07:00 AM.
  Reply With Quote
02-23-09, 12:46 PM   #4
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
I was working on it this way.

Saved variable declared in the *.toc file as e.g. ##SavedVariables : AddonNameSavedVars

which automatically creates a global table with that name in the AddonName.lua file in the Saved Variables folder.

Then in my event handler method putting something like this

Code:
function AddonName:OnEvent(self, event, addon)
if(event == "ADDON_LOADED") then
        if addon ~= "AddonName" then return end;
            if not AddonNameSavedVars then
                AddonNameSavedVars= {}; 
            end;
            AddonNameSavedVarsLocal = setmetatable (AddonNameSavedVars, {_index = DEFAULT_SETTINGS});
            self:UnregisterEvent("ADDON_LOADED");
end
The setmetatable function allows you to use either your saved variables data or if it's not there the default settings (which as Mera said can be hard coded in to the lua file as a local var). I like to place the whole lot into the local sv as it's a potential mixture of both the global and the default.

At least this is my understanding of it; I am in the process of trying to write my first addon so if I have this wrong or it could be done better, don't hesitate to say anyone.

BTW Thanks to Slakah for posting that code a while back
__________________


ultima ratio regum
  Reply With Quote
02-23-09, 12:52 PM   #5
Zergreth
A Fallenroot Satyr
 
Zergreth's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 24
You can shorten the existance check to a single line:
Code:
AddonNameSavedVars = AddonNameSavedVars or {};
  Reply With Quote
02-23-09, 01:09 PM   #6
ZikO
An Aku'mai Servant
Join Date: Jun 2008
Posts: 36
Thanks for answers.

Well, you might ask yourself why that silly person use this memory eating solution not anything easier and more efficient. The answer is:
because im beginner and you have just enlighten me here and I am very happy you have done it.

I am gonna immediately change it as your codes are much more effective and I bear in mind how it is important in WoW.

Many Many Thanks =)
  Reply With Quote
02-23-09, 01:43 PM   #7
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
Originally Posted by Zergreth View Post
You can shorten the existance check to a single line:
Code:
AddonNameSavedVars = AddonNameSavedVars or {};
Ah, of course; cool. Still getting used to nil (values) being both an unknown and a boolean depending on how it's used (or both as your example seems to indicate!).
__________________


ultima ratio regum
  Reply With Quote
02-28-09, 03:40 AM   #8
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
Originally Posted by Zergreth View Post
You can shorten the existance check to a single line:
Code:
AddonNameSavedVars = AddonNameSavedVars or {};
Actually, I was thinking, could I not shorten that whole thing further to

Code:
  AddonNameSavedVarsLocal = setmetatable (AddonNameSavedVars or {}, {_index =  DEFAULT_SETTINGS});
?
__________________


ultima ratio regum
  Reply With Quote
02-28-09, 03:50 AM   #9
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by DreamStorm View Post
Actually, I was thinking, could I not shorten that whole thing further to

Code:
  AddonNameSavedVarsLocal = setmetatable (AddonNameSavedVars or {}, {_index =  DEFAULT_SETTINGS});
?
Yes and no, it will not work if your saved variable has subtables, due to reasons highlighted here : http://www.wowinterface.com/forums/s...aved+variables.

Last edited by Slakah : 02-28-09 at 03:20 PM.
  Reply With Quote
02-28-09, 12:28 PM   #10
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
Ah, OK. I pretty much get that. Thanks for the link Slakah.

Can you clear up one thing for me (hopefully I am not being too stupid)? When you say 'subtables' do you mean any key/value pair? For example;

Code:
 tbl = { 
this = {that}
};

--being equivalent to 

tbl ["this"] = that;

--being eqivalent to 

tbl = {
["this"] = that
};
I mean equivalent in terms of the context here. So the value is effectively considered a subtable whether it is typed as a table or not, by virtue of being referenced by a key.

Would this, then work in the context of what you are saying?

Code:
local  DEFAULT_SETTINGS = {
    ["BUTTON_SHOW"] = true,
    ["BUTTON_LOCK"] = false
};

AddonNameSavedVarsLocal = setmetatable (AddonNameSavedVars or {}, {_index =  DEFAULT_SETTINGS});
Or do you mean specifically keys referencing tables?
__________________


ultima ratio regum
  Reply With Quote
02-28-09, 03:59 PM   #11
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Or do you mean specifically keys referencing tables?
yes (I think you call them nested tables, instead of subtables).

Anyway, basically if your default table has references to tables, then if you attempt to change that table, then all you will end up doing is modifying the table in the metatable (god this is confusing, I'll just use examples :P).

lua Code:
  1. defaults = {
  2.     tbl = {
  3.         foo = "bar",
  4.         dog = "animal",
  5.         tree = "plant"
  6.     }
  7. }
  8. SavedVar = setmetatable(SavedVar or {}, {__index = defaults})
  9.  
  10. print(SavedVar.tbl)
  11. --Will return: "table:<num>"
  12. --which leads you believe its acting correctly, but when you actually have a look with rawget, it isn't
  13.  
  14. print(rawget(SavedVar, "tbl"))
  15. --would return nil because there is nothing there
  16.  
  17. --and if you were to actually modify "tbl"
  18. SavedVar.tbl.foo = "blah"
  19. --it would modify "defaults", instead of SavedVar
  20.  
  21. print(defaults.tbl.foo)
  22. --would output "blah", because that was the table returned by __index.

So for defaults containing nested tables you would have to do
Code:
llocal function DeepMetatable(tbl, deftbl)
    for k, v in pairs(deftbl) do
        if type(v) == "table" then
            tbl[k] = DeepMetatable(tbl[k] or {}, v)
        end
    end
    return setmetatable(tbl, {__index = deftbl})
end
local defaults = {
    tbl = {
    }
}

SavedVar = DeepMetatable(SavedVar or {}, defaults)
Or equivalent.

p.s. It appears [ highlight=lua] doesn't like "[" or "]"

Last edited by Slakah : 03-01-09 at 05:22 AM.
  Reply With Quote
03-01-09, 04:55 AM   #12
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
So, if a table has nested tables, the setmetatable function produces SavedVar as a shallow copy of defaults rather than a deep one.
__________________


ultima ratio regum
  Reply With Quote
03-01-09, 05:24 AM   #13
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by DreamStorm View Post
So, if a table has nested tables, the setmetatable function produces SavedVar as a shallow copy of defaults rather than a deep one.
yes (if I understand what your saying :P)
  Reply With Quote
03-01-09, 07:06 AM   #14
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
Originally Posted by Slakah View Post
yes (if I understand what your saying :P)
Sorry, I didn't word my last post quite the way I meant to. But yeah, I think you do :-) I just wanted to be sure I understood how lua was handling that. The addon I am "working" on doesn't actually have nested tables in its saved vars (not yet at any rate), but your DeepMetatable function will be part of the arsenal whenever I need to have them.

Thanks again.
__________________


ultima ratio regum
  Reply With Quote
03-01-09, 09:29 AM   #15
ZikO
An Aku'mai Servant
Join Date: Jun 2008
Posts: 36
Code:
llocal function DeepMetatable(tbl, deftbl)
    for k, v in pairs(deftbl) do
        if type(v) == "table" then
            tbl[k] = DeepMetatable(tbl[k] or {}, v)
        end
    end
    return setmetatable(tbl, {__index = deftbl})
end
local defaults = {
    tbl = {
    }
}

SavedVar = DeepMetatable(SavedVar or {}, defaults)
I do not understand the use of return in this code. I mean what does it mean if you return:
Code:
return setmetatable(tbl, {__index = deftbl})
I thought setmetatable() is just the function to use if you want to set metamethod to your table but what does it mean if you return it from function?

Regards.
  Reply With Quote
03-01-09, 07:03 PM   #16
DreamStorm
A Deviate Faerie Dragon
 
DreamStorm's Avatar
Join Date: Feb 2009
Posts: 15
Originally Posted by ZikO View Post
I thought setmetatable() is just the function to use if you want to set metamethod to your table but what does it mean if you return it from function?
If you look at the bit of code below, you will see that the result of running the setmetatable function is assigned to the AddonNameSavedVarsLocal variable.

Code:
AddonNameSavedVarsLocal = setmetatable (tbl, {__index = deftbl});
In Slakah's example the DeepMetatable function returns the result of the setmetatable function after the key/value processing has been done, so it would be written in the code as;

Code:
AddonNameSavedVarsLocal = DeepMetatable(tbl, deftbl);
The DeepMetatable function is 'wrapping' the setmetatable function and in this case is just returning what the setmetatable function would return.
__________________


ultima ratio regum
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Can't load default values :/


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