diff --git a/WeakAuras/AuraEnvironment.lua b/WeakAuras/AuraEnvironment.lua index ed29fdc..50abe3b 100644 --- a/WeakAuras/AuraEnvironment.lua +++ b/WeakAuras/AuraEnvironment.lua @@ -5,6 +5,9 @@ local WeakAuras = WeakAuras local L = WeakAuras.L local prettyPrint = WeakAuras.prettyPrint +local LibSerialize = LibStub("LibSerialize") +local LibDeflate = LibStub:GetLibrary("LibDeflate") + local UnitAura = UnitAura -- Unit Aura functions that return info about the first Aura matching the spellName or spellID given on the unit. local WA_GetUnitAura = function(unit, spell, filter) @@ -154,6 +157,7 @@ local blockedTables = { SendMailMoneyGold = true, MailFrameTab2 = true, ChatFrame1 = true, + --WeakAurasSaved = true, WeakAurasOptions = true, WeakAurasOptionsSaved = true } @@ -163,6 +167,7 @@ local aura_environments = {} -- 1 == config initialized -- 2 == fully initialized local environment_initialized = {} +local getDataCallCounts = {} function Private.IsEnvironmentInitialized(id) return environment_initialized[id] == 2 @@ -171,11 +176,13 @@ end function Private.DeleteAuraEnvironment(id) aura_environments[id] = nil environment_initialized[id] = nil + getDataCallCounts[id] = nil end function Private.RenameAuraEnvironment(oldid, newid) aura_environments[oldid], aura_environments[newid] = nil, aura_environments[oldid] environment_initialized[oldid], environment_initialized[newid] = nil, environment_initialized[oldid] + getDataCallCounts[oldid], getDataCallCounts[newid] = nil, getDataCallCounts[oldid] end local current_uid = nil @@ -183,8 +190,71 @@ local current_aura_env = nil -- Stack of of aura environments/uids, allows use of recursive aura activations through calls to WeakAuras.ScanEvents(). local aura_env_stack = {} +local function UpdateSavedDataWarning(uid, size) + local savedDataWarning = 16 * 1024 * 1024 -- 16 KB, but it's only a warning + if size > savedDataWarning then + Private.AuraWarnings.UpdateWarning(uid, "CustomSavedData", "warning", + L["This aura is saving %s KB of data"]:format(ceil(size / 1024))) + else + Private.AuraWarnings.UpdateWarning(uid, "CustomSavedData") + end +end + +function Private.SaveAuraEnvironment(id) + local data = WeakAuras.GetData(id) + if not data then + return + end + + local input = aura_environments[id] and aura_environments[id].saved + if input then + local serialized = LibSerialize:SerializeEx({errorOnUnserializableType = false}, input) + -- We use minimal compression, since that already achieves a reasonable compression ratio, + -- but takes significant less time + local compressed = LibDeflate:CompressDeflate(serialized, {level = 1}) + local encoded = LibDeflate:EncodeForPrint(compressed) + UpdateSavedDataWarning(data.uid, #encoded) + data.information.saved = encoded + else + data.information.saved = nil + end +end + +function Private.RestoreAuraEnvironment(id) + local data = WeakAuras.GetData(id) + if not data then + return + end + + local input = data.information.saved + if input then + local decoded = LibDeflate:DecodeForPrint(input) + local decompressed = LibDeflate:DecompressDeflate(decoded) + local success, deserialized = LibSerialize:Deserialize(decompressed) + if success then + aura_environments[id].saved = deserialized + else + aura_environments[id].saved = nil + end + UpdateSavedDataWarning(data.uid, #input) + else + aura_environments[id].saved = nil + end +end + +function Private.ClearAuraEnvironmentSavedData(id) + if environment_initialized[id] then + aura_environments[id].saved = nil + end +end + function Private.ClearAuraEnvironment(id) - environment_initialized[id] = nil; + if environment_initialized[id] then + Private.SaveAuraEnvironment(id) + environment_initialized[id] = nil + aura_environments[id] = nil + getDataCallCounts[id] = nil + end end function Private.ActivateAuraEnvironmentForRegion(region, onlyConfig) @@ -219,6 +289,7 @@ function Private.ActivateAuraEnvironment(id, cloneId, state, states, onlyConfig) elseif onlyConfig then environment_initialized[id] = 1 aura_environments[id] = {} + getDataCallCounts[id] = 0 current_uid = data.uid current_aura_env = aura_environments[id] current_aura_env.id = id @@ -235,6 +306,7 @@ function Private.ActivateAuraEnvironment(id, cloneId, state, states, onlyConfig) -- Either this aura environment has not yet been initialized, or it was reset via an edit in WeakaurasOptions environment_initialized[id] = 2 aura_environments[id] = aura_environments[id] or {} + getDataCallCounts[id] = getDataCallCounts[id] or 0 current_uid = data.uid current_aura_env = aura_environments[id] current_aura_env.id = id @@ -242,6 +314,7 @@ function Private.ActivateAuraEnvironment(id, cloneId, state, states, onlyConfig) current_aura_env.state = state current_aura_env.states = states current_aura_env.region = region + Private.RestoreAuraEnvironment(id) -- push new environment onto the stack tinsert(aura_env_stack, {current_aura_env, data.uid}) @@ -363,7 +436,16 @@ local FakeWeakAurasMixin = { }, override = { me = UnitName("player"), - myGUID = UnitGUID("player") + myGUID = UnitGUID("player"), + GetData = function(id) + local currentId = Private.UIDtoID(current_uid) + getDataCallCounts[currentId] = getDataCallCounts[currentId] + 1 + if getDataCallCounts[currentId] > 99 then + Private.AuraWarnings.UpdateWarning(current_uid, "FakeWeakAurasGetData", "warning", + L["This aura calls GetData a lot, which is a slow function."]) + end + return CopyTable(WeakAuras.GetData(id)) + end }, blocked = blocked, setBlocked = function() diff --git a/WeakAuras/Prototypes.lua b/WeakAuras/Prototypes.lua index 27e7cb5..b05f89c 100644 --- a/WeakAuras/Prototypes.lua +++ b/WeakAuras/Prototypes.lua @@ -799,6 +799,19 @@ local function valuesForTalentFunction(trigger) end Private.load_prototype = { + -- Each entry + -- name: name of argument for load function/option for options/setting in saved data + -- Options data + -- display: name to be displayed in the options + -- type: type to be used for the options + -- width: width in the options + -- hidden: whether the option is shown in the options, defaults to false + -- Load Function Data + -- enable: whether the test should be tested or not, defaults to true + -- test: overrides the default test + -- init: whether the argument should be a function parameter or not. "arg" for yes. Defaults to no argument + -- events: the events on which the test must be reevaluated + -- optional: whether the test is relevant for the options classification between loaded and unloaded, defaults to false args = { { name ="generalTitle", diff --git a/WeakAuras/Types.lua b/WeakAuras/Types.lua index 083f9a5..6a26e9b 100644 --- a/WeakAuras/Types.lua +++ b/WeakAuras/Types.lua @@ -2466,6 +2466,9 @@ Private.non_transmissable_fields = { skipWagoUpdate = true, ignoreWagoUpdate = true, preferToUpdate = true, + information = { + saved = true + } } -- For nested groups, we do transmit parent + controlledChildren @@ -2474,6 +2477,9 @@ Private.non_transmissable_fields_v2000 = { skipWagoUpdate = true, ignoreWagoUpdate = true, preferToUpdate = true, + information = { + saved = true + } } WeakAuras.data_stub = { diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index 6a59b76..7a34bfe 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1146,6 +1146,7 @@ local loadedFrame = CreateFrame("Frame"); WeakAuras.frames["Addon Initialization Handler"] = loadedFrame; loadedFrame:RegisterEvent("ADDON_LOADED"); loadedFrame:RegisterEvent("PLAYER_LOGIN"); +loadedFrame:RegisterEvent("PLAYER_LOGOUT"); loadedFrame:RegisterEvent("PLAYER_ENTERING_WORLD"); loadedFrame:SetScript("OnEvent", function(self, event, addon) if(event == "ADDON_LOADED") then @@ -1200,6 +1201,10 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) -- db isn't valid. Request permission to run repair tool before logging in StaticPopup_Show("WEAKAURAS_CONFIRM_REPAIR", nil, nil, {reason = "downgrade"}) end + elseif event == "PLAYER_LOGOUT" then + for id in pairs(db.displays) do + Private.SaveAuraEnvironment(id) + end else local callback if(event == "PLAYER_ENTERING_WORLD") then diff --git a/WeakAurasOptions/AnimationOptions.lua b/WeakAurasOptions/AnimationOptions.lua index e237efe..1fe0a05 100644 --- a/WeakAurasOptions/AnimationOptions.lua +++ b/WeakAurasOptions/AnimationOptions.lua @@ -385,10 +385,10 @@ function OptionsPrivate.GetAnimationOptions(data) return (data.animation.start.type ~= "custom" or not OptionsPrivate.Private.EnsureRegion(id).Color) end, get = function() - return data.animation.start.colorR, - data.animation.start.colorG, - data.animation.start.colorB, - data.animation.start.colorA; + return data.animation.start.colorR or 1, + data.animation.start.colorG or 1, + data.animation.start.colorB or 1, + data.animation.start.colorA or 1; end, set = function(info, r, g, b, a) data.animation.start.colorR = r; @@ -661,10 +661,10 @@ function OptionsPrivate.GetAnimationOptions(data) return (data.animation.main.type ~= "custom" or not OptionsPrivate.Private.EnsureRegion(id).Color) end, get = function() - return data.animation.main.colorR, - data.animation.main.colorG, - data.animation.main.colorB, - data.animation.main.colorA; + return data.animation.main.colorR or 1, + data.animation.main.colorG or 1, + data.animation.main.colorB or 1, + data.animation.main.colorA or 1; end, set = function(info, r, g, b, a) data.animation.main.colorR = r; @@ -910,10 +910,10 @@ function OptionsPrivate.GetAnimationOptions(data) return (data.animation.finish.type ~= "custom" or not OptionsPrivate.Private.EnsureRegion(id).Color) end, get = function() - return data.animation.finish.colorR, - data.animation.finish.colorG, - data.animation.finish.colorB, - data.animation.finish.colorA; + return data.animation.finish.colorR or 1, + data.animation.finish.colorG or 1, + data.animation.finish.colorB or 1, + data.animation.finish.colorA or 1; end, set = function(info, r, g, b, a) data.animation.finish.colorR = r; diff --git a/WeakAurasOptions/CommonOptions.lua b/WeakAurasOptions/CommonOptions.lua index 2c9b6ed..28d24b8 100644 --- a/WeakAurasOptions/CommonOptions.lua +++ b/WeakAurasOptions/CommonOptions.lua @@ -885,15 +885,18 @@ local getHelper = { local function CreateGetAll(subOption) return function(data, info, ...) local isToggle = nil + local isColor = nil local allChildren = CopyTable(getHelper) local enabledChildren = CopyTable(getHelper) for child in OptionsPrivate.Private.TraverseLeafs(data) do - if isToggle == nil then + if isToggle == nil or isColor == nil then local childOptions = getChildOption(OptionsPrivate.EnsureOptions(child, subOption), info) isToggle = childOptions and childOptions.type == "toggle" + isColor = childOptions and childOptions.type == "color" end + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) local childOption = childOptions; local childOptionTable = {[0] = childOption}; @@ -915,6 +918,9 @@ local function CreateGetAll(subOption) end if not allChildren:GetSame() and not enabledChildren:GetSame() then + if isColor then + return 0, 0, 0, 1 + end return nil; end break; diff --git a/WeakAurasOptions/InformationOptions.lua b/WeakAurasOptions/InformationOptions.lua index 1de2680..15be76a 100644 --- a/WeakAurasOptions/InformationOptions.lua +++ b/WeakAurasOptions/InformationOptions.lua @@ -245,6 +245,52 @@ function OptionsPrivate.GetInformationOptions(data) end end + -- Saved Data + local savedDataCount = 0 + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + OptionsPrivate.Private.SaveAuraEnvironment(data.id) + if child.information.saved then + savedDataCount = savedDataCount + 1 + end + end + if savedDataCount > 0 then + args.savedDataTitle = { + type = "header", + name = L["Saved Data"], + width = WeakAuras.doubleWidth, + order = order, + } + order = order + 1 + + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + if child.information.saved then + args["savedData." .. child.uid] = { + type = "description", + name = L["%s stores around %s KB of data"]:format(child.id, ceil((#child.information.saved) / 1024)), + width = savedDataCount > 1 and WeakAuras.doubleWidth or WeakAuras.normalWidth, + order = order, + } + order = order + 1 + end + end + + args.savedDataClear = { + type = "execute", + name = L["Clear Saved Data"], + width = savedDataCount > 1 and WeakAuras.doubleWidth or WeakAuras.normalWidth, + order = order, + func = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + OptionsPrivate.Private.ClearAuraEnvironmentSavedData(child.id) + WeakAuras.Add(child) + OptionsPrivate.ClearOptions(child.id) + end + WeakAuras.ClearAndUpdateOptions(data.id) + end + } + order = order + 1 + end + -- Debug Log args.debugLogTitle = { type = "header",