WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   General Authoring Discussion (https://www.wowinterface.com/forums/forumdisplay.php?f=20)
-   -   Better Saved Variables Tutorial? (https://www.wowinterface.com/forums/showthread.php?t=19104)

HyperGig 11-04-08 03:20 PM

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

Cralor 11-04-08 05:03 PM

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

Akryn 11-04-08 05:23 PM

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)

HyperGig 11-05-08 08:42 AM

Where do i put the
Code:

if (not my_var) then my_var = "default Val" end
part?

Slakah 11-05-08 09:32 AM

Quote:

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.

p3lim 11-05-08 09:55 AM

Quote:

Originally Posted by HyperGig (Post 108145)
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}

Seerah 11-05-08 10:22 AM

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.

Slakah 11-05-08 10:39 AM

Quote:

Originally Posted by Seerah (Post 108159)
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.

Seerah 11-05-08 10:51 AM

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

Slakah 11-05-08 10:57 AM

Quote:

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".

Seerah 11-05-08 10:57 AM

Okies - was wondering if it was a typo :p

slizen 11-05-08 12:17 PM

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.

HyperGig 11-05-08 12:24 PM

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.

conner686 11-06-08 01:43 AM

Quote:

Originally Posted by slizen (Post 108171)
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.

Seerah 11-09-08 11:27 PM

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... :confused: 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


lieandswell 11-12-08 08:14 AM

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.

dafire 11-12-08 08:34 AM

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.

Slakah 11-12-08 10:15 AM

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.

p3lim 11-12-08 10:45 AM

Thanks for the tips btw, gonna implement it for pMinimap now (sick of all those bugreports people toss at me because of nil variables)

Seerah 11-12-08 11:03 AM

Quote:

Originally Posted by Slakah (Post 109006)
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. :)


All times are GMT -6. The time now is 03:29 PM.

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