WoW:Saving variables between game sessions: Difference between revisions

From AddOn Studio
Jump to navigation Jump to search
m (Move page script moved page Saving variables between game sessions to WoW:Saving variables between game sessions without leaving a redirect)
 
(29 intermediate revisions by 25 users not shown)
Line 1: Line 1:
(Note - this is just a quick first version, hopefully someone can make it nicer. [[User:Flickering|Flickering]] 13:19, 4 Jan 2005 (EST))
{{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.


== SavedVariables.lua ==
'''Summary:''' to save a global variable FOOBAR, add <code>##SavedVariables: FOOBAR</code> or <code>##SavedVariablesPerCharacter: FOOBAR</code> to an addon's .toc file.


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 in a single file, <tt>SavedVariables.lua</tt>, which is written whenever the UI engine shuts down (either at game quit, or at the start of a forced UI reload), and then executed when the UI is started (after logging in or at the end of a forced UI reload).
== Specifying which variables to save ==
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.
; <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.


Everything that's in that file is always loaded at start up, but only selected global variables are written at the end. There are a couple of ways to indicate that a variable should be saved:
== 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.


=== SavedVariables in AddOn's .toc ===
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.


The best way is to use the SavedVariables keyword in your Addon's .toc file. Thus, an addon with some saved variables would have a .toc like:
== 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.


## Interface: 4114
== Code Example ==
## Title: Demo AddOn
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.
## Notes: This is a test to show how to save variables between game sessions
## SavedVariables: Demo_test, Demo_foo
Demo.xml


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.
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 -->
=== HaveWeMet\HaveWeMet.toc ===
<code>
## Interface: 30000
## Title: Have We Met?
## SavedVariables: HaveWeMetCount
## SavedVariablesPerCharacter: HaveWeMetBool
HaveWeMet.lua
</code>
=== HaveWeMet\HaveWeMet.lua ===
<code>
local frame = CreateFrame("FRAME"); -- Need a frame to respond to events
frame:RegisterEvent("ADDON_LOADED"); -- Fired when saved variables are loaded
frame:RegisterEvent("PLAYER_LOGOUT"); -- Fired when about to log out
function frame:OnEvent(event, arg1)
  if event == "ADDON_LOADED" and arg1 == "HaveWeMet" then
  -- Our saved variables are ready at this point. If there are none, both variables will set to nil.
  if HaveWeMetCount == nil then
    HaveWeMetCount = 0; -- This is the first time this addon is loaded; initialize the count to 0.
  end
  if HaveWeMetBool then
    print("Hello again, " .. UnitName("player") .. "!");
  else
    HaveWeMetCount = HaveWeMetCount + 1; -- It's a new character.
    print("Hi; what is your name?");
  end
  elseif event == "PLAYER_LOGOUT" then
    HaveWeMetBool = true; -- We've met; commit it to memory.
  end
end
frame:SetScript("OnEvent", frame.OnEvent);
SLASH_HAVEWEMET1 = "/hwm";
function SlashCmdList.HAVEWEMET(msg)
  print("HaveWeMet has met " .. HaveWeMetCount .. " characters.");
end
</code>


This method is best because it works even if your module is not active (based on the ui addons menu), has a version mismatch, or fails during startup due to compilation, dependency, or other issues.
== 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.


=== The RegisterForSave function ===
== Storage ==
Saved variables are stored on a per-account basis in three file classes:
* <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.
* <tt>WTF\Account\ACCOUNTNAME\RealmName\CharacterName\SavedVariables\AddOnName.lua</tt> - Per-character settings for each individual AddOn.


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.
Deleting the WTF folder, or simply moving its contents will therefore reset the settings for all of your addons.
 
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>
 
== Settings are per-ACCOUNT ==
 
It's important to note that settings are saved per-account, not per-character. If you wish to do per-character settings in your AddOn then you should use something like the player name as a key into a table for settings.  This does give the AddOn author some flexibility in being able to make some settings global, some per-class, some per-user, etc.
 
[[Category:Interface Customization HOWTOs|Save Variables Between Game Sessions]]

Latest revision as of 04:48, 15 August 2023

HOWTOs

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.

Summary: to save a global variable FOOBAR, add ##SavedVariables: FOOBAR or ##SavedVariablesPerCharacter: FOOBAR to an addon's .toc file.

Specifying which variables to save

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.

##SavedVariables
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.
##SavedVariablesPerCharacter
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.

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.

  1. WoW FrameXML code is loaded and executed.
  2. Addon code is loaded and executed.
  3. Saved variables for one addon a time are loaded and executed, then ADDON_LOADED event is fired for that addon.
  4. VARIABLES_LOADED event is fired to let addons know that all saved variables were loaded.

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.

Saving to disk

The client saves variables to disk automatically when you log out, disconnect, quit the game, or reload your user interface (/run ReloadUI()). If an addon needs to make last-minute updates before the variables are saved, the 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.

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.

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.

HaveWeMet\HaveWeMet.toc

## Interface: 30000
## Title: Have We Met?
## SavedVariables: HaveWeMetCount
## SavedVariablesPerCharacter: HaveWeMetBool
HaveWeMet.lua

HaveWeMet\HaveWeMet.lua

local frame = CreateFrame("FRAME"); -- Need a frame to respond to events
frame:RegisterEvent("ADDON_LOADED"); -- Fired when saved variables are loaded
frame:RegisterEvent("PLAYER_LOGOUT"); -- Fired when about to log out

function frame:OnEvent(event, arg1)
 if event == "ADDON_LOADED" and arg1 == "HaveWeMet" then
  -- Our saved variables are ready at this point. If there are none, both variables will set to nil.
  if HaveWeMetCount == nil then
   HaveWeMetCount = 0; -- This is the first time this addon is loaded; initialize the count to 0.
  end
  if HaveWeMetBool then
   print("Hello again, " .. UnitName("player") .. "!");
  else
   HaveWeMetCount = HaveWeMetCount + 1; -- It's a new character.
   print("Hi; what is your name?");
  end
 elseif event == "PLAYER_LOGOUT" then
   HaveWeMetBool = true; -- We've met; commit it to memory.
 end
end
frame:SetScript("OnEvent", frame.OnEvent);
SLASH_HAVEWEMET1 = "/hwm";
function SlashCmdList.HAVEWEMET(msg)
 print("HaveWeMet has met " .. HaveWeMetCount .. " characters.");
end

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.

Storage

Saved variables are stored on a per-account basis in three file classes:

  • WTF\Account\ACCOUNTNAME\SavedVariables.lua - Blizzard's saved variables.
  • WTF\Account\ACCOUNTNAME\SavedVariables\AddOnName.lua - Per-account settings for each individual AddOn.
  • WTF\Account\ACCOUNTNAME\RealmName\CharacterName\SavedVariables\AddOnName.lua - Per-character settings for each individual AddOn.

Deleting the WTF folder, or simply moving its contents will therefore reset the settings for all of your addons.