>@5: --- **LibModuleDBShare-1.0**\\ >@5: -- A description will eventually be here. >@5: -- >@5: -- @usage >@5: -- Also coming soon. >@5: -- @class file >@5: -- @name LibModuleDBShare-1.0.lua >@3: local MAJOR, MINOR = "LibModuleDBShare-1.0", 1 >@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. >@5: -- @param groupName The name of the new DB group. andrewtknoll@21: -- @param groupDescription A description of the group to be shown in the root options panel. andrewtknoll@21: -- @param initialDB The first DB to add to the group. andrewtknoll@28: -- @param usesDualSpec True if this group should use LibDualSpec, false otherwise. >@5: -- @usage >@5: -- local myAddonDBGroup = LibStub("LibModuleDBShare-1.0"):NewGroup("MyAddonGroupName", true) >@5: -- @return the new DB group object andrewtknoll@21: function LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec) 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@26: error("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@27: error("LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'initalDB' must be an AceDB-3.0 database.", 2); andrewtknoll@28: elseif type(usesDualSpec) ~= "boolean" and type(usesDualSpec) ~= "nil" then andrewtknoll@28: error("LibModuleDBShare:NewGroup(groupName, groupDescription, initialDB, usesDualSpec): 'usesDualSpec' must be a boolean or nil.", 2); andrewtknoll@28: elseif usesDualSpec and not LibDualSpec then andrewtknoll@28: error("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@29: if usesDualSpec and storedData.altProfile then andrewtknoll@30: namespace = group.syncDB:GetNamespace("LibDualSpec-1.0"); andrewtknoll@30: namespace.char.enabled = storedData.dualSpecEnabled; andrewtknoll@30: namespace.char.profile = storedData.altProfile; andrewtknoll@30: namespace.char.specGroup = storedData.activeSpecGroup; andrewtknoll@30: group.syncDB:CheckDualSpecState(); 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@28: initialDB.RegisterCallback(group, "OnDatabaseShutdown", "OnMemberShutdown"); andrewtknoll@20: group.squelchCallbacks = false; andrewtknoll@19: LibModuleDBShare.groups[groupName] = group; @12: return group; >@4: end >@4: >@5: --- Retrieves an existing DB group. >@5: -- @param groupName The name of the DB group to retrieve. >@5: -- @usage >@5: -- local myAddonDBGroup = LibStub("LibModuleDBShare-1.0"):GetGroup("MyAddonGroupName") >@5: -- @return the DB group object, or nil if not found >@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@22: -- @param newDB The database to add. >@5: -- @usage >@5: -- myAddonDBGroup:AddDB(MyAddon.db) andrewtknoll@22: function DBGroup:AddDB(newDB) andrewtknoll@22: -- verify parameters andrewtknoll@26: if type(newDB) ~= "table" or not AceDB.db_registry[newDB] then andrewtknoll@26: error("Usage: DBGroup:AddDB(newDB): 'newDB' must be a table.", 2); andrewtknoll@26: elseif type(self.members[newDB]) ~= "nil" then andrewtknoll@26: error("DBGroup:AddDB(newDB): 'newDB' is already a member of DBGroup.", 2); 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@30: if usesDualSpec and storedData.altProfile then andrewtknoll@30: namespace = group.syncDB:GetNamespace("LibDualSpec-1.0"); andrewtknoll@30: namespace.char.enabled = storedData.dualSpecEnabled; andrewtknoll@30: namespace.char.profile = storedData.altProfile; andrewtknoll@30: namespace.char.specGroup = storedData.activeSpecGroup; andrewtknoll@30: group.syncDB:CheckDualSpecState(); 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@28: newDB.RegisterCallback(self, "OnDatabaseShutdown", "OnMemberShutdown"); >@5: end andrewtknoll@18: 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@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@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@29: self.members[db].char.logoutTimestamp = timestamp; andrewtknoll@29: self.members[db].char.altProfile = altProfile; andrewtknoll@29: self.members[db].char.dualSpecEnabled = dualSpecEnabled; andrewtknoll@29: self.members[db].char.activeSpecGroup = activeSpecGroup; andrewtknoll@28: end andrewtknoll@23: end