Thread Tools Display Modes
12-13-06, 02:57 PM   #1
Kiphere
A Deviate Faerie Dragon
Join Date: Dec 2006
Posts: 17
Heh, saved variables?

I've been looking at other addons and I read the WoWWiki HOWTO on saved variables and I really can't figure out what I'm supposed to do in my addon to save the layout and load it percharacter.

Right now its just buttons with hide, show, lock, move, and scale options. I'd like it to actualy save where everything is, whats shown etc. ANyone have a link to a better guide than the one on WoWWiki by chance or if it's simple enough to explain in a paragraph have the time to gimme a lil knowledge?
  Reply With Quote
12-13-06, 03:56 PM   #2
Flickerstreak
A Deviate Faerie Dragon
Join Date: Dec 2006
Posts: 19
SavedVariables in a nutshell:


When you log in, a sequence of things happen:

(1) AddOn code is parsed and executed for each addon
(2) "ADDON_LOADED" event is sent to each addon
(3) Saved variables are loaded for each addon (more on this later)
(4) "VARIABLES_LOADED" event is sent to each addon.

This means that, prior to the VARIABLES_LOADED event being received, any variables that you saved in a previous session haven't been loaded yet.

When you log out:

(1) saved variables are written to disk.

When you reload the UI, it's effectively the same as log out / log in.


There are two types of saved variables that an addon can use: account-wide settings and character-wide settings. In order for a variable to be saved, you must declare it at the top of your .toc file like so:

Code:
## SavedVariables: var1, var2, var3  (etc)
## SavedVariablesPerCharacter: cvar1, cvar2, (etc)
Any values that you set to your saved variables are automatically then written to disk when you log out, and loaded up when you log in. The file is a straight up Lua file, and it is stored at:

account-wide: WTF/Account/<ACCOUNTNAME>/SavedVariables/<YourAddonName>.lua
per-character: WTF/Account/<ACCOUNTNAME>/<RealmName>/<CharacterName>/SavedVariables/<YourAddonName>.lua

When your "variables are loaded", that file is simply executed. It has the effect of setting a global variable, the same as specified in your.toc file, to the stored value. Take a look at some of those files for your installed addons. If you have both an account-wide saved variable and a per-character saved variable, both files are executed.

So, what's the big deal? WoW correctly writes tables to the saved variables file. This means that your AddOn should really only have one saved variable, named something like MyAddOnVars, and it should be a table. You can then index that table however you like, and WoW will automatically save its contents from session to session.

At the startup of your addon (after the VARIABLES_LOADED event has fired), you should check for the presence of your saved variable. If it doesn't exist yet, create it! and automagically its value will be saved across sessions. You can then put any manner of data in your saved variables, in any organization you want, and it will be persistent across sessions. Realistically speaking, the table can contain only numbers and strings, not functions or object references. But the nature of Lua tables being heterogeneous key-value maps means that you can organize a lot of data is an efficient manner, if you structure your saved variable table smartly.

There are a few libraries out there that help you to manage saved variables: my favorite is part of Ace2 (AceDB-2.0), found at http://www.wowace.com.
  Reply With Quote
12-13-06, 03:59 PM   #3
Saiiyna
An Aku'mai Servant
 
Saiiyna's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2006
Posts: 33
1) In your .toc file you need a line like

## SavedVariablesPerCharacter: bandager_CONFIG

2) in your code you have your variable i.e.

bandager_CONFIG =
{
enable = true;
location = { 10, 10};
}

where you replace bandager_CONFIG with your variable name

when you log out they will be saved in the SavedVariables folder under your account in the WTF directory.

this way saves per account not per character.

Hope this helps
  Reply With Quote
12-13-06, 04:04 PM   #4
Kiphere
A Deviate Faerie Dragon
Join Date: Dec 2006
Posts: 17
ugh, so given my application everytime i move a button, resize a button, show or hide a button it will have to write a variable?

Is there any global way to get the layout of all the positions and lock/hide/shown variables together?

I'm too new at this to jump into a library like Ace I think... I don't really knwo much at all yet.
  Reply With Quote
12-13-06, 04:47 PM   #5
Kiphere
A Deviate Faerie Dragon
Join Date: Dec 2006
Posts: 17
ok, I have a pretty simple set up. All my commands reference single buttons at the moment.

Could I set the saved variable to my slash commands action? (I assume I'll have to)

I imagine if I record my locations when I lock a button, when I hide a button or show a button I record its hide/show state, and when I scale a button I record its scale size...

I have no idea how to do that properly or if it'll even work how I'm thinking.

I have

Code:
SlashCmdList["ColonelBarCOMMAND"] = 
function (msg)	
	local argT = {};
	gsub(msg, "(%S+)", function(w) tinsert(argT, w) end)
	if (argT[1]) == "move" then
		if tonumber(argT[2]) > 0 then
			getglobal("EasyBarButton"..argT[2].."Cap"):Show();
		end
	else if (argT[1]) == "lock" then
		if tonumber(argT[2]) > 0 then
			getglobal("EasyBarButton"..argT[2].."Cap"):Hide();
		end
	end
	local argT = {};
	gsub(msg, "(%S+)", function(w) tinsert(argT, w) end)
	if (argT[1]) == "hide" then
		if tonumber(argT[2]) > 0 then
			getglobal("EasyBarButton"..argT[2]):Hide();
		end
	else if (argT[1]) == "display" then
		if tonumber(argT[2]) > 0 then
			getglobal("EasyBarButton"..argT[2]):Show();
		end
	end
	local argT = {};
	gsub(msg, "(%S+)", function(w) tinsert(argT, w) end)
	if (argT[1]) == "scale" then
		if tonumber(argT[2]) > 0 then
			getglobal("EasyBarButton"..argT[2]):SetScale(argT[3]);
		end
	end
end
end
end	

SLASH_ColonelBarCOMMAND1 = "/eb"
SLASH_ColonelBarCOMMAND2 = "/easybar"
I'm lookin at a simple mod called cleanbar, when it does its call "if (input == "left") then" it records it's variables right then. But it only has 1 input, all my commands have at least 2. The command and the button number.

Could I do something like have the commands as normal like above and put a save command in as well? like...

Code:
SlashCmdList["ColonelBarCOMMAND"] = 
function(input) 
    if (input) == "saveloc" then
        for i = 1, 120 do
              getglobal("EasyBarButton"..i):GetCenter()
        end
   if (input) == "savescale" then
        for i = 1, 120 do
              getglobal("EasyBarButton"..i):GetScale()
        end
end
That woudl get the coords of the center of all the frames and the scale of all the frames, I'm not sure if that would output anything at all since its doign it for a varible number of frames and the fact that i'm just mutilating an earlier attempt at a slash command.... Am I at least on the right track lol.
  Reply With Quote
12-14-06, 05:44 PM   #6
Flickerstreak
A Deviate Faerie Dragon
Join Date: Dec 2006
Posts: 19
Looking at your example code, you don't use any global variables at all - everything is done in the slash-command function locally. You're not storing the result of the commands anywhere, you're just executing them. For example, your second example does the following:

Code:
getglobal("EasyBarButton"..i):GetScale()
It gets the scale value of EasyBarButton, and then discards the value, without doing anything with it. I'm assuming you want to print it or something.

If you want the effects of your slash-commands to persist across logins, you must store the state somewhere, and then set it up when the user logs in. That's where the SavedVariable comes in - it becomes that 'somewhere'.

Here's a (very) basic example starting point. You need, at a bare minimum, 3 things to correctly save and restore state across logins:
  • A Frame object to register for the VARIABLES_LOADED event, and a handler which initializes the state to the saved value when the player logs in
  • A global variable which is declared to be saved in the .toc file, and a data structure to store your state across sessions
  • Write to that global variable whenever the state changes

So, this is the simplest modification I can make to your code to make it work. Shown are the .toc file and the .lua file (it's been abbreviated to only support the 'scale' command, you should be able to extend it from there)

EasyBar.toc:
Code:
## Interface: 20000
## Title: EasyBar
## SavedVariablesPerCharacter: EasyBarVars
EasyBar.lua
EasyBar.lua
Code:
-- The first section could be set up in an XML file, since you're just defining a frame. 
-- However, I'll do it here to show you how it can be done in Lua. 
-- As an added bonus, the frame is 'anonymous' - it doesn't have a global name, and 
-- it doesn't really need one.

-- In order to get the VARIABLES_LOADED event, you need a Frame with an OnEvent handler. 
-- We'll set the frame to be blank and unnamed, as a child of UIParent
local MyFrame = CreateFrame("Frame",nil,UIParent)

-- You have to anchor the frame somewhere and show it to receive events,
-- even though it's an empty frame
MyFrame:SetAllPoints()
MyFrame:Show()

-- Register for the VARIABLES_LOADED event
MyFrame:RegisterEvent(VARIABLES_LOADED)

function MyEventHandler( evt )
  -- the only event registered was VARIABLES_LOADED, if you want to register other events
  -- you'd need to do an if/else/elseif test here

  if EasyBarVars == nil then
    -- If this variable doesn't exist yet, then it's because it's the first time the user
    -- has used this AddOn. (or, they wiped their WTF folder and blew away their saved vars)
    -- Create it as a table with 1 field named 'scale' (which is also a table)
    EasyBarVars = { 
      scale = { },
    }
  else
    -- if it exists already, then that means that it was loaded as a SavedVariable.
    -- Wo, set up the saved state (scale settings)
    -- We index the EasyBarVars.scale table by the object name for convenience
    for objName, scale in pairs(EasyBarVars) do
      local obj = getglobal(objName)
      if obj then
        obj:SetScale(scale)
      end
    end

  end

end

-- this hooks the event handler to the frame
MyFrame:SetScript("OnEvent", MyEventHandler)

-- now here's your slash-command, abbreviated for just the scale setting
SlashCmdList["ColonelBarCOMMAND"] = 
function (msg)  
  local argT = {};
  gsub(msg, "(%S+)", function(w) tinsert(argT, w) end)
  if (argT[1]) == "scale" then
    if tonumber(argT[2]) > 0 then
      local btnName = "EasyBarButton"..argT[2]
      local scale = tonumber(argT[3])
      if scale ~= nil and scale > 0 then
        -- this is where you save the state so it can be loaded in the next session
        -- Remember, we indexed EasyBarVars.scale by the button name.
        EasyBarVars.scale[btnName] = scale
        getglobal(btnName):SetScale(scale);
      end
    end
  end
end


SLASH_ColonelBarCOMMAND1 = "/eb"
SLASH_ColonelBarCOMMAND2 = "/easybar"
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Heh, saved variables?


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