Saving Local Variables Between Sessions

Gamepedia documents that

If you want to save a local value, you have to first read it from the global environment (_G table) on ADDON_LOADED, then return it into the global environment before the player logs out.

What would this look like? For example, my addon has several global variables and a table that I want to save between sessions. Would this work?

When the addon loads, the game executes this line:
local fooBar = _G.fooBar

Upon logout, reload, etc., the game executes this line
_G.fooBar = fooBar

And how would fooBar be specified as a parameter to the TOC’s ## SavedVariables command?

Hope this wasn’t too confusing.

The only mechanism for saving variable data between sessions are via the ## SavedVariables: and ## SavedVariablesPerCharacter: designations in the .toc.

The first saves entities to the global SavedVariables folder under WTF and the second saves into the individual realm/character SavedVariables folder under WTF.

## SavedVariables: MYADDON_SAVEDDVAR1, MYADDON_SAVEDDVAR2
You can have multiple saved variable spaces for each type if required.

The game doesn’t allocate the space(s), just creates a place holder in the global table for each one so the names must be globaly unique to your addon (Prefix with the addon name or acronym if you think that’s sufficient. Use all upper case and add _DATA or some such if you’re not sure or want to differentiate with local/addon level data ADDONNAME_DATA = {}, get creative :wink:).

The best place to initialise your saved variables is at the PLAYER_LOGIN event as it only fires once after all addons have loaded (including saved variables) and before PLAYER_ENTERING_WORLD.

if event == "PLAYER_LOGIN"  then
    if not MYADDON_SAVEDDVAR1 then -- first time the addon is used
        MYADDON_SAVEDDVAR1 = {} -- allocate a table to store your saved variables (the table will be saved to disk on logout)
        -- Now, load the table with defaults if needed.
        MYADDON_SAVEDDVAR1.foobar = "Some Value"
        MYADDON_SAVEDDVAR1.Frame1 = {
            width=100,
            height=150,
        }
    end
    -- Initialise the addon with the Saved Variables information (new or loaded from disk)
    local foobar = MYADDON_SAVEDDVAR1.foobar
    local Frame1 = CreateFrame("Frame", "MyAddon_Frame1", UIParent)
    Frame1:SetSize(MYADDON_SAVEDDVAR1.Frame1.width, MYADDON_SAVEDDVAR1.Frame1.height)
end

[Example code only]
The Saved Variables are only written out to disk when a session ends so you can’t change during play as changes will be overwritten. They are saved to a filename that uses the addon name with an extension of .lua (.lua.bak files will also be created)

In short, locals don’t get saved unless they are copied to your global SV space before logout like your examples show.

1 Like

I’m trying to save/restore the position of a frame across reloads, logouts, exits, etc. The position is stored in an array, framePosition.

framePosition = {f:GetPoint())}

In the .toc,

SavedVariablesPerCharacter: framePosition

In the ADDON_LOADED handler, the frame is set to its default, initial position.

    if not framePosition  then
        framePosition = { "CENTER", nil, "CENTER", 0, 0 }
    end

In the frame display code, the location is restored

f:SetPoint( framePosition[1],
framePosition[2],
framePosition[3],
framePosition[4],
framePosition[5] )

If I understand how this works, by the time the addon is loaded, but before the ADDON_LOADED event is fired, this code has been executed. But, I’m missing something because this doesn’t work so clearly I do not understand what exactly is going on.

However, I do have one question: where/how is framePosition to be declared? Since this is Lua, is it parsed as global within the if/then clause since it’s not declared as global?

Anyway, any help would be much appreciated.

You can’t store the frame reference of the relative frame that f is attached to so if possible, store it’s name and retrieve it from the global table next time:

local p,r,t,x,y = f:GetPoint(1)
if r:GetName() then
    r = r:GetName()
else
    r = "UIParent"
end
framePosition = {p, r, t, x, y)

and to restore:

f:SetPoint(framePosition[1], _G[framePosition[2]], framePosition[3], framePosition[4], framePosition[5])

If f is attached to a frame that belongs to your addon, you don’t need to store r

framePosition = {p, t, x, y)
f:SetPoint(framePosition[1], MyAddonFrame, framePosition[2], framePosition[3], framePosition[4])

If f is attached to a frame that doesn’t have a name and you can’t just attach it to UIParent then you need some way of “finding” the frame to attach too when you login.

The ADDON_LOADED event fires first when your addon loads and for every addon that loads after that (you don’t get the event for any addons that load before yours).

If the frame you’re saving/restoring belongs to another addon then maybe consider using the PLAYER_LOGIN event as that fires only once, after all addons identified to load at startup have finished loading and before you enter the world.

Variables declared in the toc as ## SavedVariables or ## SavedVariablesPerCharacter are initially created in the global table with a nil value. When you logout/exit the value of the variables (including nil) will be written to the addons SavedVariables file(s) under the WTF folder (location being dependent on whether the variable is global or per character).

The game subsequently loads whatever has been saved in the various SV files as Globals so using unique variable names is important.

If you delete a SV file for your addon before the game starts, it will start again at nil.

1 Like

Thanks for the response. Much appreciated. Got it working now.