Back to Home

ESO Lua File v101044

libraries/utility/zo_savedvars.lua

[◄ back to folders ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
ZO_SavedVars = {}
local WILD_CARD_KEY = '*'
ZO_SAVED_VARS_CHARACTER_NAME_KEY = 1
ZO_SAVED_VARS_CHARACTER_ID_KEY = 2
local currentPlayerName
local currentDisplayName
--[[ Creates an interface around raw saved variable access
Usage:
local sv = ZO_SavedVars:New(savedVariableTable, version, [, namespace], defaults [, profile])
*savedVariableTable - The string name of the saved variable table
*version - The current version. If the saved data is a lower version it is destroyed and replaced with the defaults
*namespace - An optional string namespace to separate other variables using the same table
*defaults - A table describing the default saved variables, see the example below
*profile - An optional string to describe the profile, or "Default"
The defaults table will be used when accessing a key that doesn't exist or hasn't been set yet. There is a special wild card key
that can be used to make all sibling keys inherit the defaults specified by the wild card. The wild card value can be either a value or a table.
Example:
local defaults = {
firstRun = true
containers = {
["*"] = { --these are defaults all containers inherit
width = 20,
height = 50,
}
}
}
Note: SavedVars must be created in the EVENT_ADD_ON_LOADED function event in order for the settings file to properly save.
local sv = ZO_SavedVars:New("ZO_Ingame_SavedVariables", 1, "ExampleNamespace", defaults)
if sv.firstRun then
--initialize for first run
local primaryContainerSettings = sv.container[1] --automatically generates a table based off the wild card key
local container = self:GetPrimaryContainer()
container:SetDimensions(primaryContainerSettings.width, primaryContainerSettings.height)
sv.firstRun = false
end
Note: ZO_SavedVars:NewAccountWide provides the same interface as ZO_SavedVars:New, but is used to save account-wide saved vars.
--]]
function ZO_SavedVars:New(savedVariableTable, version, namespace, defaults, profile, displayName, characterName, characterId, characterKeyType)
    displayName = displayName or GetDisplayName()
    characterName = characterName or GetUnitName("player")
    characterId = characterId or GetCurrentCharacterId()
    characterKeyType = characterKeyType or ZO_SAVED_VARS_CHARACTER_NAME_KEY
    return GetNewSavedVars(savedVariableTable, version, namespace, defaults, profile, displayName, characterName, characterId, characterKeyType)
end
function ZO_SavedVars:NewCharacterNameSettings(savedVariableTable, version, namespace, defaults, profile)
    return GetNewSavedVars(savedVariableTable, version, namespace, defaults, profile, GetDisplayName(), GetUnitName("player"), GetCurrentCharacterId(), ZO_SAVED_VARS_CHARACTER_NAME_KEY)
end
function ZO_SavedVars:NewCharacterIdSettings(savedVariableTable, version, namespace, defaults, profile)
    return GetNewSavedVars(savedVariableTable, version, namespace, defaults, profile, GetDisplayName(), GetUnitName("player"), GetCurrentCharacterId(), ZO_SAVED_VARS_CHARACTER_ID_KEY)
end
function ZO_SavedVars:NewAccountWide(savedVariableTable, version, namespace, defaults, profile, displayName)
    displayName = displayName or GetDisplayName()
    return GetNewSavedVars(savedVariableTable, version, namespace, defaults, profile, displayName)
end
local function SearchPath(t, ...)
    local current = t
    for i = 1, select("#", ...) do
        local key = select(i, ...)
        if key ~= nil then
            if not current[key] then
                return nil
            end
            current = current[key]
        end
    end
    return current
end
local function CreatePath(t, ...)
    local current = t
    local container
    local containerKey
    for i=1, select("#", ...) do
        local key = select(i, ...)
        if key ~= nil then
            if not current[key] then
                current[key] = {}
            end
            container = current
            containerKey = key
            current = current[key]
        end
    end
    return current, container, containerKey
end
local function SetPath(t, value, ...)
    if value ~= nil then
        CreatePath(t, ...)
    end
    local current = t
    local parent
    local lastKey
    for i = 1, select("#", ...) do
        local key = select(i, ...)
        if key ~= nil then
            lastKey = key
            parent = current
            if current == nil then
                return
            end
            current = current[key]
        end
    end
    if parent ~= nil then
        parent[lastKey] = value
    end
end
function GetNewSavedVars(savedVariableTable, version, namespace, defaults, profile, displayName, characterName, characterId, characterKeyType)
    if type(savedVariableTable) ~= "table" then
        if _G[savedVariableTable] == nil then
            _G[savedVariableTable] = {}
        end
        savedVariableTable = _G[savedVariableTable]
    end
    if type(savedVariableTable) ~= "table" then
        error("Can only apply saved variables to a table")
    end
    --namespace is an optional argument
    if defaults == nil and type(namespace) == "table" then
        profile = defaults
        defaults = namespace        
        namespace = nil
    end
    profile = profile or "Default"
    if type(profile) ~= "string" then
        error("Profile must be a string or nil")
    end
    local finalKey
    if characterName == nil then
        finalKey = "$AccountWide"
    else
        --Look for a table matching the opposite key type and if there is, then copy it over. This allows us to preserve the old
        --character name based settings mainly.
        local characterKey = characterKeyType == ZO_SAVED_VARS_CHARACTER_NAME_KEY and characterName or characterId
        local oppositeCharacterKey = characterKeyType == ZO_SAVED_VARS_CHARACTER_NAME_KEY and characterId or characterName
        local oppositeCharacterKeyTable = SearchPath(savedVariableTable, profile, displayName, oppositeCharacterKey, namespace)
        if oppositeCharacterKeyTable then
            SetPath(savedVariableTable, oppositeCharacterKeyTable, profile, displayName, characterKey, namespace)
            SetPath(savedVariableTable, nil, profile, displayName, oppositeCharacterKey, namespace)
        end
        --If an old style name based key is still being used then try to upgrade that based on a name change. Less robust.
        if characterKeyType == ZO_SAVED_VARS_CHARACTER_NAME_KEY and NAME_CHANGE:DidNameChange() then
            local oldCharacterName = NAME_CHANGE:GetOldCharacterName()
            local oldNameTable = SearchPath(savedVariableTable, profile, displayName, oldCharacterName, namespace)
            if oldNameTable then
                SetPath(savedVariableTable, oldNameTable, profile, displayName, characterName, namespace)
                SetPath(savedVariableTable, nil, profile, displayName, oldCharacterName, namespace)
            end
        end
        finalKey = characterKey
    end    
    local finalSavedVar = CreateExposedInterface(savedVariableTable, version, namespace, defaults, profile, displayName, finalKey)
    
    if characterName and characterKeyType == ZO_SAVED_VARS_CHARACTER_ID_KEY then
        savedVariableTable[profile][displayName][finalKey]["$LastCharacterName"] = characterName
    end
    return finalSavedVar
end
local CopyDefaults
local function InitializeWildCardFromDefaults(sv, defaults)
    --Make sure that all existing tables have the appropriate defaults
    --It's possible for a table to exist in the sv table (that was created in response to a wild card table), but only certain values have been set
    --It needs to inherit the rest of the values from the default table
    for savedVarKey, savedVarValue in pairs(sv) do
        if type(savedVarValue) == "table" then
            if not rawget(defaults, savedVarValue) then
                CopyDefaults(savedVarValue, defaults)
            end
        end
    end
    setmetatable(sv, {
        --Indexing this will copy the defaults, assign it to the previously missing key and return it
        --It's almost like it was always there!
        __index = function(t, k)
            if k ~= nil then
                local newValue = CopyDefaults({}, defaults)
                rawset(t, k, newValue)
                return newValue
            end
        end,
    })
end
local function CopyPotentialTable(sv, key, defaults)
    if not rawget(sv, key) then 
        --SVs have nothing, copy and create a new entry
        rawset(sv, key, CopyDefaults({}, defaults))
    elseif type(sv[key]) == "table" then
        --SV has an entry, and it's a table, set it up for defaults
        CopyDefaults(sv[key], defaults)
    end
    --The SV isn't a table, nothing to do
end
function CopyDefaults(sv, defaults)
    for defaultKey, defaultValue in pairs(defaults) do
        if defaultKey == WILD_CARD_KEY then
            if type(defaultValue) == "table" then
                --Wild card value is a subtable, initialize the subtable
                InitializeWildCardFromDefaults(sv, defaultValue)
            else
                --Wild card value is (probably) a primitive, just return a copy when the wild card is indexed
                setmetatable(sv, { __index = function(t, k)
                    if k ~= nil then
                        return defaultValue
                    end
                end,})
            end
        elseif type(defaultValue) == "table" then
            CopyPotentialTable(sv, defaultKey, defaultValue)
        elseif rawget(sv, defaultKey) == nil then
            rawset(sv, defaultKey, defaultValue)
        end
    end
    return sv
end
local function InitializeRawTable(rawSavedTable, profile, namespace, displayName, playerName)
    return CreatePath(rawSavedTable, profile, displayName, playerName, namespace)
end
local function ExposeMethods(interface, namespace, rawSavedTable, defaults, profile, cachedInterfaces)
    --Gets an interface to the same saved variable table, but for a different character and/or world
    interface.GetInterfaceForCharacter = function(self, displayName, playerName)
        if currentDisplayName == displayName and currentPlayerName == playerName then
            return self
        end
        if not cachedInterfaces[displayName] then
            cachedInterfaces[displayName] = {}
        end
        if not cachedInterfaces[displayName][playerName] then
            cachedInterfaces[displayName][playerName] = CreateExposedInterface(rawSavedTable, self.version, namespace, defaults, profile, displayName, playerName, cachedInterfaces)
        end
        return cachedInterfaces[displayName][playerName]
    end
    interface.ResetToDefaults = function(self)
        local sv = getmetatable(self).__index
        if sv then
            local version = sv.version
            ZO_ClearTable(sv)
            sv.version = version
            if self.default then
                CopyDefaults(sv, self.default)
            end
        end
    end
end
function CreateExposedInterface(rawSavedTable, version, namespace, defaults, profile, displayName, playerName, cachedInterfaces)
    local current, container, containerKey = InitializeRawTable(rawSavedTable, profile, namespace, displayName, playerName)
    --if the data is unversioned or out of date, nuke the data first
    if current.version == nil or current.version < version then
        ZO_ClearTable(current)
    end
    current.version = version
    if defaults then
        CopyDefaults(current, defaults)
    end
    local interfaceMT = { 
        __index = current,
        __newindex = function(t, k, v)
            current[k] = v
        end,
    }
    local interface = {
        default = defaults,
    }
    cachedInterfaces = cachedInterfaces or {}
    ExposeMethods(interface, namespace, rawSavedTable, defaults, profile, cachedInterfaces)
    return setmetatable(interface, interfaceMT)
end