1631 lines
75 KiB
Lua
1631 lines
75 KiB
Lua
|
-- ------------------------------------------------------------------------------ --
|
||
|
-- TradeSkillMaster --
|
||
|
-- https://tradeskillmaster.com --
|
||
|
-- All Rights Reserved - Detailed license information included with addon. --
|
||
|
-- ------------------------------------------------------------------------------ --
|
||
|
|
||
|
local _, TSM = ...
|
||
|
local Settings = TSM.Init("Service.Settings")
|
||
|
local L = TSM.Include("Locale").GetTable()
|
||
|
local TempTable = TSM.Include("Util.TempTable")
|
||
|
local Table = TSM.Include("Util.Table")
|
||
|
local Math = TSM.Include("Util.Math")
|
||
|
local String = TSM.Include("Util.String")
|
||
|
local Log = TSM.Include("Util.Log")
|
||
|
local Sound = TSM.Include("Util.Sound")
|
||
|
local CSV = TSM.Include("Util.CSV")
|
||
|
local Wow = TSM.Include("Util.Wow")
|
||
|
local private = {
|
||
|
context = {},
|
||
|
proxies = {},
|
||
|
views = {},
|
||
|
protectedAccessAllowed = {},
|
||
|
cachedConnectedRealms = nil,
|
||
|
upgradeContext = nil,
|
||
|
svCopyErrorTime = 0,
|
||
|
db = nil,
|
||
|
}
|
||
|
local LibRealmInfo = LibStub("LibRealmInfo")
|
||
|
local KEY_SEP = "@"
|
||
|
local SCOPE_KEY_SEP = " - "
|
||
|
local GLOBAL_SCOPE_KEY = " "
|
||
|
local DEFAULT_PROFILE_NAME = "Default"
|
||
|
local PLAYER = UnitName("player")
|
||
|
local FACTION = UnitFactionGroup("player")
|
||
|
local REALM = GetRealmName()
|
||
|
local VALID_TYPES = {
|
||
|
boolean = true,
|
||
|
string = true,
|
||
|
table = true,
|
||
|
number = true,
|
||
|
}
|
||
|
local SCOPE_TYPES = {
|
||
|
global = "g",
|
||
|
profile = "p",
|
||
|
realm = "r",
|
||
|
factionrealm = "f",
|
||
|
char = "c",
|
||
|
sync = "s",
|
||
|
}
|
||
|
local SCOPE_KEYS = {
|
||
|
global = " ",
|
||
|
profile = nil, -- set per-DB
|
||
|
realm = REALM,
|
||
|
factionrealm = strjoin(SCOPE_KEY_SEP, FACTION, REALM),
|
||
|
char = strjoin(SCOPE_KEY_SEP, PLAYER, REALM),
|
||
|
sync = strjoin(SCOPE_KEY_SEP, PLAYER, FACTION, REALM),
|
||
|
}
|
||
|
local DEFAULT_DB = {
|
||
|
_version = -math.huge, -- DB version
|
||
|
_currentProfile = {}, -- lookup table of the current profile name by character
|
||
|
_syncAccountKey = {}, -- lookup table of the sync account key by factionrealm
|
||
|
_syncOwner = {}, -- lookup table of the owner sync account key by character
|
||
|
_hash = 0,
|
||
|
_scopeKeys = {
|
||
|
profile = {},
|
||
|
realm = {},
|
||
|
factionrealm = {},
|
||
|
char = {},
|
||
|
sync = {},
|
||
|
},
|
||
|
_lastModifiedVersion = {},
|
||
|
}
|
||
|
|
||
|
-- Changelog:
|
||
|
-- [6] added 'global.locale' key
|
||
|
-- [7] changed default value of 'tsmItemTweetEnabled' to false
|
||
|
-- [8] added 'global.itemCacheVersion' key
|
||
|
-- [9] removed 'global.itemCacheVersion' key, added 'global.clientVersion' key
|
||
|
-- [10] first TSM4 version - combined all module settings into a single DB
|
||
|
-- [11] added profile.internalData.createdDefaultOperations
|
||
|
-- [12] added global.shoppingOptions.pctSource
|
||
|
-- [13] added profile.internalData.{managementGroupTreeContext,auctioningGroupTreeContext,shoppingGroupTreeContext}
|
||
|
-- [14] added global.userData.savedAuctioningSearches
|
||
|
-- [15] added global.coreOptions.bankUITab, profile.coreOptions.{bankUIBankFramePosition,bankUIGBankFramePosition}
|
||
|
-- [16] moved profile.coreOptions.{bankUIBankFramePosition,bankUIGBankFramePosition} to profile.internalData.{bankUIBankFramePosition,bankUIGBankFramePosition}
|
||
|
-- [17] added global.internalData.{mainUIFrameContext,auctionUIFrameContext,craftingUIFrameContext}
|
||
|
-- [18] removed global.internalData.itemStringLookup
|
||
|
-- [19] added sync scope (initially with internalData.{classKey,bagQuantity,bankQuantity,reagentBankQuantity,auctionQuantity,mailQuantity}), removed factionrealm.internalData.{syncMetadata,accountKey,inventory,characters} and factionrealm.coreOptions.syncAccounts, added global.debug.chatLoggingEnabled
|
||
|
-- [20] added global.tooltipOptions.enabled
|
||
|
-- [21] added global.craftingOptions.{profitPercent,questSmartCrafting,queueSort}
|
||
|
-- [22] added global.coreOptions.cleanGuildBank
|
||
|
-- [23] changed global.shoppingOptions.maxDeSearchPercent default to 100
|
||
|
-- [24] added global.auctioningOptions.{showAuctionDBTab,openAllBags,ahRowDisplay}
|
||
|
-- [25] split realm.internalData.goldLog into sync.internalData.goldLog and factionrealm.internalData.guildGoldLog
|
||
|
-- [26] added profile.internalData.{shoppingTabGroupContext,auctioningTabGroupContext}
|
||
|
-- [27] added char.internalData.craftingCooldowns
|
||
|
-- [28] added global.internalData.mailingUIFrameContext
|
||
|
-- [29] added global.internalData.vendoringUIFrameContext
|
||
|
-- [30] added global.internalData.bankingUIFrameContext
|
||
|
-- [31] changed global.internalData.bankingUIFrameContext default (isOpen = true), added profile.internalData.{bankingWarehousingGroupTreeContext,bankingAuctioningGroupTreeContext,bankingMailingGroupTreeContext}
|
||
|
-- [32] removed factionrealm.internalData.gathering, added factionrealm.internalData.gatheringContext.{crafter,professions}, added profile.gatheringOptions.sources
|
||
|
-- [33] added global.internalData.taskListUIFrameContext
|
||
|
-- [34] removed realm.internalData.{lastAuctionDBCompleteScan,lastAuctionDBSaveTime,auctionDBScanData}
|
||
|
-- [35] added factionrealm.userData.craftingCooldownIgnore
|
||
|
-- [36] removed factionrealm.internalData.playerProfessions and added sync.internalData.playerProfessions
|
||
|
-- [37] removed global.auctioningOptions.showAuctionDBTab
|
||
|
-- [38] removed global.mailingOptions.{defaultMailTab,autoCheck,displayMoneyCollected,deleteEmptyNPCMail,showReloadBtn,sendDelay,defaultPage}, added global.mailingOptions.recentlyMailedList
|
||
|
-- [39] added profile.internalData.{craftingGroupTreeContext,mailingGroupTreeContext,vendoringGroupTreeContext,importGroupTreeContext}
|
||
|
-- [40] removed global.accountingOptions.{timeFormat,mvSource}
|
||
|
-- [41] removed global.coreOptions.groupPriceSource
|
||
|
-- [42] removed global.vendoringOptions.defaultMerchantTab
|
||
|
-- [43] removed global.coreOptions.{moveDelay,bankUITab}, removed global.auctioningOptions.{openAllBags,ahRowDisplay}, removed global.craftingOptions.{profitPercent,questSmartCrafting,queueSort}, removed global.destroyingOptions.{logDays,timeFormat}, removed global.vendoringOptions.{autoSellTrash,qsHideGrouped,qsHideSoulbound,qsBatchSize,defaultPage,qsMaxMarketValue,qsDestroyValue}, removed profile.coreOptions.{cleanBags,cleanBank,cleanReagentBank,cleanGuildBank}
|
||
|
-- [44] changed global.internalData.{mainUIFrameContext,auctionUIFrameContext,craftingUIFrameContext,destroyingUIFrameContext,mailingUIFrameContext,vendoringUIFrameContext,bankingUIFrameContext} default (added "scale = 1")
|
||
|
-- [45] added char.internalData.auctionSaleHints
|
||
|
-- [46] added global.shoppingOptions.{buyoutConfirm,buyoutAlertSource}
|
||
|
-- [47] added factionrealm.internalData.expiringMail and factionrealm.internalData.expiringAuction
|
||
|
-- [48] added profile.internalData.exportGroupTreeContext
|
||
|
-- [49] added factionrealm.internalData.{mailDisenchantablesChar,mailExcessGoldChar,mailExcessGoldLimit}
|
||
|
-- [50] added factionrealm.internalData.{csvAuctionDBScan,auctionDBScanTime,auctionDBScanHash}
|
||
|
-- [51-53] resetting factionrealm.internalData.crafts
|
||
|
-- [54] removed global.coreOptions.{tsmItemTweetEnabled,auctionSaleEnabled,auctionBuyEnabled}
|
||
|
-- [55] added global.auctionUIContext.{auctioningSelectionDividedContainer,auctioningBagScrollingTable,auctioningLogScrollingTable,auctioningAuctionScrollingTable,myAuctionsScrollingTable,shoppingSelectionDividedContainer,shoppingAuctionScrollingTable,sniperScrollingTable,frame,showDefault,shoppingSearchesTabGroup}
|
||
|
-- added global.bankingUIContext.{frame,isOpen,tab}
|
||
|
-- added global.craftingUIContext.{craftsScrollingTable,matsScrollingTable,gatheringDividedContainer,gatheringScrollingTable,professionScrollingTable,frame,showDefault,professionDividedContainer}
|
||
|
-- added global.destroyingUIContext.itemsScrollingTable
|
||
|
-- added global.mailingUIContext.{mailsScrollingTable,frame,showDefault}
|
||
|
-- added global.mainUIContext.{ledgerDetailScrollingTable,ledgerInventoryScrollingTable,ledgerAuctionsScrollingTable,ledgerOtherScrollingTable,ledgerTransactionsScrollingTable,ledgerResaleScrollingTable,frame,dashboardDividedContainer,groupsDividedContainer,operationsDividedContainer,importExportDividedContainer}
|
||
|
-- added global.taskListUIContext.{frame,isOpen}
|
||
|
-- added global.vendoringUIContext.{buyScrollingTable,buybackScrollingTable,sellScrollingTable,frame,showDefault}
|
||
|
-- added profile.mainUIContext.{groupsManagementGroupTree,importGroupTree,exportGroupTree}
|
||
|
-- added profile.auctionUIContext.{auctioningTabGroup,auctioningGroupTree,shoppingGroupTree}
|
||
|
-- added profile.bankingUIContext.{warehousingGroupTree,auctioningGroupTree,mailingGroupTree}
|
||
|
-- added profile.craftingUIContext.groupTree
|
||
|
-- added profile.mailingUIContext.groupTree
|
||
|
-- added profile.vendoringUIContext.groupTree
|
||
|
-- removed profile.internalData.{auctioningTabGroupContext,auctioningGroupTreeContext,managementGroupTreeContext,shoppingGroupTreeContext,importGroupTreeContext,exportGroupTreeContext,bankingUIFrameContext,craftingUIFrameContext,auctionUIFrameContext,mailingUIFrameContext,vendoringUIFrameContext,destroyingUIFrameContext,mainUIFrameContext,taskListUIFrameContext}
|
||
|
-- [56] added factionrealm.internalData.isCraftFavorite
|
||
|
-- [57] updated global.auctionUIContext.auctioningAuctionScrollingTable
|
||
|
-- [58] updated global.auctionUIContext.sniperScrollingTable
|
||
|
-- [59] updated global.mainUIContext.{frame,dashboardDividedContainer,ledgerDetailScrollingTable,ledgerInventoryScrollingTable,ledgerAuctionsScrollingTable,ledgerOtherScrollingTable,ledgerTransactionsScrollingTable,ledgerResaleScrollingTable}
|
||
|
-- [60] updated global.auctionUIContext.{auctioningAuctionScrollingTable,shoppingAuctionScrollingTable,sniperScrollingTable}, global.craftingUIContext.professionScrollingTable
|
||
|
-- [61] updated global.auctionUIContext.sniperScrollingTable
|
||
|
-- [62] updated global.mainUIContext.{ledgerTransactionsScrollingTable,ledgerResaleScrollingTable}
|
||
|
-- [63] removed global.auctioningOptions.roundNormalPrice
|
||
|
-- [64] removed global.accountingOptions.smartBuyPrice
|
||
|
-- [65] added global.appearanceOptions.colorSet
|
||
|
-- [66] added global.auctionUIContext.auctioningSelectionVerticalDividedContainer
|
||
|
-- [67] updated global.mailingUIContext.mailsScrollingTable
|
||
|
-- [68] removed profile.internalData.{bankUIBankFramePosition,bankUIGBankFramePosition,shoppingTabGroupContext,bankingWarehousingGroupTreeContext,bankingAuctioningGroupTreeContext,bankingMailingGroupTreeContext,craftingGroupTreeContext,mailingGroupTreeContext,vendoringGroupTreeContext}
|
||
|
-- [69] updated global.mainUIContext.ledgerInventoryScrollingTable
|
||
|
-- [70] updated global.auctionUIContext.{auctioningAuctionScrollingTable,shoppingAuctionScrollingTable}
|
||
|
-- [71] moved profile.auctionUIContext.{auctioningGroupTree,shoppingGroupTree},profile.bankingUIContext.{warehousingGroupTree,auctioningGroupTree,mailingGroupTree},profile.craftingUIContext.groupTree,profile.mailingUIContext.groupTree,profile.mainUIContext.{groupsManagementGroupTree,importGroupTree,exportGroupTree} to char.*
|
||
|
-- [72] updated global.auctionUIContext.sniperScrollingTable
|
||
|
-- [73] added profile.vendoringUIContext.groupTree
|
||
|
-- [74] added sync.internalData.money
|
||
|
-- [75] updated global.appearanceOptions.colorSet
|
||
|
-- [76] updated global.mainUIContext.operationsSummaryScrollingTable
|
||
|
-- [77] added global.coreOptions.protectAuctionHouse
|
||
|
-- [78] added global.mainUIContext.{dashboardUnselectedCharacters,dashboardTimeRange}
|
||
|
-- [79] updated global.shoppingOptions.maxDeSearchLvl
|
||
|
-- [80] updated char.auctionUIContext.{auctioningGroupTree,shoppingGroupTree},char.bankingUIContext.{warehousingGroupTree,auctioningGroupTree,mailingGroupTree},char.craftingUIContext.groupTree,char.mailingUIContext.groupTree,char.vendoringUIContext.groupTree,char.mainUIContext.{importGroupTree,exportGroupTree}
|
||
|
-- [81] updated global.mailingUIContext.mailsScrollingTable
|
||
|
-- [82] updated global.craftingUIContext.professionScrollingTable
|
||
|
-- [83] added sync.internalData.goldLogLastUpdate, factionrealm.internalData.guildGoldLogLastUpdate
|
||
|
-- [84] added global.auctionUIContext.myAuctionsScrollingTable
|
||
|
-- [85] removed global.craftingOptions.ignoreCDCraftCost
|
||
|
-- [86] updated global.craftingUIContext.craftsScrollingTable
|
||
|
-- [87] added global.craftingUIContext.craftsScrollingTable
|
||
|
-- [88] added global.shoppingOptions.searchAutoFocus
|
||
|
-- [89] updated global.craftingOptions.defaultCraftPriceMethod
|
||
|
-- [90] added global.internalData.lastCharacter
|
||
|
-- [91] updated global.craftingUIContext.professionScrollingTable
|
||
|
-- [92] updated global.vendoringUIContext.buyScrollingTable
|
||
|
-- [93] moved profile.auctionUIContext.auctioningTabGroup to global.auctionUIContext.auctioningTabGroup
|
||
|
-- [94] added global.internalData.whatsNewVersion
|
||
|
-- [95] added global.appearanceOptions.showTotalMoney
|
||
|
-- [96] updated global.userData.{savedShoppingSearches,savedAuctioningSearches}
|
||
|
-- [97] added global.internalData.{optionalMatBonusIdLookup,optionalMatTextLookup}
|
||
|
|
||
|
local SETTINGS_INFO = {
|
||
|
version = 97,
|
||
|
global = {
|
||
|
debug = {
|
||
|
chatLoggingEnabled = { type = "boolean", default = false, lastModifiedVersion = 19 },
|
||
|
},
|
||
|
internalData = {
|
||
|
lastCharacter = { type = "string", default = "???", lastModifiedVersion = 90 },
|
||
|
vendorItems = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
appMessageId = { type = "number", default = 0, lastModifiedVersion = 10 },
|
||
|
destroyingHistory = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
whatsNewVersion = { type = "number", default = 0, lastModifiedVersion = 94 },
|
||
|
optionalMatBonusIdLookup = { type = "table", default = {}, lastModifiedVersion = 97 },
|
||
|
optionalMatTextLookup = { type = "table", default = {}, lastModifiedVersion = 97 },
|
||
|
},
|
||
|
appearanceOptions = {
|
||
|
taskListBackgroundLock = { type = "boolean", default = false, lastModifiedVersion = 87 },
|
||
|
showTotalMoney = { type = "boolean", default = false, lastModifiedVersion = 95 },
|
||
|
colorSet = { type = "string", default = "midnight", lastModifiedVersion = 75 },
|
||
|
},
|
||
|
auctionUIContext = {
|
||
|
frame = { type = "table", default = { width = 830, height = 587, centerX = -300, centerY = 100, scale = 1, page = 1 }, lastModifiedVersion = 55 },
|
||
|
showDefault = { type = "boolean", default = false, lastModifiedVersion = 55 },
|
||
|
auctioningSelectionDividedContainer = { type = "table", default = { leftWidth = 272 }, lastModifiedVersion = 55 },
|
||
|
auctioningSelectionVerticalDividedContainer = { type = "table", default = { leftWidth = 220 }, lastModifiedVersion = 66 },
|
||
|
auctioningBagScrollingTable = { type = "table", default = { colWidth = { selected = 16, item = 246, operation = 206 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
auctioningLogScrollingTable = { type = "table", default = { colWidth = { index = 14, item = 190, buyout = 110, operation = 108, seller = 90, info = 234 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
auctioningAuctionScrollingTable = { type = "table", default = { colWidth = { item = 226, ilvl = 32, qty = not TSM.IsWowClassic() and 40 or nil, posts = TSM.IsWowClassic() and 40 or nil, stack = TSM.IsWowClassic() and 40 or nil, timeLeft = 26, seller = TSM.IsWowClassic() and 88 or 136, itemBid = 115, bid = 115, itemBuyout = 115, buyout = 115, bidPct = 40, pct = 40 }, colHidden = { bid = true, buyout = true, bidPct = true } }, lastModifiedVersion = 70 },
|
||
|
myAuctionsScrollingTable = { type = "table", default = { colWidth = { item = 248, stackSize = 30, timeLeft = 40, highbidder = TSM.IsWowClassic() and 110 or nil, group = TSM.IsWowClassic() and 110 or 228, currentBid = 100, buyout = 100 }, colHidden = {} }, lastModifiedVersion = 84 },
|
||
|
shoppingSelectionDividedContainer = { type = "table", default = { leftWidth = 272 }, lastModifiedVersion = 55 },
|
||
|
shoppingAuctionScrollingTable = { type = "table", default = { colWidth = { item = 226, ilvl = 32, qty = not TSM.IsWowClassic() and 40 or nil, posts = TSM.IsWowClassic() and 40 or nil, stack = TSM.IsWowClassic() and 40 or nil, timeLeft = 26, seller = TSM.IsWowClassic() and 88 or 136, itemBid = 115, bid = 115, itemBuyout = 115, buyout = 115, bidPct = 40, pct = 40 }, colHidden = { bid = true, buyout = true, bidPct = true } }, lastModifiedVersion = 70 },
|
||
|
sniperScrollingTable = { type = "table", default = { colWidth = { icon = 24, item = 230, ilvl = 32, qty = not TSM.IsWowClassic() and 40 or nil, posts = TSM.IsWowClassic() and 40 or nil, stack = TSM.IsWowClassic() and 40 or nil, seller = TSM.IsWowClassic() and 86 or 134, itemBid = 115, bid = 115, itemBuyout = 115, buyout = 115, bidPct = 40, pct = 40 }, colHidden = { bid = true, buyout = true, bidPct = true } }, lastModifiedVersion = 72 },
|
||
|
shoppingSearchesTabGroup = { type = "table", default = { pathIndex = 1 }, lastModifiedVersion = 55 },
|
||
|
auctioningTabGroup = { type = "table", default = { pathIndex = 1 }, lastModifiedVersion = 93 },
|
||
|
},
|
||
|
bankingUIContext = {
|
||
|
frame = { type = "table", default = { width = 325, height = 600, centerX = 500, centerY = 0, scale = 1 }, lastModifiedVersion = 55 },
|
||
|
isOpen = { type = "boolean", default = true, lastModifiedVersion = 55 },
|
||
|
tab = { type = "string", default = "Warehousing", lastModifiedVersion = 55 },
|
||
|
},
|
||
|
craftingUIContext = {
|
||
|
frame = { type = "table", default = { width = 820, height = 587, centerX = -200, centerY = 0, scale = 1, page = 1 }, lastModifiedVersion = 55 },
|
||
|
showDefault = { type = "boolean", default = false, lastModifiedVersion = 55 },
|
||
|
craftsScrollingTable = { type = "table", default = { colWidth = { queued = 30, craftName = 218, operation = 80, bags = 28, ah = 24, craftingCost = 100, itemValue = 100, profit = 100, profitPct = 50, saleRate = 32 }, colHidden = { profitPct = true } }, lastModifiedVersion = 86 },
|
||
|
matsScrollingTable = { type = "table", default = { colWidth = { name = 242, price = 100, professions = 310, num = 100 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
gatheringDividedContainer = { type = "table", default = { leftWidth = 284 }, lastModifiedVersion = 55 },
|
||
|
gatheringScrollingTable = { type = "table", default = { colWidth = { name = 206, sources = 160, have = 50, need = 50 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
professionScrollingTable = { type = "table", default = { colWidth = { name = not TSM.IsWowClassic() and 240 or 288, qty = 54, rank = not TSM.IsWowClassic() and 40 or nil, craftingCost = 100, itemValue = 100, profit = 100, profitPct = 50, saleRate = 30 }, colHidden = { craftingCost = true, itemValue = true, profitPct = true }, collapsed = {} }, lastModifiedVersion = 91 },
|
||
|
professionDividedContainer = { type = "table", default = { leftWidth = 520 }, lastModifiedVersion = 55 },
|
||
|
},
|
||
|
destroyingUIContext = {
|
||
|
frame = { type = "table", default = { width = 296, height = 442, centerX = 0, centerY = 0, scale = 1 }, lastModifiedVersion = 55 },
|
||
|
itemsScrollingTable = { type = "table", default = { colWidth = { item = 214, num = 30 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
},
|
||
|
mailingUIContext = {
|
||
|
frame = { type = "table", default = { width = 620, height = 516, centerX = -200, centerY = 0, scale = 1, page = 1 }, lastModifiedVersion = 55 },
|
||
|
showDefault = { type = "boolean", default = false, lastModifiedVersion = 55 },
|
||
|
mailsScrollingTable = { type = "table", default = { colWidth = { items = 380, sender = 100, expires = 65, money = 115 }, colHidden = { sender = true } }, lastModifiedVersion = 81 },
|
||
|
},
|
||
|
mainUIContext = {
|
||
|
frame = { type = "table", default = { width = 900, height = 700, centerX = 0, centerY = 0, scale = 1, page = 1 }, lastModifiedVersion = 59 },
|
||
|
ledgerDetailScrollingTable = { type = "table", default = { colWidth = { activityType = 91, source = 60, buyerSeller = 100, qty = 45, perItem = 120, totalPrice = 120, time = 110 }, colHidden = {} }, lastModifiedVersion = 59 },
|
||
|
ledgerInventoryScrollingTable = { type = "table", default = { colWidth = { item = 160, totalItems = 50, bags = 50, banks = 50, mail = 50, alts = 50, guildVault = 50, auctionHouse = 50, totalValue = 120 }, colHidden = {} }, lastModifiedVersion = 69 },
|
||
|
ledgerAuctionsScrollingTable = { type = "table", default = { colWidth = { item = 305, player = 110, stackSize = 55, quantity = 72, time = 120 }, colHidden = {} }, lastModifiedVersion = 59 },
|
||
|
ledgerOtherScrollingTable = { type = "table", default = { colWidth = { type = 200, character = 110, otherCharacter = 122, amount = 120, time = 110 }, colHidden = {} }, lastModifiedVersion = 59 },
|
||
|
ledgerTransactionsScrollingTable = { type = "table", default = { colWidth = { item = 156, player = 95, type = 50, stack = 55, auctions = 60, perItem = 120, total = 120, time = 110 }, colHidden = { total = true } }, lastModifiedVersion = 62 },
|
||
|
ledgerResaleScrollingTable = { type = "table", default = { colWidth = { item = 194, bought = 50, avgBuyPrice = 120, sold = 50, avgSellPrice = 120, avgProfit = 120, totalProfit = 120, profitPct = 80 }, colHidden = { totalProfit = true, profitPct = true } }, lastModifiedVersion = 62 },
|
||
|
dashboardDividedContainer = { type = "table", default = { leftWidth = 300 }, lastModifiedVersion = 59 },
|
||
|
dashboardUnselectedCharacters = { type = "table", default = {}, lastModifiedVersion = 78 },
|
||
|
dashboardTimeRange = { type = "number", default = -1, lastModifiedVersion = 78 },
|
||
|
groupsDividedContainer = { type = "table", default = { leftWidth = 300 }, lastModifiedVersion = 55 },
|
||
|
operationsDividedContainer = { type = "table", default = { leftWidth = 306 }, lastModifiedVersion = 55 },
|
||
|
importExportDividedContainer = { type = "table", default = { leftWidth = 300 }, lastModifiedVersion = 55 },
|
||
|
operationsSummaryScrollingTable = { type = "table", default = { colWidth = { selected = 16, name = 248, groups = 130, items = 130 }, colHidden = {} }, lastModifiedVersion = 76 },
|
||
|
},
|
||
|
taskListUIContext = {
|
||
|
frame = { type = "table", default = { topRightX = -220, topRightY = -10, minimized = false, isOpen = true }, lastModifiedVersion = 55 },
|
||
|
isOpen = { type = "boolean", default = true, lastModifiedVersion = 55 },
|
||
|
},
|
||
|
vendoringUIContext = {
|
||
|
frame = { type = "table", default = { width = 560, height = 500, centerX = -200, centerY = 0, scale = 1, page = 1 }, lastModifiedVersion = 55 },
|
||
|
showDefault = { type = "boolean", default = false, lastModifiedVersion = 55 },
|
||
|
buyScrollingTable = { type = "table", default = { colWidth = { qty = 40, item = 310, ilvl = 32, cost = 150 }, colHidden = { ilvl = true } }, lastModifiedVersion = 92 },
|
||
|
buybackScrollingTable = { type = "table", default = { colWidth = { qty = 40, item = 360, cost = 100 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
sellScrollingTable = { type = "table", default = { colWidth = { item = 300, vendorSell = 100, potential = 100 }, colHidden = {} }, lastModifiedVersion = 55 },
|
||
|
},
|
||
|
coreOptions = {
|
||
|
globalOperations = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
protectAuctionHouse = { type = "boolean", default = false, lastModifiedVersion = 77 },
|
||
|
chatFrame = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
auctionSaleSound = { type = "string", default = Sound.GetNoSoundKey(), lastModifiedVersion = 10 },
|
||
|
minimapIcon = { type = "table", default = { hide = false, minimapPos = 220, radius = 80 }, lastModifiedVersion = 10 },
|
||
|
destroyValueSource = { type = "string", default = "dbmarket", lastModifiedVersion = 10 },
|
||
|
groupPriceSource = { type = "string", default = "dbmarket", lastModifiedVersion = 41 },
|
||
|
},
|
||
|
accountingOptions = {
|
||
|
trackTrades = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
autoTrackTrades = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
auctioningOptions = {
|
||
|
cancelWithBid = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
disableInvalidMsg = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
matchWhitelist = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
scanCompleteSound = { type = "string", default = Sound.GetNoSoundKey(), lastModifiedVersion = 10 },
|
||
|
confirmCompleteSound = { type = "string", default = Sound.GetNoSoundKey(), lastModifiedVersion = 10 },
|
||
|
},
|
||
|
craftingOptions = {
|
||
|
defaultMatCostMethod = { type = "string", default = "min(dbmarket, crafting, vendorbuy, convert(dbmarket))", lastModifiedVersion = 10 },
|
||
|
defaultCraftPriceMethod = { type = "string", default = "first(dbminbuyout, dbmarket)*0.95", lastModifiedVersion = 89 },
|
||
|
ignoreCharacters = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
ignoreGuilds = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
destroyingOptions = {
|
||
|
autoStack = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
includeSoulbound = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
autoShow = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
deMaxQuality = { type = "number", default = 3, lastModifiedVersion = 10 },
|
||
|
deAbovePrice = { type = "string", default = "0c", lastModifiedVersion = 10 },
|
||
|
},
|
||
|
mailingOptions = {
|
||
|
sendItemsIndividually = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
inboxMessages = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
sendMessages = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
resendDelay = { type = "number", default = 1, lastModifiedVersion = 10 },
|
||
|
keepMailSpace = { type = "number", default = 0, lastModifiedVersion = 10 },
|
||
|
deMaxQuality = { type = "number", default = 2, lastModifiedVersion = 10 },
|
||
|
openMailSound = { type = "string", default = Sound.GetNoSoundKey(), lastModifiedVersion = 10 },
|
||
|
recentlyMailedList = { type = "table", default = {}, lastModifiedVersion = 38 },
|
||
|
},
|
||
|
shoppingOptions = {
|
||
|
minDeSearchLvl = { type = "number", default = 1, lastModifiedVersion = 10 },
|
||
|
maxDeSearchLvl = { type = "number", default = 500, lastModifiedVersion = 79 },
|
||
|
maxDeSearchPercent = { type = "number", default = 100, lastModifiedVersion = 23 },
|
||
|
pctSource = { type = "string", default = "dbmarket", lastModifiedVersion = 12 },
|
||
|
buyoutConfirm = { type = "boolean", default = false, lastModifiedVersion = 46 },
|
||
|
buyoutAlertSource = { type = "string", default = "min(100000g, 200% dbmarket)", lastModifiedVersion = 46 },
|
||
|
searchAutoFocus = { type = "boolean", default = true, lastModifiedVersion = 88 },
|
||
|
},
|
||
|
sniperOptions = {
|
||
|
sniperSound = { type = "string", default = Sound.GetNoSoundKey(), lastModifiedVersion = 10 },
|
||
|
},
|
||
|
vendoringOptions = {
|
||
|
displayMoneyCollected = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
qsMarketValue = { type = "string", default = "dbmarket", lastModifiedVersion = 10 },
|
||
|
},
|
||
|
tooltipOptions = {
|
||
|
enabled = { type = "boolean", default = true, lastModifiedVersion = 20 },
|
||
|
embeddedTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
customPriceTooltips = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
moduleTooltips = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
vendorBuyTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
vendorSellTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
groupNameTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
detailedDestroyTooltip = { type = "boolean", default = false, lastModifiedVersion = 10 },
|
||
|
millTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
prospectTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
deTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
transformTooltip = { type = "boolean", default = true, lastModifiedVersion = 10 },
|
||
|
operationTooltips = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
tooltipShowModifier = { type = "string", default = "none", lastModifiedVersion = 10 },
|
||
|
inventoryTooltipFormat = { type = "string", default = "full", lastModifiedVersion = 10 },
|
||
|
tooltipPriceFormat = { type = "string", default = "text", lastModifiedVersion = 10 },
|
||
|
},
|
||
|
userData = {
|
||
|
operations = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
customPriceSources = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
destroyingIgnore = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
savedShoppingSearches = { type = "table", default = { filters = {}, name = {}, isFavorite = {} }, lastModifiedVersion = 96 },
|
||
|
vendoringIgnore = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
savedAuctioningSearches = { type = "table", default = { filters = {}, searchTypes = {}, name = {}, isFavorite = {} }, lastModifiedVersion = 96 },
|
||
|
},
|
||
|
},
|
||
|
profile = {
|
||
|
internalData = {
|
||
|
createdDefaultOperations = { type = "boolean", default = false, lastModifiedVersion = 11 },
|
||
|
},
|
||
|
userData = {
|
||
|
groups = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
items = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
operations = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
gatheringOptions = {
|
||
|
sources = { type = "table", default = { "vendor", "guildBank", "alt", "altGuildBank", "craftProfit", "auction", "craftNoProfit" }, lastModifiedVersion = 32 },
|
||
|
},
|
||
|
},
|
||
|
factionrealm = {
|
||
|
internalData = {
|
||
|
characterGuilds = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
guildVaults = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
pendingMail = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
expiringMail = { type = "table", default = {}, lastModifiedVersion = 47 },
|
||
|
expiringAuction = { type = "table", default = {}, lastModifiedVersion = 47 },
|
||
|
mailDisenchantablesChar = { type = "string", default = "", lastModifiedVersion = 49 },
|
||
|
mailExcessGoldChar = { type = "string", default = "", lastModifiedVersion = 49 },
|
||
|
mailExcessGoldLimit = { type = "number", default = 10000000000, lastModifiedVersion = 49 },
|
||
|
crafts = { type = "table", default = {}, lastModifiedVersion = 53 },
|
||
|
mats = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
guildGoldLog = { type = "table", default = {}, lastModifiedVersion = 25 },
|
||
|
guildGoldLogLastUpdate = { type = "table", default = {}, lastModifiedVersion = 83 },
|
||
|
csvAuctionDBScan = { type = "string", default = "", lastModifiedVersion = 50 },
|
||
|
auctionDBScanTime = { type = "number", default = 0, lastModifiedVersion = 50 },
|
||
|
auctionDBScanHash = { type = "number", default = 0, lastModifiedVersion = 50 },
|
||
|
isCraftFavorite = { type = "table", default = {}, lastModifiedVersion = 56 },
|
||
|
},
|
||
|
coreOptions = {
|
||
|
ignoreGuilds = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
auctioningOptions = {
|
||
|
whitelist = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
gatheringContext = {
|
||
|
crafter = { type = "string", default = "", lastModifiedVersion = 32 },
|
||
|
professions = { type = "table", default = {}, lastModifiedVersion = 32 },
|
||
|
},
|
||
|
userData = {
|
||
|
craftingCooldownIgnore = { type = "table", default = {}, lastModifiedVersion = 35 },
|
||
|
},
|
||
|
},
|
||
|
realm = {
|
||
|
internalData = {
|
||
|
csvSales = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
csvBuys = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
csvIncome = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
csvExpense = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
csvExpired = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
csvCancelled = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
saveTimeSales = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
saveTimeBuys = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
saveTimeExpires = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
saveTimeCancels = { type = "string", default = "", lastModifiedVersion = 10 },
|
||
|
accountingTrimmed = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
},
|
||
|
},
|
||
|
char = {
|
||
|
internalData = {
|
||
|
auctionPrices = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
auctionMessages = { type = "table", default = {}, lastModifiedVersion = 10 },
|
||
|
craftingCooldowns = { type = "table", default = {}, lastModifiedVersion = 27 },
|
||
|
auctionSaleHints = { type = "table", default = {}, lastModifiedVersion = 45 },
|
||
|
},
|
||
|
auctionUIContext = {
|
||
|
auctioningGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
shoppingGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
bankingUIContext = {
|
||
|
warehousingGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
auctioningGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
mailingGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
craftingUIContext = {
|
||
|
groupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
mailingUIContext = {
|
||
|
groupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
vendoringUIContext = {
|
||
|
groupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
mainUIContext = {
|
||
|
groupsManagementGroupTree = { type = "table", default = { collapsed = {} }, lastModifiedVersion = 71 },
|
||
|
importGroupTree = { type = "table", default = { collapsed = {}, selected = {} }, lastModifiedVersion = 80 },
|
||
|
exportGroupTree = { type = "table", default = { collapsed = {}, unselected = {} }, lastModifiedVersion = 80 },
|
||
|
},
|
||
|
},
|
||
|
sync = {
|
||
|
-- NOTE: whenever these are changed, the sync version needs to be increased in LibTSM/Services/SyncClasses/Constants.lua
|
||
|
internalData = {
|
||
|
money = { type = "number", default = 0, lastModifiedVersion = 74 },
|
||
|
classKey = { type = "string", default = "", lastModifiedVersion = 19 },
|
||
|
bagQuantity = { type = "table", default = {}, lastModifiedVersion = 19 },
|
||
|
bankQuantity = { type = "table", default = {}, lastModifiedVersion = 19 },
|
||
|
reagentBankQuantity = { type = "table", default = {}, lastModifiedVersion = 19 },
|
||
|
auctionQuantity = { type = "table", default = {}, lastModifiedVersion = 19 },
|
||
|
mailQuantity = { type = "table", default = {}, lastModifiedVersion = 19 },
|
||
|
goldLog = { type = "string", default = "", lastModifiedVersion = 25 },
|
||
|
goldLogLastUpdate = { type = "number", default = 0, lastModifiedVersion = 83 },
|
||
|
playerProfessions = { type = "table", default = {}, lastModifiedVersion = 36 },
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Module Loading
|
||
|
-- ============================================================================
|
||
|
|
||
|
Settings:OnSettingsLoad(function()
|
||
|
local db, upgradeObj = private.Constructor("TradeSkillMasterDB", SETTINGS_INFO)
|
||
|
private.db = db
|
||
|
if not upgradeObj then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- process DB upgrades
|
||
|
local prevVersion = upgradeObj:GetPrevVersion()
|
||
|
if prevVersion < 19 then
|
||
|
-- migrate inventory data to the sync scope
|
||
|
local oldInventoryData = TempTable.Acquire()
|
||
|
local oldSyncMetadata = TempTable.Acquire()
|
||
|
local oldAccountKey = TempTable.Acquire()
|
||
|
local oldCharacters = TempTable.Acquire()
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, scopeKey, _, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "factionrealm" then
|
||
|
if settingKey == "inventory" then
|
||
|
oldInventoryData[scopeKey] = value
|
||
|
elseif settingKey == "syncMetadata" then
|
||
|
oldSyncMetadata[scopeKey] = value
|
||
|
elseif settingKey == "accountKey" then
|
||
|
oldAccountKey[scopeKey] = value
|
||
|
elseif settingKey == "characters" then
|
||
|
oldCharacters[scopeKey] = value
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
for factionrealm, characters in pairs(oldInventoryData) do
|
||
|
local syncMetadata = oldSyncMetadata[factionrealm] and oldSyncMetadata[factionrealm].TSM_CHARACTERS
|
||
|
for character, inventoryData in pairs(characters) do
|
||
|
if not syncMetadata or not syncMetadata[character] or syncMetadata[character].owner == oldAccountKey[factionrealm] then
|
||
|
db:NewSyncCharacter(character, db:GetSyncAccountKey(factionrealm), factionrealm)
|
||
|
local syncScopeKey = db:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
local class = oldCharacters[factionrealm] and oldCharacters[factionrealm][character]
|
||
|
if type(class) == "string" then
|
||
|
db:Set("sync", syncScopeKey, "internalData", "classKey", class)
|
||
|
end
|
||
|
db:Set("sync", syncScopeKey, "internalData", "bagQuantity", inventoryData.bag)
|
||
|
db:Set("sync", syncScopeKey, "internalData", "bankQuantity", inventoryData.bank)
|
||
|
db:Set("sync", syncScopeKey, "internalData", "reagentBankQuantity", inventoryData.reagentBank)
|
||
|
db:Set("sync", syncScopeKey, "internalData", "auctionQuantity", inventoryData.auction)
|
||
|
db:Set("sync", syncScopeKey, "internalData", "mailQuantity", inventoryData.mail)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
TempTable.Release(oldInventoryData)
|
||
|
TempTable.Release(oldSyncMetadata)
|
||
|
TempTable.Release(oldAccountKey)
|
||
|
TempTable.Release(oldCharacters)
|
||
|
end
|
||
|
if prevVersion < 25 then
|
||
|
-- migrate gold log info
|
||
|
local NEW_CSV_COLS = { "minute", "copper" }
|
||
|
local function ConvertGoldLogFormat(data)
|
||
|
local decodedData = select(2, CSV.Decode(data))
|
||
|
if not decodedData then
|
||
|
return
|
||
|
end
|
||
|
for _, entry in ipairs(decodedData) do
|
||
|
local minute = entry.startMinute
|
||
|
local copper = entry.copper
|
||
|
wipe(entry)
|
||
|
entry.minute = minute
|
||
|
entry.copper = copper
|
||
|
end
|
||
|
return CSV.Encode(NEW_CSV_COLS, decodedData)
|
||
|
end
|
||
|
local function ProcessGoldLogData(character, data, scopeKey)
|
||
|
if type(data) ~= "string" then
|
||
|
return
|
||
|
end
|
||
|
-- check if we know about this character and under what faction
|
||
|
local syncScopeKey = nil
|
||
|
for factionrealm in db:FactionrealmByRealmIterator(scopeKey) do
|
||
|
local testSyncScopeKey = db:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
if db:Get("sync", testSyncScopeKey, "internalData", "classKey") then
|
||
|
syncScopeKey = testSyncScopeKey
|
||
|
end
|
||
|
end
|
||
|
if syncScopeKey then
|
||
|
db:Set("sync", syncScopeKey, "internalData", "goldLog", ConvertGoldLogFormat(data))
|
||
|
else
|
||
|
-- check if this is a known guild
|
||
|
local found = false
|
||
|
for factionrealm in db:FactionrealmByRealmIterator(scopeKey) do
|
||
|
local characterGuilds = db:Get("factionrealm", factionrealm, "internalData", "characterGuilds")
|
||
|
if not found and characterGuilds and Table.KeyByValue(characterGuilds, character) then
|
||
|
local guildGoldLog = db:Get("factionrealm", factionrealm, "internalData", "guildGoldLog") or {}
|
||
|
guildGoldLog[character] = ConvertGoldLogFormat(data)
|
||
|
db:Set("factionrealm", factionrealm, "internalData", "guildGoldLog", guildGoldLog)
|
||
|
found = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion >= 10 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, scopeKey, _, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "realm" and settingKey == "goldLog" then
|
||
|
for character, data in pairs(value) do
|
||
|
ProcessGoldLogData(character, data, scopeKey)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 36 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, factionrealm, _, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "factionrealm" and settingKey == "playerProfessions" then
|
||
|
for character, data in pairs(value) do
|
||
|
-- check if we know about this character
|
||
|
local syncScopeKey = db:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
if db:Get("sync", syncScopeKey, "internalData", "classKey") then
|
||
|
db:Set("sync", syncScopeKey, "internalData", "playerProfessions", data)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 53 and not TSM.IsWowClassic() then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, factionrealm, namespace, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "factionrealm" and namespace == "internalData" and settingKey == "crafts" then
|
||
|
db:Set("factionrealm", factionrealm, "internalData", "crafts", value)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 64 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, _, namespace, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "global" and namespace == "accountingOptions" and settingKey == "smartBuyPrice" and value then
|
||
|
-- show a dialog to inform the user that this was removed
|
||
|
StaticPopupDialogs["TSM_ACCOUNTING_SMART_AVG_REMOVED"] = {
|
||
|
text = L["The 'use smart average for purchase price' setting has been removed from TSM and replaced with a new 'SmartAvgBuy' price source. Please update your custom prices appropriately."],
|
||
|
button1 = OKAY,
|
||
|
timeout = 0,
|
||
|
whileDead = true,
|
||
|
}
|
||
|
Wow.ShowStaticPopupDialog("TSM_ACCOUNTING_SMART_AVG_REMOVED")
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 82 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, scopeKey, namespace, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "global" and namespace == "craftingUIContext" and settingKey == "professionScrollingTable" then
|
||
|
-- preserve the previous values
|
||
|
local newTbl = db:Get(scopeType, scopeKey, namespace, settingKey)
|
||
|
for col, width in pairs(value.colWidth) do
|
||
|
newTbl.colWidth[col] = width
|
||
|
end
|
||
|
for col, hidden in pairs(value.colHidden) do
|
||
|
newTbl.colHidden[col] = hidden
|
||
|
end
|
||
|
if value.collapsed then
|
||
|
for col, collapsed in pairs(value.collapsed) do
|
||
|
newTbl.collapsed[col] = collapsed
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 89 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, scopeKey, namespace, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "global" and namespace == "craftingOptions" and settingKey == "defaultCraftPriceMethod" then
|
||
|
-- preserve the previous value
|
||
|
db:Set(scopeType, scopeKey, namespace, settingKey, value)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if prevVersion < 96 then
|
||
|
for key, value in upgradeObj:RemovedSettingIterator() do
|
||
|
local scopeType, scopeKey, namespace, settingKey = upgradeObj:GetKeyInfo(key)
|
||
|
if scopeType == "global" and namespace == "userData" and settingKey == "savedShoppingSearches" then
|
||
|
-- convert how they are stored
|
||
|
local newTbl = db:Get(scopeType, scopeKey, namespace, settingKey)
|
||
|
for i, searchInfo in ipairs(value) do
|
||
|
local filter = searchInfo.filter
|
||
|
if searchInfo.name ~= filter then
|
||
|
newTbl.name[filter] = searchInfo.name
|
||
|
end
|
||
|
if searchInfo.isFavorite then
|
||
|
newTbl.isFavorite[filter] = true
|
||
|
end
|
||
|
newTbl.filters[i] = filter
|
||
|
end
|
||
|
elseif scopeType == "global" and namespace == "userData" and settingKey == "savedAuctioningSearches" then
|
||
|
-- convert how they are stored
|
||
|
local newTbl = db:Get(scopeType, scopeKey, namespace, settingKey)
|
||
|
for i, searchInfo in ipairs(value) do
|
||
|
local filter = searchInfo.filter
|
||
|
if searchInfo.name ~= filter then
|
||
|
newTbl.name[filter] = searchInfo.name
|
||
|
end
|
||
|
if searchInfo.isFavorite then
|
||
|
newTbl.isFavorite[filter] = true
|
||
|
end
|
||
|
newTbl.filters[i] = filter
|
||
|
newTbl.searchTypes[i] = searchInfo.searchType
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Module Functions
|
||
|
-- ============================================================================
|
||
|
|
||
|
-- TODO: get rid of this
|
||
|
function Settings.GetDB()
|
||
|
assert(private.db)
|
||
|
return private.db
|
||
|
end
|
||
|
|
||
|
function Settings.NewView()
|
||
|
assert(private.db)
|
||
|
return private.CreateView(private.db)
|
||
|
end
|
||
|
|
||
|
function Settings.Get(scope, scopeKey, namespace, key)
|
||
|
return private.db:Get(scope, scopeKey, namespace, key)
|
||
|
end
|
||
|
|
||
|
function Settings.Set(scope, scopeKey, namespace, key, value)
|
||
|
return private.db:Set(scope, scopeKey, namespace, key, value)
|
||
|
end
|
||
|
|
||
|
function Settings.GetCurrentSyncAccountKey()
|
||
|
return private.db:GetSyncAccountKey()
|
||
|
end
|
||
|
|
||
|
function Settings.GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
return private.db:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
end
|
||
|
|
||
|
function Settings.GetCharacterSyncAccountKey(character)
|
||
|
return private.context[private.db].db._syncOwner[private.db:GetSyncScopeKeyByCharacter(character)]
|
||
|
end
|
||
|
|
||
|
function Settings.ShowSyncSVCopyError()
|
||
|
if time() - private.svCopyErrorTime < 60 then
|
||
|
return
|
||
|
end
|
||
|
private.svCopyErrorTime = time()
|
||
|
Log.PrintfUser(L["It appears that you've manually copied your saved variables between accounts which will cause TSM's automatic sync'ing to not work. You'll need to undo this, and/or delete the TradeSkillMaster saved variables files on both accounts (with WoW closed) in order to fix this."])
|
||
|
end
|
||
|
|
||
|
function Settings.CharacterByAccountFactionrealmIterator(account, factionrealm)
|
||
|
factionrealm = factionrealm or SCOPE_KEYS.factionrealm
|
||
|
account = account or private.db:GetSyncAccountKey(factionrealm)
|
||
|
local result = TempTable.Acquire()
|
||
|
for scopeKey, ownerAccount in pairs(private.context[private.db].db._syncOwner) do
|
||
|
if ownerAccount == account then
|
||
|
local character = strmatch(scopeKey, "^(.+)"..String.Escape(SCOPE_KEY_SEP..factionrealm))
|
||
|
if character then
|
||
|
tinsert(result, character)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return TempTable.Iterator(result)
|
||
|
end
|
||
|
|
||
|
function Settings.CharacterByFactionrealmIterator(factionrealm)
|
||
|
factionrealm = factionrealm or SCOPE_KEYS.factionrealm
|
||
|
local result = TempTable.Acquire()
|
||
|
for scopeKey in pairs(private.context[private.db].db._syncOwner) do
|
||
|
local character = strmatch(scopeKey, "^(.+)"..String.Escape(SCOPE_KEY_SEP..factionrealm))
|
||
|
if character then
|
||
|
tinsert(result, character)
|
||
|
end
|
||
|
end
|
||
|
return TempTable.Iterator(result)
|
||
|
end
|
||
|
|
||
|
function Settings.IsCurrentAccountOwner(character)
|
||
|
return Settings.GetCharacterSyncAccountKey(character) == Settings.GetCurrentSyncAccountKey()
|
||
|
end
|
||
|
|
||
|
function Settings.ConnectedFactionrealmAltCharacterIterator()
|
||
|
local result = TempTable.Acquire()
|
||
|
for factionrealm in private.db:GetConnectedRealmIterator("factionrealm") do
|
||
|
for scopeKey in pairs(private.context[private.db].db._syncOwner) do
|
||
|
local character = strmatch(scopeKey, "^(.+)"..String.Escape(SCOPE_KEY_SEP..factionrealm))
|
||
|
if character and (factionrealm ~= SCOPE_KEYS.factionrealm or character ~= PLAYER) then
|
||
|
tinsert(result, factionrealm)
|
||
|
tinsert(result, character)
|
||
|
tinsert(result, character..SCOPE_KEY_SEP..factionrealm)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return TempTable.Iterator(result, 3)
|
||
|
end
|
||
|
|
||
|
function Settings.SyncAccountIterator()
|
||
|
local result = TempTable.Acquire()
|
||
|
local used = TempTable.Acquire()
|
||
|
for _, syncOwner in pairs(private.context[private.db].db._syncOwner) do
|
||
|
if strmatch(syncOwner, "^"..String.Escape(SCOPE_KEYS.factionrealm..SCOPE_KEY_SEP).."(%d+)$") and not used[syncOwner] and syncOwner ~= Settings.GetCurrentSyncAccountKey() then
|
||
|
used[syncOwner] = true
|
||
|
tinsert(result, syncOwner)
|
||
|
end
|
||
|
end
|
||
|
TempTable.Release(used)
|
||
|
return TempTable.Iterator(result)
|
||
|
end
|
||
|
|
||
|
function Settings.NewSyncCharacter(accountKey, character)
|
||
|
local factionrealm = SCOPE_KEYS.factionrealm
|
||
|
assert(strmatch(accountKey, "^"..String.Escape(factionrealm..SCOPE_KEY_SEP).."(%d+)$"), "Invalid account key")
|
||
|
local scopeKey = private.db:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
local context = private.context[private.db]
|
||
|
context.db._syncOwner[scopeKey] = accountKey
|
||
|
if not tContains(context.db._scopeKeys.sync, scopeKey) then
|
||
|
tinsert(context.db._scopeKeys.sync, scopeKey)
|
||
|
end
|
||
|
private.SetScopeDefaults(context.db, context.settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES.sync, String.Escape(scopeKey), ".+", ".+"))
|
||
|
end
|
||
|
|
||
|
function Settings.RemoveSyncAccount(accountKey)
|
||
|
local settingsDB = private.context[private.db].db
|
||
|
assert(accountKey ~= private.db:GetSyncAccountKey())
|
||
|
local scopeKeysToRemove = TempTable.Acquire()
|
||
|
for scopeKey, ownerAccountKey in pairs(settingsDB._syncOwner) do
|
||
|
if ownerAccountKey == accountKey then
|
||
|
tinsert(scopeKeysToRemove, scopeKey)
|
||
|
end
|
||
|
end
|
||
|
for _, scopeKey in ipairs(scopeKeysToRemove) do
|
||
|
private.db:DeleteScope("sync", scopeKey)
|
||
|
settingsDB._syncOwner[scopeKey] = nil
|
||
|
end
|
||
|
TempTable.Release(scopeKeysToRemove)
|
||
|
end
|
||
|
|
||
|
function Settings.RemoveSyncCharacter(character)
|
||
|
local settingsDB = private.context[private.db].db
|
||
|
local scopeKey = private.db:GetSyncScopeKeyByCharacter(character)
|
||
|
private.db:DeleteScope("sync", scopeKey)
|
||
|
settingsDB._syncOwner[scopeKey] = nil
|
||
|
end
|
||
|
|
||
|
function Settings.SyncSettingIterator()
|
||
|
local result = TempTable.Acquire()
|
||
|
for namespace, settings in pairs(private.context[private.db].settingsInfo.sync) do
|
||
|
for settingKey in pairs(settings) do
|
||
|
tinsert(result, namespace)
|
||
|
tinsert(result, settingKey)
|
||
|
end
|
||
|
end
|
||
|
return TempTable.Iterator(result, 2)
|
||
|
end
|
||
|
|
||
|
function Settings.FactionrealmCharacterIterator()
|
||
|
return private.db:FactionrealmCharacterIterator()
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Main SettingsDB Class
|
||
|
-- ============================================================================
|
||
|
|
||
|
local PROTECTED_TABLE_MT = {
|
||
|
__newindex = function(self, key, value)
|
||
|
assert(private.protectedAccessAllowed[self], "Attempting to modify a protected table")
|
||
|
rawset(self, key, value)
|
||
|
end,
|
||
|
__metatable = false
|
||
|
}
|
||
|
|
||
|
local SETTINGS_MT = {
|
||
|
-- getter
|
||
|
__index = function(self, key)
|
||
|
if private.SettingsDBMethods[key] then
|
||
|
return private.SettingsDBMethods[key]
|
||
|
elseif SCOPE_TYPES[key] then
|
||
|
return private.context[self].scopeProxies[key]
|
||
|
else
|
||
|
error("Invalid scope: "..tostring(key))
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
-- setter
|
||
|
__newindex = function(self, key, value)
|
||
|
error("You cannot set values in this table! You're probably missing a scope.")
|
||
|
end,
|
||
|
|
||
|
__metatable = false,
|
||
|
}
|
||
|
|
||
|
function private.Constructor(name, rawSettingsInfo)
|
||
|
assert(type(name) == "string")
|
||
|
assert(type(rawSettingsInfo) == "table")
|
||
|
local version = rawSettingsInfo.version
|
||
|
assert(type(version) == "number" and version >= 1)
|
||
|
|
||
|
-- get (and create if necessary) the global table
|
||
|
local db = _G[name]
|
||
|
if not db then
|
||
|
db = {}
|
||
|
_G[name] = db
|
||
|
end
|
||
|
|
||
|
-- flatten and validate rawSettingsInfo and generate hash data
|
||
|
local settingsInfo = CopyTable(rawSettingsInfo)
|
||
|
local hashDataParts = TempTable.Acquire()
|
||
|
local newLastModifiedVersion = TempTable.Acquire()
|
||
|
for scope, scopeSettingsInfo in pairs(rawSettingsInfo) do
|
||
|
if scope ~= "version" then
|
||
|
assert(SCOPE_TYPES[scope], "Invalid scope: "..tostring(scope))
|
||
|
for namespace, namespaceSettingsInfo in pairs(scopeSettingsInfo) do
|
||
|
assert(type(namespace) == "string" and type(namespaceSettingsInfo) == "table")
|
||
|
assert(not strfind(namespace, KEY_SEP))
|
||
|
for key, info in pairs(namespaceSettingsInfo) do
|
||
|
assert(type(key) == "string" and type(info) == "table", "Invalid type for key: "..tostring(key))
|
||
|
assert(not strfind(key, KEY_SEP))
|
||
|
for k, v in pairs(info) do
|
||
|
if k == "type" then
|
||
|
assert(VALID_TYPES[info.type], "Invalid type for key: "..key)
|
||
|
elseif k == "default" then
|
||
|
assert(type(v) == info.type, "Invalid default for key: "..key)
|
||
|
if type(v) == "table" then
|
||
|
private.CheckDefaultTable(v)
|
||
|
end
|
||
|
elseif k == "lastModifiedVersion" then
|
||
|
assert(type(v) == "number" and v <= version, "Invalid lastModifiedVersion for key: "..key)
|
||
|
newLastModifiedVersion[strjoin(KEY_SEP, SCOPE_TYPES[scope], namespace, key)] = v
|
||
|
else
|
||
|
error("Unexpected key in settingsInfo for key: "..key)
|
||
|
end
|
||
|
end
|
||
|
tinsert(hashDataParts, strjoin(",", key, scope, namespace, info.type, type(info.default) == "table" and "table" or tostring(info.default)))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
sort(hashDataParts)
|
||
|
local hash = Math.CalculateHash(table.concat(hashDataParts, ";"))
|
||
|
TempTable.Release(hashDataParts)
|
||
|
|
||
|
-- reset the DB if it's not valid
|
||
|
local isValid = true
|
||
|
if not next(db) then
|
||
|
-- new DB
|
||
|
isValid = false
|
||
|
elseif not private.ValidateDB(db) then
|
||
|
-- corrupted DB
|
||
|
assert(not TSM.IsDevVersion(), "DB is not valid!")
|
||
|
isValid = false
|
||
|
elseif db._version == version and db._hash ~= hash then
|
||
|
-- the hash didn't match
|
||
|
assert(not TSM.IsDevVersion(), "Invalid settings hash! Did you forget to increase the version?")
|
||
|
isValid = false
|
||
|
elseif db._syncOwner and db._syncOwner[SCOPE_KEYS.sync] and db._syncOwner[SCOPE_KEYS.sync] ~= db._syncAccountKey[SCOPE_KEYS.factionrealm] then
|
||
|
-- we aren't the owner of this character, so wipe the DB and show a manual error
|
||
|
Settings.ShowSyncSVCopyError()
|
||
|
assert(not TSM.IsDevVersion(), "Settings are corrupted due to manual copying of saved variables file")
|
||
|
isValid = false
|
||
|
end
|
||
|
if not isValid then
|
||
|
-- wipe the DB and start over
|
||
|
wipe(db)
|
||
|
for key, value in pairs(DEFAULT_DB) do
|
||
|
db[key] = private.CopyData(value)
|
||
|
end
|
||
|
end
|
||
|
db._hash = hash
|
||
|
|
||
|
if not db._syncOwner then
|
||
|
-- we just upgraded to the first version with the sync scope
|
||
|
db._syncOwner = {}
|
||
|
db._syncAccountKey = {}
|
||
|
db._scopeKeys.sync = {}
|
||
|
end
|
||
|
|
||
|
-- make sure we have sync account keys for every factionrealm
|
||
|
for _, factionrealm in ipairs(db._scopeKeys.factionrealm) do
|
||
|
db._syncAccountKey[factionrealm] = db._syncAccountKey[factionrealm] or strjoin(SCOPE_KEY_SEP, factionrealm, random(time()))
|
||
|
end
|
||
|
-- create the sync account key for this factionrealm if necessary
|
||
|
db._syncAccountKey[SCOPE_KEYS.factionrealm] = db._syncAccountKey[SCOPE_KEYS.factionrealm] or strjoin(SCOPE_KEY_SEP, SCOPE_KEYS.factionrealm, random(time()))
|
||
|
-- set the sync owner of the current sync scope key to this account
|
||
|
db._syncOwner[SCOPE_KEYS.sync] = db._syncOwner[SCOPE_KEYS.sync] or db._syncAccountKey[SCOPE_KEYS.factionrealm]
|
||
|
|
||
|
-- setup current scope keys and set defaults for new keys
|
||
|
db._currentProfile[SCOPE_KEYS.char] = db._currentProfile[SCOPE_KEYS.char] or DEFAULT_PROFILE_NAME
|
||
|
local currentScopeKeys = CopyTable(SCOPE_KEYS)
|
||
|
currentScopeKeys.profile = db._currentProfile[SCOPE_KEYS.char]
|
||
|
for scopeType, scopeKey in pairs(currentScopeKeys) do
|
||
|
if scopeType ~= "global" and not tContains(db._scopeKeys[scopeType], scopeKey) then
|
||
|
tinsert(db._scopeKeys[scopeType], scopeKey)
|
||
|
private.SetScopeDefaults(db, settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES[scopeType], String.Escape(scopeKey), ".+", ".+"))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- set any values which are nil to their default value
|
||
|
db._scopeKeys = db._scopeKeys or {
|
||
|
profile = {},
|
||
|
realm = {},
|
||
|
factionrealm = {},
|
||
|
char = {},
|
||
|
sync = {},
|
||
|
}
|
||
|
for scopeType, scopeKeys in pairs(db._scopeKeys) do
|
||
|
for _, scopeKey in ipairs(scopeKeys) do
|
||
|
for namespace, namespaceInfo in pairs(settingsInfo[scopeType]) do
|
||
|
for settingKey, info in pairs(namespaceInfo) do
|
||
|
local key = strjoin(KEY_SEP, SCOPE_TYPES[scopeType], scopeKey, namespace, settingKey)
|
||
|
if db[key] == nil then
|
||
|
private.SetDBKeyValue(db, key, private.CopyData(info.default))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
for namespace, namespaceInfo in pairs(settingsInfo.global) do
|
||
|
for settingKey, info in pairs(namespaceInfo) do
|
||
|
local key = strjoin(KEY_SEP, SCOPE_TYPES.global, GLOBAL_SCOPE_KEY, namespace, settingKey)
|
||
|
if db[key] == nil then
|
||
|
private.SetDBKeyValue(db, key, private.CopyData(info.default))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- do any necessary upgrading or downgrading if the version changed
|
||
|
db._lastModifiedVersion = db._lastModifiedVersion or {}
|
||
|
local removedSettings, prevVersion = nil, nil
|
||
|
if version ~= db._version then
|
||
|
-- clear any settings which no longer exist, and set new/updated settings to their default values
|
||
|
removedSettings = {}
|
||
|
for key in pairs(db) do
|
||
|
-- ignore metadata (keys starting with "_")
|
||
|
if strsub(key, 1, 1) ~= "_" then
|
||
|
local scopeTypeShort, namespace, settingKey = strmatch(key, "^(.+)"..KEY_SEP..".+"..KEY_SEP.."(.+)"..KEY_SEP.."(.+)$")
|
||
|
local settingLastModifiedVersion = scopeTypeShort and db._lastModifiedVersion[strjoin(KEY_SEP, scopeTypeShort, namespace, settingKey)]
|
||
|
local scopeType = scopeTypeShort and private.ScopeReverseLookup(scopeTypeShort)
|
||
|
local info = settingKey and settingsInfo[scopeType] and settingsInfo[scopeType][namespace] and settingsInfo[scopeType][namespace][settingKey]
|
||
|
if not info then
|
||
|
-- this setting was removed so remove it from the db
|
||
|
removedSettings[key] = db[key]
|
||
|
db[key] = nil
|
||
|
elseif info.lastModifiedVersion > db._version then
|
||
|
-- this setting was updated, so we'll reset it to the default value
|
||
|
removedSettings[key] = db[key]
|
||
|
elseif not settingLastModifiedVersion and version < db._version then
|
||
|
-- we don't have lastModifiedVersion info for this setting and the DB is getting downgraded, so we'll reset it to the default value
|
||
|
removedSettings[key] = db[key]
|
||
|
elseif (settingLastModifiedVersion or 0) > version then
|
||
|
-- this setting is being downgraded, so we'll reset it to the default value
|
||
|
removedSettings[key] = db[key]
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
for scope, scopeInfo in pairs(settingsInfo) do
|
||
|
if scope ~= "version" then
|
||
|
for namespace, namespaceInfo in pairs(scopeInfo) do
|
||
|
for settingKey, info in pairs(namespaceInfo) do
|
||
|
local settingLastModifiedVersion = db._lastModifiedVersion[strjoin(KEY_SEP, SCOPE_TYPES[scope], namespace, settingKey)]
|
||
|
if info.lastModifiedVersion > db._version or (not settingLastModifiedVersion and version < db._version) or (settingLastModifiedVersion or 0) > version then
|
||
|
-- this is either a new setting or was changed or this is a downgrade - either way set it to the default value
|
||
|
private.SetScopeDefaults(db, settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES[scope], ".+", namespace, settingKey))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if version > db._version then
|
||
|
prevVersion = db._version
|
||
|
else
|
||
|
removedSettings = nil
|
||
|
end
|
||
|
db._version = version
|
||
|
end
|
||
|
|
||
|
-- populate the new lastModifiedVersion info
|
||
|
wipe(db._lastModifiedVersion)
|
||
|
for k, v in pairs(newLastModifiedVersion) do
|
||
|
db._lastModifiedVersion[k] = v
|
||
|
end
|
||
|
TempTable.Release(newLastModifiedVersion)
|
||
|
|
||
|
-- make the db table protected
|
||
|
setmetatable(db, PROTECTED_TABLE_MT)
|
||
|
|
||
|
-- create the new object and return it
|
||
|
local new = setmetatable({}, SETTINGS_MT)
|
||
|
private.context[new] = {
|
||
|
db = db,
|
||
|
settingsInfo = settingsInfo,
|
||
|
currentScopeKeys = currentScopeKeys,
|
||
|
callbacks = {},
|
||
|
scopeProxies = {},
|
||
|
namespaceProxies = {},
|
||
|
}
|
||
|
for scopeType, scopeInfo in pairs(rawSettingsInfo) do
|
||
|
if scopeType ~= "version" then
|
||
|
for namespace in pairs(scopeInfo) do
|
||
|
private.context[new].namespaceProxies[scopeType..KEY_SEP..namespace] = private.CreateNamespace(new, namespace, scopeType)
|
||
|
end
|
||
|
private.context[new].scopeProxies[scopeType] = private.CreateScope(new, scopeType)
|
||
|
end
|
||
|
end
|
||
|
local upgradeObj = nil
|
||
|
if removedSettings then
|
||
|
upgradeObj = setmetatable({}, private.SettingsDBUpgradeObjMT)
|
||
|
assert(prevVersion)
|
||
|
private.upgradeContext = {
|
||
|
removedSettings = removedSettings,
|
||
|
prevVersion = prevVersion,
|
||
|
}
|
||
|
end
|
||
|
return new, upgradeObj
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Class for upgrade object
|
||
|
-- ============================================================================
|
||
|
|
||
|
private.SettingsDBUpgradeObjMT = {
|
||
|
-- getter
|
||
|
__index = {
|
||
|
GetPrevVersion = function(self)
|
||
|
return private.upgradeContext.prevVersion
|
||
|
end,
|
||
|
|
||
|
RemovedSettingIterator = function(self)
|
||
|
return next, private.upgradeContext.removedSettings, nil
|
||
|
end,
|
||
|
|
||
|
GetKeyInfo = function(self, key)
|
||
|
local scopeType, scopeKey, namespace, settingKey = nil, nil, nil, nil
|
||
|
local parts = TempTable.Acquire(strsplit(KEY_SEP, key))
|
||
|
if #parts == 4 then
|
||
|
scopeType, scopeKey, namespace, settingKey = TempTable.UnpackAndRelease(parts)
|
||
|
scopeType = private.ScopeReverseLookup(scopeType)
|
||
|
elseif #parts == 3 then
|
||
|
scopeType, scopeKey, settingKey = TempTable.UnpackAndRelease(parts)
|
||
|
scopeType = private.ScopeReverseLookup(scopeType)
|
||
|
else
|
||
|
error("Unknown key: "..tostring(key))
|
||
|
end
|
||
|
return scopeType, scopeKey, namespace, settingKey
|
||
|
end,
|
||
|
},
|
||
|
|
||
|
-- setter
|
||
|
__newindex = function(self)
|
||
|
error("You cannot set values in this table!")
|
||
|
end,
|
||
|
|
||
|
__metatable = false,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- SettingsDB Object Methods
|
||
|
-- ============================================================================
|
||
|
|
||
|
private.SettingsDBMethods = {
|
||
|
Get = function(self, scope, scopeKey, namespace, key)
|
||
|
assert(SCOPE_TYPES[scope] and type(namespace) == "string" and type(key) == "string", "Invalid parameters!")
|
||
|
local context = private.context[self]
|
||
|
assert(context.settingsInfo[scope][namespace][key], "Setting does not exist!")
|
||
|
scopeKey = scopeKey or context.currentScopeKeys[scope]
|
||
|
return context.db[strjoin(KEY_SEP, SCOPE_TYPES[scope], scopeKey, namespace, key)]
|
||
|
end,
|
||
|
|
||
|
Set = function(self, scope, scopeKey, namespace, key, value)
|
||
|
assert(SCOPE_TYPES[scope] and type(namespace) == "string" and type(key) == "string", "Invalid parameters!")
|
||
|
local context = private.context[self]
|
||
|
local info = context.settingsInfo[scope][namespace][key]
|
||
|
assert(info, "Setting does not exist!")
|
||
|
assert(value == nil or type(value) == info.type, "Value is of wrong type.")
|
||
|
scopeKey = scopeKey or context.currentScopeKeys[scope]
|
||
|
private.SetDBKeyValue(context.db, strjoin(KEY_SEP, SCOPE_TYPES[scope], scopeKey, namespace, key), value)
|
||
|
end,
|
||
|
|
||
|
GetDefaultReadOnly = function(self, scope, namespace, key)
|
||
|
local context = private.context[self]
|
||
|
return context.settingsInfo[scope][namespace][key].default
|
||
|
end,
|
||
|
|
||
|
GetDefault = function(self, scope, namespace, key)
|
||
|
return private.CopyData(self:GetDefaultReadOnly(scope, namespace, key))
|
||
|
end,
|
||
|
|
||
|
RegisterCallback = function(self, event, callback)
|
||
|
assert(event == "OnProfileUpdated")
|
||
|
assert(type(callback) == "function")
|
||
|
private.context[self].callbacks[event] = callback
|
||
|
end,
|
||
|
|
||
|
IsValidProfileName = function(self, name)
|
||
|
return name ~= "" and not strfind(name, KEY_SEP)
|
||
|
end,
|
||
|
|
||
|
ProfileExists = function(self, name)
|
||
|
return tContains(private.context[self].db._scopeKeys.profile, name) and true or false
|
||
|
end,
|
||
|
|
||
|
GetCurrentProfile = function(self)
|
||
|
return private.context[self].currentScopeKeys.profile
|
||
|
end,
|
||
|
|
||
|
GetScopeKeys = function(self, scope)
|
||
|
return CopyTable(private.context[self].db._scopeKeys[scope])
|
||
|
end,
|
||
|
|
||
|
GetProfiles = function(self)
|
||
|
return self:GetScopeKeys("profile")
|
||
|
end,
|
||
|
|
||
|
ProfileIterator = function(self)
|
||
|
return ipairs(private.context[self].db._scopeKeys.profile)
|
||
|
end,
|
||
|
|
||
|
SetProfile = function(self, profileName, noCallback)
|
||
|
assert(type(profileName) == "string", tostring(profileName))
|
||
|
assert(not strfind(profileName, KEY_SEP))
|
||
|
local context = private.context[self]
|
||
|
|
||
|
-- change the current profile for this character
|
||
|
context.db._currentProfile[SCOPE_KEYS.char] = profileName
|
||
|
context.currentScopeKeys.profile = context.db._currentProfile[SCOPE_KEYS.char]
|
||
|
|
||
|
local isNew = false
|
||
|
if not tContains(context.db._scopeKeys.profile, profileName) then
|
||
|
tinsert(context.db._scopeKeys.profile, profileName)
|
||
|
-- this is a new profile, so set all the settings to their default values
|
||
|
private.SetScopeDefaults(context.db, context.settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES.profile, String.Escape(profileName), ".+", ".+"))
|
||
|
isNew = true
|
||
|
end
|
||
|
|
||
|
if context.callbacks.OnProfileUpdated and not noCallback then
|
||
|
context.callbacks.OnProfileUpdated(isNew)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
ResetProfile = function(self)
|
||
|
local context = private.context[self]
|
||
|
private.SetScopeDefaults(context.db, context.settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES.profile, String.Escape(context.currentScopeKeys.profile), ".+", ".+"))
|
||
|
if context.callbacks.OnProfileUpdated then
|
||
|
context.callbacks.OnProfileUpdated(true)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
CopyProfile = function(self, sourceProfileName)
|
||
|
assert(type(sourceProfileName) == "string")
|
||
|
assert(not strfind(sourceProfileName, KEY_SEP))
|
||
|
local context = private.context[self]
|
||
|
assert(sourceProfileName ~= context.currentScopeKeys.profile)
|
||
|
|
||
|
-- copy all the settings from the source profile to the current one
|
||
|
for namespace, namespaceInfo in pairs(context.settingsInfo.profile) do
|
||
|
for settingKey in pairs(namespaceInfo) do
|
||
|
local srcKey = strjoin(KEY_SEP, SCOPE_TYPES.profile, sourceProfileName, namespace, settingKey)
|
||
|
local destKey = strjoin(KEY_SEP, SCOPE_TYPES.profile, context.currentScopeKeys.profile, namespace, settingKey)
|
||
|
private.SetDBKeyValue(context.db, destKey, private.CopyData(context.db[srcKey]))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if context.callbacks.OnProfileUpdated then
|
||
|
context.callbacks.OnProfileUpdated(false)
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
DeleteScope = function(self, scopeType, scopeKey)
|
||
|
assert(SCOPE_TYPES[scopeType])
|
||
|
assert(type(scopeKey) == "string")
|
||
|
local context = private.context[self]
|
||
|
assert(scopeKey ~= context.currentScopeKeys[scopeType])
|
||
|
|
||
|
-- remove all settings for the specified profile
|
||
|
local searchPattern = strjoin(KEY_SEP, SCOPE_TYPES[scopeType], String.Escape(scopeKey), ".+", ".+")
|
||
|
for key in pairs(context.db) do
|
||
|
if strmatch(key, searchPattern) then
|
||
|
private.SetDBKeyValue(context.db, key, nil)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- remove the scope key from the list
|
||
|
Table.RemoveByValue(context.db._scopeKeys[scopeType], scopeKey)
|
||
|
end,
|
||
|
|
||
|
DeleteProfile = function(self, profileName, defaultNewProfileName)
|
||
|
self:DeleteScope("profile", profileName)
|
||
|
-- move other characters which were on this profile to another one
|
||
|
local context = private.context[self]
|
||
|
if not defaultNewProfileName then
|
||
|
defaultNewProfileName = context.db._scopeKeys.profile[1]
|
||
|
end
|
||
|
assert(defaultNewProfileName and tContains(context.db._scopeKeys.profile, defaultNewProfileName))
|
||
|
for character, currentProfileName in pairs(context.db._currentProfile) do
|
||
|
if currentProfileName == profileName then
|
||
|
assert(character ~= SCOPE_KEYS.char)
|
||
|
context.db._currentProfile[character] = defaultNewProfileName
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
|
||
|
GetConnectedRealmIterator = function(self, scope)
|
||
|
assert(scope == "factionrealm" or scope == "realm")
|
||
|
return private.ConnectedRealmIterator, self, scope
|
||
|
end,
|
||
|
|
||
|
GetSyncAccountKey = function(self, factionrealm)
|
||
|
factionrealm = factionrealm or SCOPE_KEYS.factionrealm
|
||
|
return private.context[self].db._syncAccountKey[factionrealm]
|
||
|
end,
|
||
|
|
||
|
SyncAccountIterator = function(self)
|
||
|
local result = TempTable.Acquire()
|
||
|
local used = TempTable.Acquire()
|
||
|
for _, syncOwner in pairs(private.context[self].db._syncOwner) do
|
||
|
if strmatch(syncOwner, "^"..String.Escape(SCOPE_KEYS.factionrealm..SCOPE_KEY_SEP).."(%d+)$") and not used[syncOwner] and syncOwner ~= self:GetSyncAccountKey() then
|
||
|
used[syncOwner] = true
|
||
|
tinsert(result, syncOwner)
|
||
|
end
|
||
|
end
|
||
|
TempTable.Release(used)
|
||
|
return TempTable.Iterator(result)
|
||
|
end,
|
||
|
|
||
|
NewSyncCharacter = function(self, character, accountKey, factionrealm)
|
||
|
factionrealm = factionrealm or SCOPE_KEYS.factionrealm
|
||
|
assert(strmatch(accountKey, "^"..String.Escape(factionrealm..SCOPE_KEY_SEP).."(%d+)$"), "Invalid account key")
|
||
|
local scopeKey = self:GetSyncScopeKeyByCharacter(character, factionrealm)
|
||
|
local context = private.context[self]
|
||
|
context.db._syncOwner[scopeKey] = accountKey
|
||
|
if not tContains(context.db._scopeKeys.sync, scopeKey) then
|
||
|
tinsert(context.db._scopeKeys.sync, scopeKey)
|
||
|
end
|
||
|
private.SetScopeDefaults(context.db, context.settingsInfo, strjoin(KEY_SEP, SCOPE_TYPES.sync, String.Escape(scopeKey), ".+", ".+"))
|
||
|
end,
|
||
|
|
||
|
RemoveSyncAccount = function(self, accountKey)
|
||
|
local settingsDB = private.context[self].db
|
||
|
assert(accountKey ~= self:GetSyncAccountKey())
|
||
|
local scopeKeysToRemove = TempTable.Acquire()
|
||
|
for scopeKey, ownerAccountKey in pairs(settingsDB._syncOwner) do
|
||
|
if ownerAccountKey == accountKey then
|
||
|
tinsert(scopeKeysToRemove, scopeKey)
|
||
|
end
|
||
|
end
|
||
|
for _, scopeKey in ipairs(scopeKeysToRemove) do
|
||
|
self:DeleteScope("sync", scopeKey)
|
||
|
settingsDB._syncOwner[scopeKey] = nil
|
||
|
end
|
||
|
TempTable.Release(scopeKeysToRemove)
|
||
|
end,
|
||
|
|
||
|
RemoveSyncCharacter = function(self, character)
|
||
|
local settingsDB = private.context[self].db
|
||
|
local scopeKey = self:GetSyncScopeKeyByCharacter(character)
|
||
|
self:DeleteScope("sync", scopeKey)
|
||
|
settingsDB._syncOwner[scopeKey] = nil
|
||
|
end,
|
||
|
|
||
|
GetSyncOwnerAccountKey = function(self, character)
|
||
|
return private.context[self].db._syncOwner[self:GetSyncScopeKeyByCharacter(character)]
|
||
|
end,
|
||
|
|
||
|
FactionrealmCharacterIterator = function(self, factionrealm)
|
||
|
factionrealm = factionrealm or SCOPE_KEYS.factionrealm
|
||
|
local result = TempTable.Acquire()
|
||
|
for scopeKey in pairs(private.context[self].db._syncOwner) do
|
||
|
local character = strmatch(scopeKey, "^(.+)"..String.Escape(SCOPE_KEY_SEP..factionrealm))
|
||
|
if character then
|
||
|
tinsert(result, character)
|
||
|
end
|
||
|
end
|
||
|
return TempTable.Iterator(result)
|
||
|
end,
|
||
|
|
||
|
GetSyncScopeKeyByCharacter = function(self, character, factionrealm)
|
||
|
return character..SCOPE_KEY_SEP..(factionrealm or SCOPE_KEYS.factionrealm)
|
||
|
end,
|
||
|
|
||
|
FactionrealmByRealmIterator = function(self, realm)
|
||
|
return private.FactionrealmByRealmIteratorHelper, realm
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Proxy Class for Scopes (TSM.db.XXXXX)
|
||
|
-- ============================================================================
|
||
|
|
||
|
local SCOPE_MT = {
|
||
|
-- getter
|
||
|
__index = function(self, namespace)
|
||
|
assert(type(namespace) == "string", "Invalid namespace type!")
|
||
|
local proxyInfo = private.proxies[self]
|
||
|
local context = private.context[proxyInfo.settingsDB]
|
||
|
assert(context.settingsInfo[proxyInfo.scope][namespace], "Namespace does not exist!")
|
||
|
local namespaceProxy = context.namespaceProxies[proxyInfo.scope..KEY_SEP..namespace]
|
||
|
assert(namespaceProxy)
|
||
|
return namespaceProxy
|
||
|
end,
|
||
|
|
||
|
-- setter
|
||
|
__newindex = function(self, key, value)
|
||
|
error("You cannot set values in this table! You're probably missing a namespace.")
|
||
|
end,
|
||
|
|
||
|
__metatable = false,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Proxy Class for Namespaces (TSM.db.<scope>.XXXXX)
|
||
|
-- ============================================================================
|
||
|
|
||
|
local NAMESPACE_MT = {
|
||
|
-- getter
|
||
|
__index = function(self, key)
|
||
|
assert(type(key) == "string", "Invalid setting key type!")
|
||
|
local proxyInfo = private.proxies[self]
|
||
|
return proxyInfo.settingsDB:Get(proxyInfo.scope, nil, proxyInfo.namespace, key)
|
||
|
end,
|
||
|
|
||
|
-- setter
|
||
|
__newindex = function(self, key, value)
|
||
|
local proxyInfo = private.proxies[self]
|
||
|
proxyInfo.settingsDB:Set(proxyInfo.scope, nil, proxyInfo.namespace, key, value)
|
||
|
end,
|
||
|
|
||
|
__metatable = false,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Setting View Class (see Settings.CreateView(...))
|
||
|
-- ============================================================================
|
||
|
|
||
|
local VIEW_METHODS = {
|
||
|
AddKey = function(self, scopeType, namespace, key)
|
||
|
local viewInfo = private.views[self]
|
||
|
assert(viewInfo and not viewInfo.keyProxies[key])
|
||
|
viewInfo.scopeNamespace[key] = scopeType..KEY_SEP..namespace
|
||
|
viewInfo.keyProxies[key] = private.context[viewInfo.settingsDB].namespaceProxies[viewInfo.scopeNamespace[key]]
|
||
|
return self
|
||
|
end,
|
||
|
RegisterCallback = function(self, key, callback)
|
||
|
local viewInfo = private.views[self]
|
||
|
assert(callback and not viewInfo.callbacks[key])
|
||
|
viewInfo.callbacks[key] = callback
|
||
|
return self
|
||
|
end,
|
||
|
GetDefaultReadOnly = function(self, key)
|
||
|
local viewInfo = private.views[self]
|
||
|
local scope, namespace = strsplit(KEY_SEP, viewInfo.scopeNamespace[key])
|
||
|
assert(scope and namespace)
|
||
|
return viewInfo.settingsDB:GetDefaultReadOnly(scope, namespace, key)
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
local VIEW_MT = {
|
||
|
__index = function(self, key)
|
||
|
if VIEW_METHODS[key] then
|
||
|
return VIEW_METHODS[key]
|
||
|
end
|
||
|
return private.views[self].keyProxies[key][key]
|
||
|
end,
|
||
|
__newindex = function(self, key, value)
|
||
|
private.views[self].keyProxies[key][key] = value
|
||
|
end,
|
||
|
__metatable = false,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
-- ============================================================================
|
||
|
-- Helper Functions
|
||
|
-- ============================================================================
|
||
|
|
||
|
function private.CheckDefaultTable(tbl)
|
||
|
for k, v in pairs(tbl) do
|
||
|
assert(type(k) == "string" or type(k) == "number")
|
||
|
if type(v) == "table" then
|
||
|
private.CheckDefaultTable(v)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.CreateScope(settingsDB, scope)
|
||
|
assert(private.context[settingsDB])
|
||
|
local new = setmetatable({}, SCOPE_MT)
|
||
|
private.proxies[new] = {
|
||
|
settingsDB = settingsDB,
|
||
|
scope = scope,
|
||
|
}
|
||
|
return new
|
||
|
end
|
||
|
|
||
|
function private.CreateNamespace(settingsDB, namespace, scope)
|
||
|
assert(private.context[settingsDB])
|
||
|
local new = setmetatable({}, NAMESPACE_MT)
|
||
|
private.proxies[new] = {
|
||
|
settingsDB = settingsDB,
|
||
|
namespace = namespace,
|
||
|
scope = scope,
|
||
|
}
|
||
|
return new
|
||
|
end
|
||
|
|
||
|
function private.CreateView(settingsDB)
|
||
|
assert(private.context[settingsDB])
|
||
|
local view = setmetatable({}, VIEW_MT)
|
||
|
private.views[view] = {
|
||
|
settingsDB = settingsDB,
|
||
|
keyProxies = {},
|
||
|
scopeNamespace = {},
|
||
|
callbacks = {},
|
||
|
}
|
||
|
return view
|
||
|
end
|
||
|
|
||
|
function private.SetDBKeyValue(db, key, value)
|
||
|
private.protectedAccessAllowed[db] = true
|
||
|
db[key] = value
|
||
|
private.protectedAccessAllowed[db] = nil
|
||
|
local scopeType, _, namespace, settingKey = strsplit(KEY_SEP, key)
|
||
|
if not settingKey then
|
||
|
return
|
||
|
end
|
||
|
scopeType = private.ScopeReverseLookup(scopeType)
|
||
|
for _, info in pairs(private.views) do
|
||
|
if info.callbacks[settingKey] and info.scopeNamespace[settingKey] == scopeType..KEY_SEP..namespace then
|
||
|
info.callbacks[settingKey]()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.CopyData(data)
|
||
|
if type(data) == "table" then
|
||
|
return CopyTable(data)
|
||
|
elseif VALID_TYPES[type(data)] or type(data) == nil then
|
||
|
return data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.ScopeReverseLookup(scopeTypeShort)
|
||
|
for key, value in pairs(SCOPE_TYPES) do
|
||
|
if value == scopeTypeShort then
|
||
|
return key
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.ValidateDB(db)
|
||
|
-- make sure the DB we are loading from is valid
|
||
|
if #db > 0 then return end
|
||
|
if type(db._version) ~= "number" then return end
|
||
|
if type(db._hash) ~= "number" then return end
|
||
|
if db._lastModifiedVersion ~= nil and type(db._lastModifiedVersion) ~= "table" then return end
|
||
|
if type(db._scopeKeys) ~= "table" then return end
|
||
|
for scopeType, keys in pairs(db._scopeKeys) do
|
||
|
if not SCOPE_TYPES[scopeType] then return end
|
||
|
for i, name in pairs(keys) do
|
||
|
if type(i) ~= "number" or i > #keys or i <= 0 or type(name) ~= "string" then return end
|
||
|
end
|
||
|
end
|
||
|
if type(db._currentProfile) ~= "table" then return end
|
||
|
for key, value in pairs(db._currentProfile) do
|
||
|
if type(key) ~= "string" or type(value) ~= "string" then return end
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function private.SetScopeDefaults(db, settingsInfo, searchPattern)
|
||
|
-- remove any existing entries for matching keys
|
||
|
for key in pairs(db) do
|
||
|
if strmatch(key, searchPattern) then
|
||
|
private.SetDBKeyValue(db, key, nil)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local scopeTypeShort = strsub(searchPattern, 1, 1)
|
||
|
local scopeType = private.ScopeReverseLookup(scopeTypeShort)
|
||
|
assert(scopeType, "Couldn't find scopeType: "..tostring(scopeTypeShort))
|
||
|
local scopeKeys = nil
|
||
|
if scopeTypeShort == SCOPE_TYPES.global then
|
||
|
scopeKeys = {GLOBAL_SCOPE_KEY}
|
||
|
else
|
||
|
scopeKeys = db._scopeKeys[scopeType]
|
||
|
assert(scopeKeys, "Couldn't find scopeKeys for type: "..tostring(scopeTypeShort))
|
||
|
end
|
||
|
|
||
|
-- set any matching keys to their default values
|
||
|
if not settingsInfo[scopeType] then return end
|
||
|
for namespace, namespaceInfo in pairs(settingsInfo[scopeType]) do
|
||
|
for settingKey, info in pairs(namespaceInfo) do
|
||
|
for _, scopeKey in ipairs(scopeKeys) do
|
||
|
local key = strjoin(KEY_SEP, scopeTypeShort, scopeKey, namespace, settingKey)
|
||
|
if strmatch(key, searchPattern) then
|
||
|
private.SetDBKeyValue(db, key, private.CopyData(info.default))
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.ConnectedRealmIterator(self, prevScopeKey)
|
||
|
if not private.cachedConnectedRealms then
|
||
|
local connectedRealms = {}
|
||
|
if not TSM.IsWowClassic() then
|
||
|
local realmId, _, _, _, _, _, _, _, connectedRealmIds = LibRealmInfo:GetRealmInfo(REALM)
|
||
|
if connectedRealmIds then
|
||
|
for _, id in ipairs(connectedRealmIds) do
|
||
|
if id ~= realmId then
|
||
|
local _, connectedRealmName = LibRealmInfo:GetRealmInfoByID(id)
|
||
|
tinsert(connectedRealms, connectedRealmName)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
private.cachedConnectedRealms = connectedRealms
|
||
|
end
|
||
|
local scope = nil
|
||
|
if prevScopeKey == "factionrealm" or prevScopeKey == "realm" then
|
||
|
-- this is the first time
|
||
|
scope = prevScopeKey
|
||
|
prevScopeKey = nil
|
||
|
else
|
||
|
scope = strmatch(prevScopeKey, String.Escape(FACTION.." - ")) and "factionrealm" or "realm"
|
||
|
end
|
||
|
local foundPrev = prevScopeKey == nil
|
||
|
local index = 0
|
||
|
while true do
|
||
|
local realm = index == 0 and SCOPE_KEYS.realm or private.cachedConnectedRealms[index]
|
||
|
if not realm then return end
|
||
|
index = index + 1
|
||
|
local scopeKey = (scope == "factionrealm") and (FACTION..SCOPE_KEY_SEP..realm) or realm
|
||
|
if scopeKey == prevScopeKey then
|
||
|
foundPrev = true
|
||
|
elseif foundPrev and tContains(private.context[self].db._scopeKeys[scope], scopeKey) then
|
||
|
return scopeKey
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function private.FactionrealmByRealmIteratorHelper(realm, prevValue)
|
||
|
if not prevValue then
|
||
|
return strjoin(SCOPE_KEY_SEP, "Horde", realm)
|
||
|
elseif strmatch(prevValue, "^Horde") then
|
||
|
return strjoin(SCOPE_KEY_SEP, "Alliance", realm)
|
||
|
elseif strmatch(prevValue, "^Alliance") then
|
||
|
return strjoin(SCOPE_KEY_SEP, "Neutral", realm)
|
||
|
end
|
||
|
end
|