andrewtknoll@33: --- **LibModuleDBShare-1.0** provides a shared profile manager for addons without a central core. andrewtknoll@33: -- A basic options panel for the group is added to the Blizzard options panel, as well as a andrewtknoll@33: -- standard profile manager as a subpanel. Changes through the profiles panel are propagated andrewtknoll@33: -- to member databases. The root panel can be used as a parent for your module config panels, andrewtknoll@33: -- to keep all your addon's config in one place. The root panel's name is the same as the group's andrewtknoll@33: -- name.\\ andrewtknoll@33: -- \\ andrewtknoll@35: -- A group can be created using the ':NewGroup' library method. The returned object inherits all andrewtknoll@35: -- methods of the DBGroup object described below.\\ andrewtknoll@33: -- \\ andrewtknoll@33: -- **LibDualSpec Support**\\ andrewtknoll@33: -- LibModuleDBShare can use LibDualSpec to manage automatic profile switching with talent spec andrewtknoll@33: -- changes. This integration is handled by the library; there is no need to use LibDualSpec andrewtknoll@40: -- on member databases directly.\\ andrewtknoll@40: -- \\ andrewtknoll@40: -- **Slash Command Support**\\ andrewtknoll@40: -- LibModuleDBShare can associate a slash command with a DBGroup. The default handler function andrewtknoll@40: -- for the slash command opens the root options panel.\\ andrewtknoll@40: -- Additional handler functions can be registered to respond to specific arguments given to the andrewtknoll@44: -- slash command. If you provide a custom handler function for the slash command, that function andrewtknoll@44: -- is responsible for detecting secondary commands. >@5: -- >@5: -- @usage andrewtknoll@33: -- local database; andrewtknoll@33: -- -- this function is called after the ADDON_LOADED event fires andrewtknoll@33: -- function initializeDB() andrewtknoll@33: -- database = LibStub("AceDB-3.0"):New("MyAddonDB", defaults, true); andrewtknoll@33: -- local group = LibStub("LibModuleDBShare-1.0"):GetGroup("Group Name"); andrewtknoll@33: -- if not group then andrewtknoll@33: -- group = LibStub("LibModuleDBShare-1.0"):NewGroup("Group Name", "A description for this group.", database); andrewtknoll@33: -- else andrewtknoll@33: -- group:AddDB(database); andrewtknoll@33: -- end andrewtknoll@40: -- -- if you want to add a slash command andrewtknoll@40: -- if not group:HasSlashCommand() then andrewtknoll@40: -- group:EnableSlashCommand("COMMAND_NAME", "/groupname"); andrewtknoll@40: -- end andrewtknoll@33: -- end >@5: -- @class file andrewtknoll@33: -- @name LibModuleDBShare-1.0 andrewtknoll@39: local MAJOR, MINOR = "LibModuleDBShare-1.0", 5 >@3: local LibModuleDBShare, oldminor = LibStub:NewLibrary(MAJOR, MINOR) >@3: >@4: if not LibModuleDBShare then return end -- No upgrade needed >@4: andrewtknoll@29: -- Lua functions andrewtknoll@26: local error, type, pairs, time = error, type, pairs, time; andrewtknoll@15: andrewtknoll@17: -- Required Libraries @12: local AceDB = LibStub("AceDB-3.0"); andrewtknoll@17: local AceDBOptions = LibStub("AceDBOptions-3.0"); andrewtknoll@17: local AceConfigRegistry = LibStub("AceConfigRegistry-3.0"); andrewtknoll@17: local AceConfigDialog = LibStub("AceConfigDialog-3.0"); @12: andrewtknoll@28: -- Optional Libraries andrewtknoll@28: local LibDualSpec = LibStub("LibDualSpec-1.0", true); andrewtknoll@28: >@4: LibModuleDBShare.groups = LibModuleDBShare.groups or {}; >@4: >@5: local DBGroup = {}; >@5: >@5: --- Creates a new DB group. andrewtknoll@42: -- @paramsig groupName, groupDescription, initialDB[, usesDualSpec] andrewtknoll@40: -- @param groupName The name of the new DB group, as shown in the options panel. (string) andrewtknoll@40: -- @param groupDescription A description of the group to be shown in the root options panel. (string) andrewtknoll@40: -- @param initialDB The first DB to add to the group. (table) andrewtknoll@40: -- @param usesDualSpec True if this group should use LibDualSpec, false otherwise. (boolean or nil) andrewtknoll@44: -- @return the new DB group object (table) andrewtknoll@21: function LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec) andrewtknoll@35: -- check to see if LibDualSpec has been loaded andrewtknoll@35: if not LibDualSpec then andrewtknoll@35: LibDualSpec = LibStub("LibDualSpec-1.0", true); andrewtknoll@35: end andrewtknoll@21: -- verify parameters andrewtknoll@26: if type(groupName) ~= "string" then andrewtknoll@26: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'groupName' must be a string.", 2); andrewtknoll@26: elseif type(groupDescription) ~= "string" then andrewtknoll@26: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'groupDescription' must be a string.", 2); andrewtknoll@26: elseif type(LibModuleDBShare.groups[groupName]) ~= "nil" then andrewtknoll@31: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): group '"..groupName.."' already exists.", 2); andrewtknoll@27: elseif type(initialDB) ~= "table" or not AceDB.db_registry[initialDB] then andrewtknoll@31: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'initialDB' must be an AceDB-3.0 database.", 2); andrewtknoll@31: elseif initialDB.parent then andrewtknoll@31: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'initialDB' must not be a namespace.", 2) andrewtknoll@28: elseif type(usesDualSpec) ~= "boolean" and type(usesDualSpec) ~= "nil" then andrewtknoll@31: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'usesDualSpec' must be a boolean or nil.", 2); andrewtknoll@28: elseif usesDualSpec and not LibDualSpec then andrewtknoll@31: error("Usage: LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'usesDualSpec' cannot be true without LibDualSpec-1.0 installed.", 2); andrewtknoll@26: end andrewtknoll@21: -- create group @12: local group = {} @12: group.name = groupName; andrewtknoll@21: group.members = {}; andrewtknoll@21: -- create root option panel for group andrewtknoll@17: group.rootOptionsTable = { andrewtknoll@17: type = "group", andrewtknoll@17: name = groupName, andrewtknoll@17: args = { andrewtknoll@17: text = { andrewtknoll@17: type = "description", andrewtknoll@21: name = groupDescription, andrewtknoll@17: }, andrewtknoll@17: }, andrewtknoll@17: }; andrewtknoll@17: AceConfigRegistry:RegisterOptionsTable(groupName, group.rootOptionsTable); andrewtknoll@17: AceConfigDialog:AddToBlizOptions(groupName); andrewtknoll@21: -- create sync DB and profile options page @12: group.syncDBTable = {}; andrewtknoll@21: group.syncDB = AceDB:New(group.syncDBTable, nil, initialDB:GetCurrentProfile()); andrewtknoll@17: group.profileOptionsTable = AceDBOptions:GetOptionsTable(group.syncDB, false); andrewtknoll@28: if usesDualSpec then andrewtknoll@29: group.usesDualSpec = true; andrewtknoll@28: LibDualSpec:EnhanceDatabase(group.syncDB, groupName); andrewtknoll@28: LibDualSpec:EnhanceOptions(group.profileOptionsTable, group.syncDB); andrewtknoll@29: else andrewtknoll@29: group.usesDualSpec = false; andrewtknoll@28: end andrewtknoll@17: AceConfigRegistry:RegisterOptionsTable(groupName.."Profiles", group.profileOptionsTable); andrewtknoll@18: AceConfigDialog:AddToBlizOptions(groupName.."Profiles", group.profileOptionsTable.name, groupName); andrewtknoll@21: -- add all profiles from initialDB to syncDB andrewtknoll@21: for i, profile in pairs(initialDB:GetProfiles()) do andrewtknoll@21: group.syncDB:SetProfile(profile); andrewtknoll@21: end andrewtknoll@28: -- load profile info from initialDB andrewtknoll@21: group.syncDB:SetProfile(initialDB:GetCurrentProfile()); andrewtknoll@27: group.members[initialDB] = initialDB:GetNamespace(MAJOR, true) or initialDB:RegisterNamespace(MAJOR); andrewtknoll@29: local storedData = group.members[initialDB].char; andrewtknoll@29: if type(storedData.logoutTimestamp) == "number" then andrewtknoll@29: group.profileTimestamp = storedData.logoutTimestamp; andrewtknoll@22: else andrewtknoll@22: group.profileTimestamp = 0; andrewtknoll@22: end andrewtknoll@35: if usesDualSpec then andrewtknoll@35: local LDSnamespace = group.syncDB:GetNamespace("LibDualSpec-1.0"); andrewtknoll@35: LDSnamespace.char.enabled = storedData.dualSpecEnabled; andrewtknoll@35: LDSnamespace.char.profile = storedData.altProfile; andrewtknoll@35: LDSnamespace.char.specGroup = storedData.activeSpecGroup; andrewtknoll@30: group.syncDB:CheckDualSpecState(); andrewtknoll@35: else andrewtknoll@35: group.syncDB.char.enabled = storedData.dualSpecEnabled; andrewtknoll@35: group.syncDB.char.profile = storedData.altProfile; andrewtknoll@35: group.syncDB.char.specGroup = storedData.activeSpecGroup; andrewtknoll@28: end andrewtknoll@21: -- add methods and callbacks @12: for k, v in pairs(DBGroup) do @12: group[k] = v; @12: end andrewtknoll@19: group.syncDB.RegisterCallback(group, "OnProfileChanged", "OnProfileChanged"); andrewtknoll@19: group.syncDB.RegisterCallback(group, "OnProfileDeleted", "OnProfileDeleted"); andrewtknoll@19: group.syncDB.RegisterCallback(group, "OnProfileCopied", "OnProfileCopied"); andrewtknoll@19: group.syncDB.RegisterCallback(group, "OnProfileReset", "OnProfileReset"); andrewtknoll@28: group.syncDB.RegisterCallback(group, "OnDatabaseShutdown", "OnSyncShutdown"); andrewtknoll@41: group.members[initialDB].RegisterCallback(group, "OnDatabaseShutdown", "OnMemberShutdown"); -- register the namespace, not the base db andrewtknoll@20: group.squelchCallbacks = false; andrewtknoll@19: LibModuleDBShare.groups[groupName] = group; @12: return group; >@4: end >@4: >@5: --- Retrieves an existing DB group. andrewtknoll@40: -- @param groupName The name of the DB group to retrieve. (string) andrewtknoll@44: -- @return the DB group object, or ##nil## if not found (table) >@5: function LibModuleDBShare:GetGroup(groupName) andrewtknoll@27: if type(groupName) ~= "string" then andrewtknoll@27: error("Usage: LibModuleDBShare:GetGroup(groupName): 'groupName' must be a string.", 2); andrewtknoll@27: end @12: return LibModuleDBShare.groups[groupName]; >@4: end >@5: >@5: --- Adds a database to the group. andrewtknoll@40: -- @param newDB The database to add. (table) andrewtknoll@22: function DBGroup:AddDB(newDB) andrewtknoll@22: -- verify parameters andrewtknoll@26: if type(newDB) ~= "table" or not AceDB.db_registry[newDB] then andrewtknoll@31: error("Usage: DBGroup:AddDB(newDB): 'newDB' must be an AceDB-3.0 database.", 2); andrewtknoll@31: elseif newDB.parent then andrewtknoll@31: error("Usage: DBGroup:AddDB(newDB): 'newDB' must not be a namespace.", 2) andrewtknoll@26: elseif type(self.members[newDB]) ~= "nil" then andrewtknoll@31: error("Usage: DBGroup:AddDB(newDB): 'newDB' is already a member of DBGroup.", 2); andrewtknoll@31: end andrewtknoll@31: for groupName, group in pairs(LibModuleDBShare.groups) do andrewtknoll@31: if group.members[newDB] ~= nil then andrewtknoll@31: error("Usage: DBGroup:AddDB(newDB): 'newDB' is already a member of group '"..groupName.."'.", 2); andrewtknoll@31: end andrewtknoll@26: end andrewtknoll@22: -- record current profile andrewtknoll@20: local syncProfile = self.syncDB:GetCurrentProfile(); andrewtknoll@22: -- add new profiles to syncDB andrewtknoll@20: self.squelchCallbacks = true; andrewtknoll@22: for i, profile in pairs(newDB:GetProfiles()) do andrewtknoll@20: self.syncDB:SetProfile(profile); andrewtknoll@20: end andrewtknoll@22: -- set current profile based on timestamps andrewtknoll@27: local namespace = newDB:GetNamespace(MAJOR, true) or newDB:RegisterNamespace(MAJOR); andrewtknoll@29: local storedData = namespace.char; andrewtknoll@29: if type(storedData.logoutTimestamp) == "number" and storedData.logoutTimestamp > self.profileTimestamp then andrewtknoll@22: self.squelchCallbacks = false; andrewtknoll@22: self.syncDB:SetProfile(newDB:GetCurrentProfile()); andrewtknoll@29: self.profileTimestamp = storedData.logoutTimestamp; andrewtknoll@35: if self.usesDualSpec and storedData.altProfile then andrewtknoll@35: local LDSnamespace = group.syncDB:GetNamespace("LibDualSpec-1.0"); andrewtknoll@35: LDSnamespace.char.enabled = storedData.dualSpecEnabled; andrewtknoll@35: LDSnamespace.char.profile = storedData.altProfile; andrewtknoll@35: LDSnamespace.char.specGroup = storedData.activeSpecGroup; andrewtknoll@30: group.syncDB:CheckDualSpecState(); andrewtknoll@35: elseif storedData.altProfile then andrewtknoll@35: self.syncDB.char.enabled = storedData.dualSpecEnabled; andrewtknoll@35: self.syncDB.char.profile = storedData.altProfile; andrewtknoll@35: self.syncDB.char.specGroup = storedData.activeSpecGroup; andrewtknoll@28: end andrewtknoll@20: else andrewtknoll@20: self.syncDB:SetProfile(syncProfile); andrewtknoll@22: newDB:SetProfile(syncProfile); andrewtknoll@22: self.squelchCallbacks = false; andrewtknoll@20: end andrewtknoll@22: -- add to members list andrewtknoll@27: self.members[newDB] = namespace; andrewtknoll@41: namespace.RegisterCallback(self, "OnDatabaseShutdown", "OnMemberShutdown"); -- register the namespace, not the base db >@5: end andrewtknoll@18: andrewtknoll@39: -- LibDualSpec support andrewtknoll@39: andrewtknoll@35: --- Checks to see if this group uses LibDualSpec. andrewtknoll@44: -- @return ##true## if this group uses LibDualSpec, ##false## otherwise (boolean) andrewtknoll@35: function DBGroup:IsUsingDualSpec() andrewtknoll@35: return self.usesDualSpec; andrewtknoll@35: end andrewtknoll@35: andrewtknoll@35: --- Enables dual spec support if not already enabled. andrewtknoll@35: function DBGroup:EnableDualSpec() andrewtknoll@35: if not LibDualSpec then andrewtknoll@35: LibDualSpec = LibStub("LibDualSpec-1.0"); -- this will error if LDS isn't found andrewtknoll@35: end andrewtknoll@35: if not self.usesDualSpec then andrewtknoll@35: LibDualSpec:EnhanceDatabase(self.syncDB, self.name); andrewtknoll@35: LibDualSpec:EnhanceOptions(self.profileOptionsTable, self.syncDB); andrewtknoll@37: AceConfigRegistry:NotifyChange(self.name.."Profiles"); andrewtknoll@35: self.usesDualSpec = true; andrewtknoll@35: local namespace = self.syncDB:GetNamespace("LibDualSpec-1.0"); andrewtknoll@35: namespace.char.enabled = self.syncDB.char.enabled; andrewtknoll@35: namespace.char.profile = self.syncDB.char.profile; andrewtknoll@35: namespace.char.specGroup = self.syncDB.char.specGroup; andrewtknoll@35: self.syncDB:CheckDualSpecState(); andrewtknoll@35: end andrewtknoll@35: end andrewtknoll@35: andrewtknoll@39: -- slash command support andrewtknoll@39: andrewtknoll@39: --- Adds a slash command to the group. andrewtknoll@42: -- @paramsig slug, commandList[, handler] andrewtknoll@40: -- @param slug The base identifier to use for the slash command. (string) andrewtknoll@40: -- @param commandList The command itself, or a list of commands to use. (string or table) andrewtknoll@40: -- @param handler A handler function for the command. If nil, defaults to a function that andrewtknoll@40: -- calls the appropriate secondary command, or opens the root options panel. (function) andrewtknoll@39: function DBGroup:EnableSlashCommand(slug, commandList, handler) andrewtknoll@39: if self.slug then andrewtknoll@39: error("Usage: DBGroup:EnableSlashCommand(slug, commandList[, handler]): group already has a slash command.", 2); andrewtknoll@39: elseif type(slug) ~= "string" then andrewtknoll@39: error("Usage: DBGroup:EnableSlashCommand(slug, commandList[, handler]): 'slug' must be a string.", 2); andrewtknoll@39: elseif type(commandList) ~= "string" and type(commandList) ~= "table" then andrewtknoll@39: error("Usage: DBGroup:EnableSlashCommand(slug, commandList[, handler]): 'commandList' must be a string or table.", 2); andrewtknoll@39: elseif handler and type(handler) ~= "function" then andrewtknoll@39: error("Usage: DBGroup:EnableSlashCommand(slug, commandList[, handler]): 'handler' must be nil or a function.", 2); andrewtknoll@39: elseif type(commandList) == "table" then andrewtknoll@39: for i = 1, #commandList do andrewtknoll@39: if type(commandList[i]) ~= "string" then andrewtknoll@39: error("Usage: DBGroup:EnableSlashCommand(slug, commandList[, handler]): 'commandList' must contain only strings.", 2); andrewtknoll@39: end andrewtknoll@39: end andrewtknoll@39: end andrewtknoll@39: andrewtknoll@39: self.slug = slug; andrewtknoll@39: self.subCmdList = {}; andrewtknoll@39: if type(commandList) == "string" then andrewtknoll@39: _G["SLASH_"..slug.."1"] = commandList; andrewtknoll@39: else andrewtknoll@39: for i = 1, #commandList do andrewtknoll@39: _G["SLASH_"..slug..i] = commandList[i]; andrewtknoll@39: end andrewtknoll@39: end andrewtknoll@39: andrewtknoll@40: if handler then andrewtknoll@40: SlashCmdList[slug] = handler; andrewtknoll@40: else andrewtknoll@40: SlashCmdList[slug] = function(msg, editBox) andrewtknoll@40: for cmd, func in pairs(self.subCmdList) do andrewtknoll@40: if msg == cmd then andrewtknoll@40: func("", editBox); andrewtknoll@39: return; andrewtknoll@40: elseif msg:len() > cmd:len() then andrewtknoll@40: if msg:sub(1, cmd:len() + 1) == (cmd.." ") then andrewtknoll@40: func(msg:sub(cmd:len() + 2), editBox); andrewtknoll@40: return; andrewtknoll@40: end andrewtknoll@39: end andrewtknoll@39: end andrewtknoll@39: andrewtknoll@40: for k, button in pairs(InterfaceOptionsFrameAddOns.buttons) do andrewtknoll@46: if button.element and button.element.name == self.name and button.element.collapsed then andrewtknoll@40: OptionsListButtonToggle_OnClick(button.toggle); andrewtknoll@40: break; andrewtknoll@40: end andrewtknoll@39: end andrewtknoll@40: InterfaceOptionsFrame_OpenToCategory(self.name); andrewtknoll@40: end; andrewtknoll@40: end andrewtknoll@39: end andrewtknoll@39: andrewtknoll@40: --- Checks to see if this group has a slash command. andrewtknoll@44: -- @return ##true## if this group has a slash command, ##false## otherwise (boolean) andrewtknoll@40: function DBGroup:HasSlashCommand() andrewtknoll@40: if self.slug then andrewtknoll@40: return true; andrewtknoll@40: else andrewtknoll@40: return false; andrewtknoll@40: end andrewtknoll@40: end andrewtknoll@40: andrewtknoll@40: --- Adds an alias for the slash command. andrewtknoll@40: -- @param alias The alternate name for the slash command. (string) andrewtknoll@39: function DBGroup:AddSlashCommandAlias(alias) andrewtknoll@39: if type(alias) ~= "string" then andrewtknoll@39: error("Usage: DBGroup:AddSlashCommandAlias(alias): 'alias' must be a string.", 2); andrewtknoll@39: elseif not self.slug then andrewtknoll@39: error("Usage: DBGroup:AddSlashCommandAlias(alias): slash commands for this group have not be enabled.", 2); andrewtknoll@39: end andrewtknoll@39: andrewtknoll@39: local i = 1; andrewtknoll@39: while _G["SLASH_"..self.slug..i] do andrewtknoll@39: i = i + 1; andrewtknoll@39: end andrewtknoll@39: andrewtknoll@39: _G["SLASH_"..self.slug..i] = alias; andrewtknoll@39: end andrewtknoll@39: andrewtknoll@39: --- Adds a secondary command handler to the slash command for this group. andrewtknoll@40: -- This handler will be called if the argument to the slash command matches the name provided. andrewtknoll@42: -- @paramsig name, handler[, silent] andrewtknoll@40: -- @param name The name of the secondary command. (string) andrewtknoll@40: -- @param handler The function to handle the command. (function) andrewtknoll@42: -- @param silent ##True## if you want to replace the currently registered command, ##false## andrewtknoll@40: -- otherwise. (boolean) andrewtknoll@42: function DBGroup:AddSecondaryCommand(name, handler, silent) andrewtknoll@39: if type(name) ~= "string" then andrewtknoll@40: error("Usage: DBGroup:AddSecondaryCommand(name, handler[, overwrite]): 'name' must be a string.", 2); andrewtknoll@42: elseif type(handler) ~= "function" then andrewtknoll@40: error("Usage: DBGroup:AddSecondaryCommand(name, handler[, overwrite]): 'handler' must be a function.", 2); andrewtknoll@42: elseif not self.slug then andrewtknoll@40: error("Usage: DBGroup:AddSecondaryCommand(name, handler[, overwrite]): slash commands for this group have not be enabled.", 2); andrewtknoll@40: elseif type(overwrite) ~= "boolean" and type(overwrite) ~= "nil" then andrewtknoll@40: error("Usage: DBGroup:AddSecondaryCommand(name, handler[, overwrite]): 'overwrite' must be a boolean or nil", 2); andrewtknoll@39: end andrewtknoll@42: if not silent then andrewtknoll@40: for k, v in pairs(self.subCmdList) do andrewtknoll@40: if k == name then andrewtknoll@40: error("Usage: DBGroup:AddSecondaryCommand(name, handler[, overwrite]): command '"..name.."' already exists.", 2); andrewtknoll@40: end andrewtknoll@39: end andrewtknoll@39: end andrewtknoll@40: andrewtknoll@39: self.subCmdList[name] = handler; andrewtknoll@39: end andrewtknoll@39: andrewtknoll@44: --- Returns the list of secondary commands registered with this group. andrewtknoll@44: -- @return A table containing name-function pairs for secondary commands. (table) andrewtknoll@44: function DBGroup:GetSecondaryCommands() andrewtknoll@44: if not self.slug then andrewtknoll@44: error("Usage: DBGroup:GetSecondaryCommands(): Slash commands for this group have not been enabled", 2); andrewtknoll@44: end andrewtknoll@44: return self.subCmdList; andrewtknoll@44: end andrewtknoll@44: andrewtknoll@19: -- callback handlers (new profiles are handled by OnProfileChanged) andrewtknoll@19: andrewtknoll@24: function DBGroup:OnProfileChanged(callback, syncDB, profile) andrewtknoll@24: if not self.squelchCallbacks then andrewtknoll@24: for db, _ in pairs(self.members) do andrewtknoll@25: db:SetProfile(profile); andrewtknoll@24: end andrewtknoll@24: end andrewtknoll@18: end andrewtknoll@18: andrewtknoll@24: function DBGroup:OnProfileDeleted(callback, syncDB, profile) andrewtknoll@24: for db, _ in pairs(self.members) do andrewtknoll@24: db:DeleteProfile(profile, true); andrewtknoll@24: end andrewtknoll@18: end andrewtknoll@18: andrewtknoll@24: function DBGroup:OnProfileCopied(callback, syncDB, profile) andrewtknoll@24: for db, _ in pairs(self.members) do andrewtknoll@24: db:CopyProfile(profile, true); andrewtknoll@24: end andrewtknoll@18: end andrewtknoll@18: andrewtknoll@24: function DBGroup:OnProfileReset(callback, syncDB) andrewtknoll@24: for db, _ in pairs(self.members) do andrewtknoll@24: db:ResetProfile(false, false); andrewtknoll@24: end andrewtknoll@18: end andrewtknoll@23: andrewtknoll@42: -- shutdown handling andrewtknoll@42: andrewtknoll@29: local altProfile = nil; andrewtknoll@29: local dualSpecEnabled = nil; andrewtknoll@29: local activeSpecGroup = nil; andrewtknoll@28: andrewtknoll@28: function DBGroup:OnSyncShutdown(callback, syncDB) andrewtknoll@29: if self.usesDualSpec and not altProfile then andrewtknoll@29: altProfile = syncDB:GetDualSpecProfile(); andrewtknoll@29: dualSpecEnabled = syncDB:IsDualSpecEnabled(); andrewtknoll@29: activeSpecGroup = GetActiveSpecGroup(); andrewtknoll@28: end andrewtknoll@28: end andrewtknoll@28: andrewtknoll@24: local timestamp = nil; andrewtknoll@24: andrewtknoll@28: function DBGroup:OnMemberShutdown(callback, db) andrewtknoll@27: if not timestamp then -- ensure uniform timestamps to minimize andrewtknoll@27: timestamp = time(); -- calls to SetProfile in NewGroup andrewtknoll@24: end andrewtknoll@41: db.char.logoutTimestamp = timestamp; -- namespace is registered for callback, not base db andrewtknoll@29: if self.usesDualSpec then andrewtknoll@29: if not altProfile then andrewtknoll@29: altProfile = self.syncDB:GetDualSpecProfile(); andrewtknoll@29: dualSpecEnabled = self.syncDB:IsDualSpecEnabled(); andrewtknoll@29: activeSpecGroup = GetActiveSpecGroup(); andrewtknoll@29: end andrewtknoll@41: db.char.altProfile = altProfile; andrewtknoll@41: db.char.dualSpecEnabled = dualSpecEnabled; andrewtknoll@41: db.char.activeSpecGroup = activeSpecGroup; andrewtknoll@28: end andrewtknoll@23: end andrewtknoll@33: andrewtknoll@33: -- update existing groups andrewtknoll@33: for groupName, group in pairs(LibModuleDBShare.groups) do andrewtknoll@33: for funcName, func in pairs(DBGroup) do andrewtknoll@33: group[funcName] = func; andrewtknoll@33: end andrewtknoll@33: end