Quantcast Better Saved Variables Tutorial? - WoWInterface
Thread Tools Display Modes
11-04-08, 03:20 PM   #1
HyperGig
A Cyclonian
Join Date: Jan 2008
Posts: 46
Better Saved Variables Tutorial?

The new guy here (again) and i was looking for better Saved Variables Tutorial. The only one that i see is the one from wowwiki which has ## Interface: 1300 in its demo toc file, which is slightly unnerving , and i don't think is all that clear. What did you peoplez use when you where getting started?

http://www.wowwiki.com/Saving_variab..._game_sessions

Thanks in advanced!! (again)

Last edited by HyperGig : 11-04-08 at 03:25 PM.
  Reply With Quote
11-04-08, 05:03 PM   #2
Cralor
Mmm... cookies!!!
 
Cralor's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2007
Posts: 772
I'm still new to this, but I think I have the gist of it.

First, you need to enable the SV in the TOC (shown in that tutorial).
Code:
SavedVariables: TESTDB
or
SavedVariablesPerCharacter: TESTDB
Second, you need to set up the default variables to save.
Example:
Code:
TESTDB = {
    variable1 = true;
    variable2 = true;
    variable3 = true;
    scale = 1.5;
    parent = "UIParent";
    point = "CENTER";
    parentPoint = "CENTER";
    x = 0;
    y = -100;
};
Third, you need to set it up so that you load the saved variables.
Example:
Code:
function FRAME_Update()
    FRAME:ClearAllPoints()
    FRAME:SetPoint(TESTDB.point, TESTDB.parent, TESTDB.parentPoint, TESTDB.x, TESTDB.y)
    FRAME:SetScale(RuneMoverDB.scale);
    if (FRAME:IsMouseEnabled()) then
        TESTDB.point, relativeTo, TESTDB.parentPoint, TESTDB.x, TESTDB.y = FRAME:GetPoint();
    end
end
(NOTE: This will vary drastically based on what variables you are looking to save. In this example, I have a frame's position saved and it's scale.)

Lastly, you need to update the saved variables if the user wishes to change them.

This is the basics of it. I hope this helped you a little. I am sorry if I have said something wrong. I am still really new
__________________
Never be satisfied with satisfactory.

Last edited by Cralor : 11-04-08 at 05:07 PM.
  Reply With Quote
11-04-08, 05:23 PM   #3
Akryn
A Firelord
AddOn Author - Click to view addons
Join Date: Mar 2008
Posts: 479
the simple version is that global variables that you indicate in your .toc will get saved in whatever state they're in at logout/reload (after PLAYER_LEAVING_WORLD, etc. fire, so you can change them at the last second if you want to).

they are then restored after your addon is loaded. note that the *after* means that OnLoad handlers, etc. will run before the saved variables are loaded (see the events ADDON_LOADED and VARIABLES_LOADED)
  Reply With Quote
11-05-08, 08:42 AM   #4
HyperGig
A Cyclonian
Join Date: Jan 2008
Posts: 46
Where do i put the
Code:
if (not my_var) then my_var = "default Val" end
part?
  Reply With Quote
11-05-08, 09:32 AM   #5
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Where do i put the
Code:
if (not my_var) then my_var = "default Val" end
In your function which is fired on "ADDON_LOADED" or "VARIABLES_LOADED".

The code I generally use for saved variables goes as follows (although I have started mucking about with metatables):

Code:
local addon = CreateFrame("Frame")
addon:RegisterEvent("ADDON_LOADED") --I use "ADDON_LOADED" because the majority of my mods use "AddonLoader".

local function OnEnable(self, event, addon)
	if addon ~= "my addon name" then return end
	
	if not AddonDB then
		AddonDB = {<defaults here>}
	end
	self:UnregisterEvent("ADDON_LOADED")

	<Register any events you want to register here>
	self:SetScript("OnEvent", MyEventHandler)
end

addon:SetScript("OnEvent", OnEnable)
Hope this helps you.
  Reply With Quote
11-05-08, 09:55 AM   #6
p3lim
A Pyroguard Emberseer
 
p3lim's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2007
Posts: 1,694
Originally Posted by HyperGig View Post
Where do i put the
Code:
if (not my_var) then my_var = "default Val" end
part?
much easier/cleaner would be like this:
Code:
TESTDB = TESTDB or {defaulttable}
  Reply With Quote
11-05-08, 10:22 AM   #7
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,670
In the past I have used AceDB-3.0 for working with my saved variables. In my current project, I wanted to try not using it. One problem that I have bumped into while using MYVAR = MYVAR or defaults is this:

When I add a new variable to be saved after MYVAR has been created, MYVAR doesn't pick it up. I get errors because even though it's defined in the defaults table, the addon is looking for it in MYVAR. Which is the best way to go about this?


edit: and P3lim, I don't think you need to put defaulttable in brackets {}, as it is already a table.
__________________
"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
11-05-08, 10:39 AM   #8
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by Seerah View Post
When I add a new variable to be saved after MYVAR has been created, MYVAR doesn't pick it up. I get errors because even though it's defined in the defaults table, the addon is looking for it in MYVAR. Which is the best way to go about this?
You can use metatables to set a default value if no value for that key is found.

i.e.

Code:
local defaults = {
	foo = "bar",
	tbl = {
		cat = "meow",
		dog = "bark",
		cow = "moo"
	}
}

local myDB = setmetatable(myDB or {}, {__index = defaults})
so if I were to do
Code:
print(myDB.foo)
then it would output "bar", and
Code:
print(myDB.tbl.cow)
would output "moo".

but then if I were to do
Code:
myDB.tbl.cow = "asleep"
then
Code:
print(myDB.tbl.cow)
would output "asleep".

Hope this helps.

Last edited by Slakah : 11-05-08 at 10:57 AM.
  Reply With Quote
11-05-08, 10:51 AM   #9
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,670
that last part confused me, where assigning a new value to the existing var still returned the old value, but...

I will look into metatables. I haven't worked with them yet. Just to clarify, my issue was with creating new options during development. Say I had

Code:
defaults = {
     scale = .9,
     fontsize = 14
}
Then, in my PEW function I put
Code:
MYMODDB = MYMODDB or defaults
That would make
Code:
print(MYMODDB.scale)
return .9

After playing with the code for a while, I decide I want to add an alpha option as well. So my defaults table changes to this
Code:
defaults = {
     scale = .9,
     fontsize = 14,
     alpha = .7
}

...

MYMODDB = MYMODDB or defaults
And somewhere in the code I have
Code:
f:SetAlpha(MYMODDB.alpha)
But, upon reloading the UI to pick up the changes made to the lua, I would get an error for
Code:
f:SetAlpha(MYMODDB.alpha)
because
Code:
print(MYMODDB.alpha)
would return nil


I probably didn't need to explain myself, as I'm sure you knew what I meant, and I will read up on metatables. Thanks, Slakah.
__________________
"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
11-05-08, 10:57 AM   #10
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
that last part confused me, where assigning a new value to the existing var still returned the old value, but...
ahh that was just me being silly with copy and paste, it should say it would output "asleep".
  Reply With Quote
11-05-08, 10:57 AM   #11
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,670
Okies - was wondering if it was a typo
__________________
"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
11-05-08, 12:17 PM   #12
slizen
A Deviate Faerie Dragon
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 15
Just checking your db table with:
Code:
MYMODDB = MYMODDB or defaults
is not enough.
The problem is it only checks if the table exists, not that all keys that are in the default table also exist in the MYMODB table.


So even if you add for example an alpha key to your defaults table, the table loaded from the saved variables where there is no alpha key will still be used.

Something like this should work:
Code:
for k,v in pairs(defaults) do
    if type(MYMODDB[k]) == "nil" then
        MYMODB[k] = v
    end
end
That or using a metatable as Slakah said.
  Reply With Quote
11-05-08, 12:24 PM   #13
HyperGig
A Cyclonian
Join Date: Jan 2008
Posts: 46
Thank you everyone for all your help... that why i love this community, everyone is always so helpful.

So the general consensus is to load previously / set default saved vars via the ADDON_LOADED event? Right now i can't do this unless I have a OnMouseUp on my frame set to executed another function that sets/loads the saved/default vars.
  Reply With Quote
11-06-08, 01:43 AM   #14
conner686
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Oct 2007
Posts: 4
Originally Posted by slizen View Post
Just checking your db table with:
Code:
MYMODDB = MYMODDB or defaults
is not enough.
The problem is it only checks if the table exists, not that all keys that are in the default table also exist in the MYMODB table.


So even if you add for example an alpha key to your defaults table, the table loaded from the saved variables where there is no alpha key will still be used.

Something like this should work:
Code:
for k,v in pairs(defaults) do
    if type(MYMODDB[k]) == "nil" then
        MYMODB[k] = v
    end
end
That or using a metatable as Slakah said.
I use this method, and it provides *no* protection if performed within the VARIABLES_LOADED event. My solution was gross, involving a timed event one second after VARIABLES_LOADED. I guess you can also use a one-shot PLAYER_ENTERED_WORLD event, but that didn't occur to me at the time.

That metatable thing looks *amazing.* I wish I hadn't been intimidated by the underscores when I first stumbled upon the concept, it looks like quite the ideal solution.
  Reply With Quote
11-09-08, 11:27 PM   #15
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,670
Okay... a follow up...

This did not work for me. Gave me a PocketPlotDB is nil error.
Code:
local PocketPlotDB = setmetatable(PocketPlotDB or {}, {__index = defaults})
Added
Code:
PocketPlotDB = PocketPlotDB or {}
in front of it and it worked for everything except the table of r,g,b,a values for my frame's background. Every other subtable (frame positions) worked... Then again, those subtables (frame positions) do not exist in the defaults table and are only added once the frame is actually moved.


This, modeled after what was shown in WoW Programming, gave me the same result as above. Worked for everything *except* the background color subtable.
Code:
PocketPlotDB = PocketPlotDB or {}
local ppdb = {}
setmetatable(PocketPlotDB, ppdb)
ppdb.__index = defaults

I finally went with conner686's method (since I do it at PLAYER_LOGIN anyhow) and it works perfectly.
Code:
for k,v in pairs(defaults) do
     if type(PocketPlotDB[k]) == "nil" then
          PocketPlotDB[k] = v
     end
end
__________________
"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
11-12-08, 08:14 AM   #16
lieandswell
A Cyclonian
 
lieandswell's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2007
Posts: 45
I'd like to use metatables for default settings, but I never got it to work. Instead, I do a version check at VARIABLES_LOADED, and if the user has an older version, I do something like this:
Code:
function MyAddOn.AddNewSettings(settings, defaults)
	for k, v in pairs(defaults) do
		if ( not settings[k] ) then
			if ( type(v) == "table" ) then
				settings[k] = {};
				settings[k] = MyAddOn.AddNewSettings(settings[k], defaults[k]);
			else
				settings[k] = v;
			end
		elseif ( type(v) == "table" ) then
			settings[k] = MyAddOn.AddNewSettings(settings[k], defaults[k]);
		end
	end
	return settings;
end
It goes through the settings table and adds any new ones that may have appeared in more recent versions.
  Reply With Quote
11-12-08, 08:34 AM   #17
dafire
Premium Member
AddOn Author - Click to view addons
Join Date: Jun 2005
Posts: 216
look at this if you look for an easy way how to store settings and maybe if you need easy localisation: http://github.com/tekkub/addontemplate/tree/master

the way tekkub does it it will only save changed settings.. it will remove values from the table if they are default on logout. However this will not work if you want to use subtables.
  Reply With Quote
11-12-08, 10:15 AM   #18
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Too my knowledge :
Code:
local PocketPlotDB = setmetatable(PocketPlotDB or {}, {__index = defaults})
is the same as
Code:
PocketPlotDB = PocketPlotDB or {}
local ppdb = {}
setmetatable(PocketPlotDB, ppdb)
ppdb.__index = defaults
The only difference is that in the first method PocketPlotDB is local, so I'm guessing your "PocketPlotDB is nil" error is due to variable scope. i.e.

Code:
function printvar()
   print(var)
end
local var = "cat"
printvar()
would print nil.

As for your other problem of the metatable not being applied to existing substables, you could do this:
Code:
local function DeepMetatable(tbl, defaulttbl) --I couldn't think of a better name
    for i, v in pairs(tbl) do
        if type(v) == "table" and defaulttbl[i] then
            DeepMetatable(tbl, defaulttbl[i])
        end
    end
    return setmetatable(tbl, {__index = defaulttbl})
end

local defaults = {
    color = {
        r = 1,
        g = 0.6,
        b = 0.6
    }
}

PocketPlotDB = {
    color = {}
}
PocketPlotDB = DeepMetatable(PocketPlotDB or {}, defaults)
print(PocketPlotDB.color.r)
Would output 1.

Whereas the other
Code:
PocketPlotDB = setmetatable(PocketPlotDB or {}, {__index = defaults})
print(PocketPlotDB.color.r)
would give you nil.

I think the reason why the previous method I posted didn't work because the __index metatable wasn't applied to pre-existing subtables, so if those pre-existing subtables were missing values there was no __index metatable to get the default value.

Hope this helps.
  Reply With Quote
11-12-08, 10:45 AM   #19
p3lim
A Pyroguard Emberseer
 
p3lim's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2007
Posts: 1,694
Thanks for the tips btw, gonna implement it for pMinimap now (sick of all those bugreports people toss at me because of nil variables)
  Reply With Quote
11-12-08, 11:03 AM   #20
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 10,670
Originally Posted by Slakah View Post
The only difference is that in the first method PocketPlotDB is local, so I'm guessing your "PocketPlotDB is nil" error is due to variable scope. i.e.
I listed PocketPlotDB as a local at the top of the file, so scope was not it. As I mentioned, it did work after adding
Code:
PocketPlotDB = PocketPlotDB or defaults
(and removed the "or defaults" from the metatable assignment) *except* for the color table.

Anyway, the inpairs solution works for me. Thanks.
__________________
"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

WoWInterface » Developer Discussions » General Authoring Discussion » Better Saved Variables Tutorial?

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