WoW:Saving variables between game sessions: Difference between revisions

m
Move page script moved page Saving variables between game sessions to WoW:Saving variables between game sessions without leaving a redirect
(Clarified load sequence.)
m (Move page script moved page Saving variables between game sessions to WoW:Saving variables between game sessions without leaving a redirect)
 
(11 intermediate revisions by 10 users not shown)
Line 1: Line 1:
== SavedVariables.lua ==
{{wow/uihowto}}
An Addon may need to save settings and data between game sessions - that is, some information may need to persist through a user log out. To enable this, the addons may specify a number of variables to be saved to disk when the player's character logs out of the game, and restored when the character logs back in. Variables that are saved and restored by the client are called SavedVariables.


With the exception of a few game-managed properties (slot contents, graphics settings, etc), all custom values which need to be saved between sessions are maintained on the player's computer in a set of .lua files, which are referred to as 'SavedVariables' files. There are three types of SavedVariables files:
'''Summary:''' to save a global variable FOOBAR, add <code>##SavedVariables: FOOBAR</code> or <code>##SavedVariablesPerCharacter: FOOBAR</code> to an addon's .toc file.


* <tt>WTF\Account\ACCOUNTNAME\SavedVariables.lua</tt> - "Global" saved settings
== Specifying which variables to save ==
* <tt>WTF\Account\ACCOUNTNAME\SavedVariables\AddOnName.lua</tt> - Per-account settings for each individual AddOn.
To tell the WoW client that you want a variable to persist through log out, you need to add it to your addon's .toc file. There are two directives you may add to your .toc file, both should be followed by a colon and a comma-delimited list of variable names in the global environment (for most addons, this means variables that haven't been defined using the '''local''' keyword) that the addon wants to persist.
* <tt>WTF\Account\ACCOUNTNAME\RealmName\CharacterName\AddOnName.lua</tt> - Per-character settings for each individual AddOn
; <code>##SavedVariables</code> : Variables listed after this directive are saved on a per-account basis: if any of the characters on that account logs in, those variables will be restored. This may be more useful for global addon settings, or addons that implement profiles one can freely switch between.
; <code>##SavedVariablesPerCharacter</code> : Variables listed after this directive are saved on a per-character basis: a separate copy of the variable is stored and restored for each character. This may be more useful for simple per-character options or history data.


These files are written whenever the UI engine shuts down, either when the game is quit, or when you force a UI reload (with <code>/console reloadUI</code>). Like the other .lua scripts for the UI, the SavedVariables scripts are executed when the UI is started and AddOns are loaded. This happens when you first log on, and each time you force a UI reload.
== The loading process ==
The variables saved by those directives are not immediately available when your addon loads; instead, they're loaded at a later point. The client fires events to let addons know that their saved variables were loaded.
# WoW FrameXML code is loaded and executed.
# Addon code is loaded and executed.
# Saved variables for one addon a time are loaded and executed, then [[Events/A#ADDON_LOADED|'''ADDON_LOADED''']] event is fired for that addon.
# '''VARIABLES_LOADED''' event is fired to let addons know that all saved variables were loaded.


The SavedVariables files are executed at different times, depending on their types, and it's important to understand the when they're executed, particularly relative to the <code>ADDON_LOADED</code> and <code>VARIABLES_LOADED</code> events. The sequence is as follows:
Addons should generally use ADDON_LOADED to determine when their saved variables are accessible; the first argument of the event is the name of the addon for which it is being fired; VARIABLES_LOADED should be avoided as it is will not be fired for load-on-demand addons.
* MyAddOn loads
* AddOnName.lua loads and executes
* ADDON_LOADED event fires for MyAddOn
* &hellip; other AddOns load &hellip;
* SavedVariables.lua loads and executes
* VARIABLES_LOADED event fires
* Player gets control


When the UI engine shuts down, only those variables which have been marked for saving will be written into the <tt>SavedVariables.lua</tt> files. A variable can be written to more than one file (though this is generally ill-advised since it can cause settings to be overwritten later on)
== Saving to disk ==
The client saves variables to disk automatically when you log out, disconnect, quit the game, or reload your user interface (<code>/run ReloadUI()</code>). If an addon needs to make last-minute updates before the variables are saved, the [[Events/P#PLAYER_LOGOUT|'''PLAYER_LOGOUT''']] event can be used: it notifies you that the character is about to log out, and lets you alter your saved variables before they are serialized.


== Designating variables to save ==
== Code Example ==
To illustrate the concepts, let's consider a simple addon, HaveWeMet, shown below. The greets your characters when you log on: if it's seen you log into that character before, it outputs "Hello again, <Character Name>", and it if has not, it outputs "Hi; what is your name?" to the chat frame. When its slash command, '''/hwm''', is used, it tells the player how many characters it has met before.


=== SavedVariables in AddOn's .toc ===
There are two pieces of information that need to persist between sessions: the number of characters the addon has met, and whether it has met any particular character. To save the count, a global variable, HaveWeMetCount is used (and saved on a per-account basis through #SavedVariables); while HaveWeMetBool is saved per-character and used to determine whether the addon has seen ''this'' character before.
 
<!-- This code in the middle of an article to illustrate a point; but it shouldn't fill everything. put it in a scrollable box -->
The best way is to use the SavedVariables or SavedVariablesPerCharacter keywords in your Addon's .toc file.  Thus, an addon with some saved variables would have a .toc like:
=== HaveWeMet\HaveWeMet.toc ===
 
<code>
  ## Interface: 1300
  ## Interface: 30000
  ## Title: Demo AddOn
  ## Title: Have We Met?
## Notes: This is a test to show how to save variables between game sessions
  ## SavedVariables: HaveWeMetCount
  ## SavedVariables: Demo_test, Demo_foo
  ## SavedVariablesPerCharacter: HaveWeMetBool
  ## SavedVariablesPerCharacter: Demo_perchar
  HaveWeMet.lua
  Demo.xml
</code>
 
=== HaveWeMet\HaveWeMet.lua ===
If you have to store lots of pieces data, or data with dynamic keys, wrap it in one or two table variables rather than consuming huge quantities of global namespace.
<code>
 
local frame = CreateFrame("FRAME"); -- Need a frame to respond to events
The <tt>## SavedVariables:</tt> heading is used for data you want to be available to your addon no matter which character is playing. <tt>## SavedVariablesPerCharacter:</tt> is used for data that you ONLY want to be available to a specific character. There is no way of accessing another character's per-character data, so if you need to access other character's data, then you should use a regular per-addon saved table, and use indexes within that for realm and character.
frame:RegisterEvent("ADDON_LOADED"); -- Fired when saved variables are loaded
 
frame:RegisterEvent("PLAYER_LOGOUT"); -- Fired when about to log out
<div style="border:solid 1px; padding:1em;background:#222;color:#ccc;"><div>
=== The RegisterForSave function (Do not use this) ===
function frame:OnEvent(event, arg1)
'''THIS FUNCTION IS NOW RESTRICTED'''
   if event == "ADDON_LOADED" and arg1 == "HaveWeMet" then
This function is now restricted to Blizzard-signed addons, and addons which depend on it will no longer work.
  -- Our saved variables are ready at this point. If there are none, both variables will set to nil.
 
  if HaveWeMetCount == nil then
'''IMPORTANT:''' This information is really for reference only, you can not access this functionality from outside Blizzard signed AddOns. Use SavedVariables instead.
    HaveWeMetCount = 0; -- This is the first time this addon is loaded; initialize the count to 0.
 
  end
LUA code can explicitly request that a variable be saved by calling the [[API RegisterForSave|RegisterForSave("varName")]] function with the variable name that should be saved. That variable then becomes flagged for saving at the end of the session.  It's important to note that since this is an active process, the variable will only be saved if it is registered during a session, so compilation execution failures that prevent the call from being made will end up without the variable being flagged.
  if HaveWeMetBool then
 
    print("Hello again, " .. UnitName("player") .. "!");
Variables flagged this way go into the global <tt>SavedVariables.lua</tt> file.
 
Before the addition of the SavedVariables section in the .toc this was the only way to flag variables for saving, however now use of this function is not recommended unless there's a specific reason for optional saving (and you understand all the risks).
 
'''IMPORTANT:''' "Dynamic variable names" is NEVER a reason to use this function, use a table instead with dynamic keys, i.e.:
 
   ADDON_SETTINGS = {}
  <nowiki>ADDON_SETTINGS["dynamic" .. something] = value</nowiki>
</div></div>
 
== Using previously saved variables ==
 
If your needs are simple, then you just have to mark your variable as saved in the <tt>.toc</tt> file, and initialize it with a reasonable default value at startup or OnLoad time. If there is a previously saved value, then it will simply overwrite the default one, otherwise the default value will remain. At the end of the play session whatever value it has ended up as will be saved.
 
If your addon derives configuration from the saved variables, you will likely want to be informed when they've been loaded so the addon can reconfigure itself. In this case create an event handler and register for the <b>"ADDON_LOADED"</b> event, when the arg1 global variable has the name of your addon. Once this is called you can re-check your configuration and set things appropriately.
 
If your addon is not 'OnDemand' loaded, then you can also just use the "VARIABLES_LOADED" event.
 
Finally, if you wish to do logic involving both the default and loaded value, you can use a technique as follows:
 
When the addon sets up the default value, you do:
 
  Demo_foo = { "bar" }
  local initDemo_foo = Demo_foo;
 
In your OnLoad handler you add:
 
  this:RegisterEvent("ADDON_LOADED");
 
Then, in your OnEvent handler, you have
 
if ((event == "ADDON_LOADED") and (arg1 == "Demo")) then
  if (initDemo_foo == Demo_foo) then
    -- No change - Must be new user
   else
   else
    -- Changed - Previous user
    HaveWeMetCount = HaveWeMetCount + 1; -- It's a new character.
    print("Hi; what is your name?");
   end
   end
  elseif event == "PLAYER_LOGOUT" then
    HaveWeMetBool = true; -- We've met; commit it to memory.
  end
  end
  end
 
frame:SetScript("OnEvent", frame.OnEvent);
== Things that can't be saved ==
SLASH_HAVEWEMET1 = "/hwm";
 
  function SlashCmdList.HAVEWEMET(msg)
Not all Lua constructs can be saved. For example, inline functions.
   print("HaveWeMet has met " .. HaveWeMetCount .. " characters.");
 
  function f()
   data = 1
  local function g()
    -- code block
  end
  return { data, g }
  end
  end
</code>
savedVar = f()  -- will fail to save g


== VARIABLES_LOADED event ==
== Common Pitfalls ==
There are a few common issues beginners may experience:
; Variables are saved and loaded in the ''global'' environment : 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
; Saved variables are loaded after the addon code is executed : They cannot be accessed immediately, and will overwrite any "defaults" the addon may place in the global environment during its loading process.
; Only some variable types may be saved : Strings, booleans, numbers and tables are the only variable types that will be saved (functions, userdata and coroutines will not). Circular references in tables may not be preserved.
; Saving tables : Tables are a ''great'' way to avoid having to use a large number of names in the global namespace. However, they may be more difficult to initialize to default values when your addon is updated and you add or remove a key. Multiple saved variables that reference the same table will each create a separate (but identical) instance of the table, and as such will no longer point to the same table when they are loaded again.


When working with variables designated for saving, you should not initialized them with table values without paying attention to VARIABLES_LOADED event. Because, as explained in loading sequence above, file with saved variables is processed after code of your addon and because entire table is single value, if you define table with default values like this:
== Storage ==
tableName = {
Saved variables are stored on a per-account basis in three file classes:
    ['key1'] = 'value1';
* <tt>WTF\Account\ACCOUNTNAME\SavedVariables.lua</tt> - Blizzard's saved variables.
}
* <tt>WTF\Account\ACCOUNTNAME\SavedVariables\AddOnName.lua</tt> - Per-account settings for each individual AddOn.
and later alter the table in source to this:
* <tt>WTF\Account\ACCOUNTNAME\RealmName\CharacterName\SavedVariables\AddOnName.lua</tt> - Per-character settings for each individual AddOn.
tableName = {
    ['key1'] = 'value1';
    ['key2'] = 'value2';
}
when your saved variables are loaded, saved value of table that have no 'key2' will overwrite table defined in source and, therefore, 'key2' won't have default value set.
 
Correct way to initialize default values is to register for VARIABLES_LOADED event, test keys that you want to initialized if they are nil or not and write your default values.
function AddOnName_OnLoad()
    this:RegisterEvent('VARIABLES_LOADED')
end
function AddOnName_OnEvent(event)
    if event == 'VARIABLES_LOADED' then
        if(not tableName.key1) then tableName.key1 = 'value1' end;
        if(not tableName.key2) then tableName.key1 = 'value2' end;
    end
end
 
Simple values, such as number or strings, still can be initialized without waiting for event if you do not require any additional checks or processing (because you'd be working on your hard-coded values, not on those read from saved file and all you work will be overwritten after that), but for sake of simple maintenance in case your initialization becomes more complex in future, it is advised to use registering for VARIABLES_LOADED event from beginning to save you rewrite or headache of catching elusive bugs later.


[[Category: HOWTOs|Save Variables Between Game Sessions]]
Deleting the WTF folder, or simply moving its contents will therefore reset the settings for all of your addons.
Anonymous user