--[[ Gatherer Addon for World of Warcraft(tm). Version: 3.1.16 (<%codename%>) Revision: $Id: GatherStorage.lua 876 2010-09-18 23:41:23Z Esamynn $ License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program(see GPL.txt); if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Note: This AddOn's source code is specifically designed to work with World of Warcraft's interpreted AddOn system. You have an implicit licence to use this AddOn with these facilities since that is it's designated purpose as per: http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat Library for accessing and updating the database --]] Gatherer_RegisterRevision("$URL: http://svn.norganna.org/gatherer/trunk/Gatherer/GatherStorage.lua $", "$Rev: 876 $") -------------------------------------------------------------------------- -- Constants -------------------------------------------------------------------------- -- Node Indexing local POS_X = 1 local POS_Y = 2 local COUNT = 3 local HARVESTED = 4 local INSPECTED = 5 local SOURCE = 6 -- Current Database Version local dbVersion = 3 -------------------------------------------------------------------------- -- Data Table -------------------------------------------------------------------------- local globalName = "GatherItems" local data local corruptData = false -------------------------------------------------------------------------- -- Global Library Table with a local pointer -------------------------------------------------------------------------- local lib = Gatherer.Storage -- reference to the Astrolabe mapping library local Astrolabe = DongleStub(Gatherer.AstrolabeVersion) local ZoneData = {} local continents = {GetMapContinents()} for index, name in ipairs(continents) do ZoneData[index] = {GetMapZones(index)} ZoneData[index].name = name end --[[ ########################################################################## Regular Library Functions ########################################################################## --]] --************************************************************************ -- This returns the raw data table, BE CAREFUL WITH IT!!!! --************************************************************************ --[[ function lib.GetRawDataTable() return data end --]] local function processSourceList( newSource, ... ) for i = 1, select("#", ...) do if ( newSource == select(i, ...) ) then return ... end end return ..., newSource end local validGatherTypes = { MINE = "MINE", HERB = "HERB", OPEN = "OPEN", } function lib.AddNode(nodeName, gatherType, continent, zone, gatherX, gatherY, source, incrementCount) if not (continent and zone and gatherX and gatherY) then return end local zoneToken = Gatherer.ZoneTokens.GetZoneToken(continent, zone) zone = Gatherer.ZoneTokens.GetZoneIndex(continent, zone) -- check for invalid location information -- ccox - we should handle negative X and Y, see gatherer ticket #139 -- Swamp of Sorrows has stranglekelp at a negative Y position (northeast corner, off in the water) if not ( (continent > 0) and zoneToken and (gatherX > 0) and (gatherY > 0) ) then return end local gatherType = validGatherTypes[gatherType] if not (data[continent]) then data[continent] = { }; end if not (data[continent][zoneToken]) then data[continent][zoneToken] = { }; end if not (data[continent][zoneToken][nodeName]) then data[continent][zoneToken][nodeName] = { gtype = gatherType }; end local gatherTable = data[continent][zoneToken][nodeName] if not ( gatherType ) then gatherType = Gatherer.Nodes.Objects[nodeName] end if not ( gatherTable.gtype ) then gatherTable.gtype = gatherType end local matchDist = 10 local isImport = false if source and source:sub(1,3) == "DB:" then -- DB sources have a tendancy to be more "fuzzy" than -- actual harvested nodes, so look farther for a match matchDist = 25 isImport = true elseif source == "REQUIRE" then -- REQUIRE nodes can be harvested a little farther away -- than a directly harvested node, so search a little -- bit more for a match matchDist = 15 end local index, node for i, gatherData in ipairs(gatherTable) do local dist = Astrolabe:ComputeDistance(continent, zone, gatherX, gatherY, continent, zone, gatherData[POS_X], gatherData[POS_Y]) if ( dist < matchDist ) then node = gatherData index = i break end end -- If we found a close, matching node, then proceed to update it. if node then -- But don't allow imports to affect real gathered nodes. if isImport then return end local count = node[COUNT] -- Do a proper average of the node position gatherX = (gatherX + (node[POS_X] * count)) / (count + 1) gatherY = (gatherY + (node[POS_Y] * count)) / (count + 1) -- Update the node's source field local nodeSource = node[SOURCE] if ( nodeSource ) then -- If we got this node from someone else if ( source ) then -- If the node is imported, but wasn't or vice versa, clear current source if ( (source == "IMPORTED" or nodeSource == "IMPORTED") and nodeSource ~= source ) then node[SOURCE] = nil -- If the node is require-level, but wasn't or vice versa, clear current source elseif ( (source == "REQUIRE" or nodeSource == "REQUIRE") and nodeSource ~= source ) then node[SOURCE] = nil -- Otherwise add the new source to the current source else node[SOURCE] = string.join(",", processSourceList(source, string.split(",", nodeSource))) end -- Else, we have just personally verified the node as correct! Yay us! else node[SOURCE] = nil end end -- Else, we didn't find it in the current list, time to create a new node! else node = { [POS_X]=0, [POS_Y]=0, [COUNT]=0, [HARVESTED]=0, [INSPECTED]=0, [SOURCE]=source } table.insert(gatherTable, node) index = table.getn(gatherTable) end node[POS_X] = gatherX node[POS_Y] = gatherY if ( incrementCount ) then node[COUNT] = node[COUNT] + 1 end local now = time() -- Update last harvested time (and inspected time as well) node[HARVESTED] = now if (not node[SOURCE]) then node[INSPECTED] = now end -- Notify the reporting subsystem that something has changed Gatherer.Report.NeedsUpdate() -- Return the indexed position return index end --************************************************************************ -- Node Removal --************************************************************************ function lib.ClearDatabase() data = { dbVersion = dbVersion } collectgarbage("collect"); --reclaim the old database -- Notify the reporting subsystem that something has changed Gatherer.Report.NeedsUpdate() end local function removeNode( gatherData, index, playerName ) local remove = true if ( playerName ) then local nodeData = gatherData[index] if ( nodeData[SOURCE] ) then local newSource = (nodeData[SOURCE]..","):gsub(playerName..",", ""):sub(1, -2) if ( newSource ~= "" ) then -- don't remove the node if source string is not empty after removing the specified name remove = false nodeData[SOURCE] = newSource end else -- don't remove the node if a name was specified, but the node is "confirmed" remove = false end end if ( remove ) then table.remove(gatherData, index) end -- Notify the reporting subsystem that something has changed Gatherer.Report.NeedsUpdate() return remove end -- returns true if the node was removed function lib.RemoveNode( continent, zone, gatherId, index, playerName ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherId) ) then local gatherData = data[continent][zone][gatherId] if ( gatherData[index] ) then local result = removeNode(gatherData, index, playerName) if not ( gatherData[1] ) then -- if the gather table is now empty, remove it from the DB table lib.RemoveGather(continent, zone, gatherId) end return result end end end -- Returns: -- -2 if the gather did not exist -- -1 if no nodes were removed -- 0 if the gather was removed from the zone -- 1 if some, but not all, of the nodes in the gather were removed function lib.RemoveGather( continent, zone, gatherId, playerName ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherId) ) then local numRemoved = 0 if ( playerName ) then local gatherData = data[continent][zone][gatherId] local numNodes = #gatherData for i = numNodes, 1, -1 do removeNode(gatherData, i, playerName) end if ( gatherData[1] ) then if ( numNodes > #gatherData ) then return 1, (numNodes - #gatherData) else return -1, 0 end else lib.RemoveGather(continent, zone, gatherId) return 0, numNodes end else -- if no name was specified, remove the gather table and check for empty ancestors local numGathers = #(data[continent][zone][gatherId]) data[continent][zone][gatherId] = nil if not ( pairs(data[continent][zone]) ) then data[continent][zone] = nil end if not ( pairs(data[continent]) ) then data[continent] = nil end return 0, numGathers end end return -2, 0 end --************************************************************************ -- Node Information --************************************************************************ function lib.HasDataOnZone( continent, zone ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.HasDataOnContinent(continent) and data[continent][zone] ) then return true else return false end end function lib.HasDataOnContinent( continent ) if ( data[continent] ) then return true else return false end end function lib.IsGatherInZone( continent, zone, gatherName ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.HasDataOnZone(continent, zone) and data[continent][zone][gatherName] ) then return true else return false end end -- Returns 2 values -- 1) the number of nodes in a zone -- 2) the total of the count values for all nodes in the zone -------------------------------------------------------------------------- function lib.GetNodeCounts( continent, zone ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) local countTotal = 0 local nodeCount = 0 if ( data[continent] and data[continent][zone] ) then for gather, nodes in pairs(data[continent][zone]) do for key, node in pairs(nodes) do if ( key ~= "gtype" and key ~= "icon" ) then countTotal = countTotal + node[COUNT] nodeCount = nodeCount + 1 end end end end return nodeCount, countTotal end -- Returns the number of nodes of the given gather name in the specified zone -------------------------------------------------------------------------- function lib.GetGatherCountsForZone( continent, zone, gatherName ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( data[continent] and data[continent][zone] and data[continent][zone][gatherName] ) then return table.getn(data[continent][zone][gatherName]) else return 0 end end -- Returns the count of nodes for each "Gather Type" in the zone specified -- the return order is -------------------------------------------------------------------------- local nodeCountsByType = { OPEN=0, HERB=0, MINE=0, unknown=0, } function lib.GetNodeCountsByGatherType( continent, zone ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) for k, v in pairs(nodeCountsByType) do nodeCountsByType[k] = 0 end if ( lib.HasDataOnZone(continent, zone) ) then for gather, nodes in pairs(data[continent][zone]) do local gtype = nodes.gtype if ( nodeCountsByType[gtype] ) then nodeCountsByType[gtype] = nodeCountsByType[gtype] + table.getn(nodes) else nodeCountsByType.unknown = nodeCountsByType.unknown + table.getn(nodes) end end end return nodeCountsByType.OPEN, nodeCountsByType.HERB, nodeCountsByType.MINE, nodeCountsByType.unknown end -- Returns information on a specific node -- -- Return Values: -- x - the node's x coordinate value -- y - the node's y coordinate value -- count - the node's count value -- gtype - gather type of this node -- lastHarvested - time at which the node was last harvested -- lastInspected - time at which the node was last inspected -- source - the source of this node -------------------------------------------------------------------------- function lib.GetNodeInfo( continent, zone, gatherName, index ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherName) ) then local info = data[continent][zone][gatherName][index] if ( info ) then return info[POS_X], info[POS_Y], info[COUNT], data[continent][zone][gatherName].gtype, info[HARVESTED] or 0, info[INSPECTED] or 0, info[SOURCE] end end end function lib.SetNodeInspected( continent, zone, nodeid, index ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, nodeid) ) then local node = data[continent][zone][nodeid][index] if ( node ) then node[INSPECTED] = time() end end end function lib.GetNodeInspected( continent, zone, nodeid, index ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, nodeid) ) then local node = data[continent][zone][nodeid][index] if ( node ) then return node[INSPECTED] end end end function lib.SetNodeBuggedState( continent, zone, gatherName, index, bugged ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherName) ) then local node = data[continent][zone][gatherName][index] if ( node ) then if ( bugged == nil ) then if ( node.bugged ) then bugged = nil else bugged = true end end node.bugged = true return bugged end end end function lib.IsNodeBugged( continent, zone, gatherName, index ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherName) ) then local node = data[continent][zone][gatherName][index] if ( node ) then if ( node.bugged ) then return true else return false end end end end --[[ ########################################################################## Iterators ########################################################################## --]] local EmptyIterator = function() end local iteratorStateTables = {} setmetatable(iteratorStateTables, { __mode = "k" }); --weak keys -------------------------------------------------------------------------- -- iterator work table cache -------------------------------------------------------------------------- local workTableCache = { {}, {}, {}, {}, }; -- initial size of 4 tables local function getWorkTablePair() if ( table.getn(workTableCache) < 2 ) then table.insert(workTableCache, {}) table.insert(workTableCache, {}) end local index = table.remove(workTableCache) local state = table.remove(workTableCache) iteratorStateTables[index] = state return index, state end local function releaseWorkTablePair( index ) local data = iteratorStateTables[index] if ( data ) then iteratorStateTables[index] = nil for k, v in pairs(index) do index[k] = nil end for k, v in pairs(data) do data[k] = nil end table.insert(workTableCache, index) table.insert(workTableCache, data) end end local function getWorkTable() if ( table.getn(workTableCache) < 1 ) then table.insert(workTableCache, {}) end local workTable = table.remove(workTableCache) iteratorStateTables[workTable] = false return workTable end local function releaseWorkTable( workTable ) if ( iteratorStateTables[workTable] == false ) then iteratorStateTables[workTable] = nil for k, v in pairs(workTable) do workTable[k] = nil end table.insert(workTableCache, workTable) end end -- Iterates over the contienent or the zones of a continent and returns -- the indices for which Gatherer has data -------------------------------------------------------------------------- do --create a new block local function iterator( iteratorData, lastIndex ) if not ( iteratorData and lastIndex ) then return end --not enough information lastIndex = lastIndex + 1 if ( iteratorData[lastIndex] ) then return lastIndex, iteratorData[lastIndex] else releaseWorkTable(iteratorData) return; --no data left end end function lib.GetAreaIndices( continent ) local dataTable if ( continent and lib.HasDataOnContinent(continent) ) then dataTable = data[continent] else dataTable = data end if not ( dataTable ) then return EmptyIterator; end -- no data local iteratorData = getWorkTable() if ( continent ) then local GetZoneIndex = Gatherer.ZoneTokens.GetZoneIndex for i in pairs(dataTable) do if ( lib.HasDataOnZone(continent,i) ) then tinsert(iteratorData, GetZoneIndex(continent, i)) end end else for i in pairs(dataTable) do if (type(i) == "number") and (lib.HasDataOnContinent(i)) then tinsert(iteratorData, i) end end end table.sort(iteratorData) return iterator, iteratorData, 0 end end -- end the block -- Iterates over the node types in a zone returning data on each type -- The interator returns the following data on each node -- gatherName - loot name -- gType - Gather type -- num - number of nodes of that type -------------------------------------------------------------------------- do --create a new block local function iterator( stateIndex, lastGatherName ) local state = iteratorStateTables[stateIndex] if not ( state ) then return end; --no data left local gatherName, gatherNodesTable = next(state.table, lastGatherName) if not ( gatherName ) then releaseWorkTablePair(stateIndex) return; --no data left end local gtype = gatherNodesTable.gtype return gatherName, gtype, table.getn(gatherNodesTable) end function lib.ZoneGatherNames( continent, zone ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.HasDataOnZone(continent, zone) ) then local stateIndex, state = getWorkTablePair() state.table = data[continent][zone] return iterator, stateIndex, nil end --safety return EmptyIterator end end -- end the block -- Iterates over the nodes of a specific type in a zone -- The interator returns the following data on each node -- -- index - for direct access to this node's information -- x - the node's x coordinate value -- y - the node's y coordinate value -- count - the node's count value -- harvested - the last time the node was harvested -- inspected - the last time the node was inspected -- the node's source -------------------------------------------------------------------------- do --create a new block local function iterator( stateIndex, lastNodeIndex ) local state = iteratorStateTables[stateIndex] if not ( state ) then return end; --no data left local nodeIndex, info = state.iterator(state.stateInfo, lastNodeIndex) if not ( info ) then releaseWorkTablePair(stateIndex) return; --no data left end return nodeIndex, info[POS_X], info[POS_Y], info[COUNT], info[HARVESTED], info[INSPECTED], info[SOURCE] end function lib.ZoneGatherNodes( continent, zone, gatherName ) zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) if ( lib.IsGatherInZone(continent, zone, gatherName) ) then local stateIndex, state = getWorkTablePair() state.iterator, state.stateInfo = ipairs(data[continent][zone][gatherName]) return iterator, stateIndex, 0 end --safety return EmptyIterator end end -- end the block -- Closest Nodes -- Returns an iterator -- Iterator returns: id, continent, zone, nodeId, nodeIndex, distance -------------------------------------------------------------------------- do --create a new block local function iterator( iteratorData, lastIndex ) if not ( iteratorData and lastIndex ) then return end --not enough information lastIndex = lastIndex + 1 local nodeIndex = lastIndex * 3 if ( iteratorData[nodeIndex] ) then return lastIndex, iteratorData.continent, iteratorData.zone, iteratorData[nodeIndex - 2], iteratorData[nodeIndex - 1], iteratorData[nodeIndex] else releaseWorkTable(iteratorData) return; --no data left end end -- working tables local nodeNames = {} local nodeIndex = {} local distances = {} function lib.ClosestNodes( continent, zone, xPos, yPos, num, maxDist, filter ) local zoneToken = Gatherer.ZoneTokens.GetZoneToken(continent, zone) -- return if the position is invalid or we have no data on the specified zone if not ( lib.HasDataOnZone(continent, zone) and xPos > 0 and yPos > 0 ) then return EmptyIterator end local iteratorData = getWorkTable() iteratorData.continent = continent iteratorData.zone = zone if ( type(filter) == "function" ) then --do nothing elseif ( type(filter) == "table" ) then local filterTable = filter filter = ( function( nodeId, gatherType ) if not ( filterTable[gatherType] ) then return false elseif ( filterTable[gatherType] == true ) then return true else return filterTable[gatherType][nodeId] end end ) elseif ( filter == nil or filter ) then filter = true else return EmptyIterator end for i in ipairs(nodeNames) do nodeNames[i] = nil nodeIndex[i] = nil distances[i] = nil end local zoneData = data[continent][zoneToken] xPos = xPos yPos = yPos for gatherName, nodesList in pairs(zoneData) do if ( (filter == true) or filter(gatherName, nodesList.gtype) ) then for index, nodeData in ipairs(nodesList) do if not ( lib.IsNodeBugged(continent, zone, gatherName, index) ) then local nodeX, nodeY = nodeData[POS_X], nodeData[POS_Y] if ( (nodeX ~= 0) and (nodeY ~= 0) ) then local dist = Astrolabe:ComputeDistance(continent, zone, xPos, yPos, continent, zone, nodeX, nodeY) if ( (maxDist == 0) or (dist < maxDist) ) then local insertPoint = 1 for i, nodeName in ipairs(nodeNames) do if not ( distances[i+1] ) then insertPoint = i + 1 break elseif ( distances[i] > dist ) then insertPoint = i break end end if ( insertPoint <= num) then tinsert(nodeNames, insertPoint, gatherName) tinsert(nodeIndex, insertPoint, index) tinsert(distances, insertPoint, dist) local limit = num + 1 nodeNames[limit] = nil nodeIndex[limit] = nil distances[limit] = nil end end end end end end end for i, nodeName in ipairs(nodeNames) do local dist = math.floor(distances[i]*100)/100 tinsert(iteratorData, nodeName) tinsert(iteratorData, nodeIndex[i]) tinsert(iteratorData, dist) end return iterator, iteratorData, 0 end end -- end the block -- Closest Nodes Info -- Returns an iterator -- Iterator returns: id, continent, zone, nodeId, nodeIndex, distance, +GetNodeInfo() -------------------------------------------------------------------------- do --create a new block local function iterator( iteratorData, lastIndex ) if not ( iteratorData and lastIndex ) then return end --not enough information lastIndex = lastIndex + 1 local nodeIndex = lastIndex * 3 if ( iteratorData[nodeIndex] ) then local continent, zone, nodeName, index, dist = iteratorData.continent, iteratorData.zone, iteratorData[nodeIndex - 2], iteratorData[nodeIndex - 1], iteratorData[nodeIndex] return lastIndex, continent, zone, nodeName, index, dist, lib.GetNodeInfo(continent, zone, nodeName, index) else releaseWorkTable(iteratorData) return; --no data left end end function lib.ClosestNodesInfo( continent, zone, xPos, yPos, num, maxDist, filter ) local f, iteratorData, var = lib.ClosestNodes(continent, zone, xPos, yPos, num, maxDist, filter) if ( f == EmptyIterator ) then return f else return iterator, iteratorData, var end end end -- end the block -------------------------------------------------------------------------- -- Event Frame to import/export the data table from/to the global -- namespace when appropriate -------------------------------------------------------------------------- local eventFrame = CreateFrame("Frame") eventFrame:RegisterEvent("ADDON_LOADED") eventFrame:RegisterEvent("PLAYER_LOGIN") eventFrame:RegisterEvent("PLAYER_LOGOUT") eventFrame.UnregisterEvent = function() end eventFrame:SetScript("OnEvent", function( frame, event, arg1 ) if ( event == "ADDON_LOADED" and strlower(arg1) == "gatherer" ) then local savedData = _G[globalName] if ( savedData ) then getfenv(0)[globalName] = nil if ( savedData.dbVersion == nil ) then --old, unversioned Database savedData.dbVersion = 0 end if ( type(savedData.dbVersion) == "number" ) then if ( dbVersion == savedData.dbVersion ) then --database is current, no conversion needed data = savedData local needImport = false local dataToImport = { dbVersion = dbVersion } -- check for map File names that were used as a zone token and merge them if we now have a token local checkToken = Gatherer.ZoneTokens.GetTokenFromFileName for continent, contData in pairs(data) do if ( type(contData) == "table" ) then for zoneToken, zoneData in pairs(contData) do local token = checkToken(continent, zoneToken) if ( token ) then needImport = true if not (dataToImport[continent]) then dataToImport[continent] = { }; end if not (dataToImport[continent][token]) then dataToImport[continent][token] = { }; end dataToImport[continent][token] = data[continent][zoneToken] data[continent][zoneToken] = nil end end end end -- perform any needed node id re-mappings if ( Gatherer.Nodes.ReMappings ) then local remap = Gatherer.Nodes.ReMappings for continent, contData in pairs(data) do if ( type(contData)=="table" and type(continent)=="number" ) then for zoneToken, zoneData in pairs(contData) do for nodeId, nodes in pairs(zoneData) do if ( remap[nodeId] ) then needImport = true if not (dataToImport[continent]) then dataToImport[continent] = { }; end if not (dataToImport[continent][zoneToken]) then dataToImport[continent][zoneToken] = { }; end dataToImport[continent][zoneToken][nodeId] = nodes data[continent][zoneToken][nodeId] = nil end end end end end end if ( needImport ) then lib.ImportDatabase(dataToImport) end -- check for and remove any deprecated node ids local validObjects = Gatherer.Nodes.Objects for continent, contData in pairs(data) do if ( type(contData)=="table" and type(continent)=="number" ) then for zoneToken, zoneData in pairs(contData) do for nodeId in pairs(zoneData) do if not ( validObjects[nodeId] ) then zoneData[nodeId] = nil end end end end end elseif ( savedData.dbVersion < dbVersion ) then --old database, conversion needed lib.ImportDatabase(savedData) --TODO: check for "set aside" database that needs merging savedData = nil collectgarbage("collect"); --reclaim the old database elseif ( savedData.dbVersion > dbVersion ) then --database TOO new (Old Gatherer Version) --set the database aside and warn the user lib.ClearDatabase() data.setAsideDatabases = savedData.setAsideDatabases or {} savedData.setAsideDatabases = nil table.insert(data.setAsideDatabases, savedData) StaticPopup_Show("GATHERER_DATABASE_TOO_NEW") end else --INVALID DATABASE VERSION, raise an error and put the invalid database back into the global -- environment, the user can choose to clear the DB, or keep the invalid one StaticPopup_Show("GATHERER_INVALID_DATABASE_VERSION") getfenv(0)[globalName] = savedData corruptData = true lib.ClearDatabase() end else lib.ClearDatabase(); end elseif ( event == "PLAYER_LOGOUT" ) then -- don't write out the internal table if the user chose to keep a corrupt database if not ( corruptData ) then getfenv(0)[globalName] = data end end end) eventFrame.SetScript = function() end local workingTable = {} local function processImportedSourceField( ... ) for k in pairs(workingTable) do workingTable[k] = nil end local hasName, imported, require for i = 1, select("#", ...) do local name = select(i, ...) if ( name == "REQUIRE" ) then require = true elseif ( name == "IMPORTED" ) then imported = true else hasName = true workingTable[name] = true end end if ( (hasName and imported) or (hasName and require) or (require and imported) ) then return nil else local nameList = "" for name in pairs(workingTable) do nameList = nameList..","..name end return nameList:sub(2) end end local function MergeNode(gather, gatherType, continent, zone, gatherX, gatherY, count, harvested, inspected, source) if not ( gather and gatherType and continent and zone and gatherX and gatherY ) then return -- not enough data end local index = lib.AddNode(gather, gatherType, continent, zone, gatherX, gatherY, (source or "IMPORTED"), false) if not ( index ) then return end -- node was not added for some reason, abort local zone = Gatherer.ZoneTokens.GetZoneToken(continent, zone) local node = data[continent][zone][gather][index] if ( count ) then node[COUNT] = node[COUNT] + count end if ( harvested ) then node[HARVESTED] = harvested else node[HARVESTED] = 0 end if ( inspected ) then node[INSPECTED] = inspected else node[INSPECTED] = 0 end if ( node[SOURCE] and node[SOURCE] ~= "IMPORTED" and node[SOURCE] ~= "REQUIRE" ) then node[SOURCE] = processImportedSourceField(string.split(",", node[SOURCE])) end end function lib.ImportDatabase( database ) if not ( data ) then lib.ClearDatabase(); end if ( database.dbVersion <= 1 ) then Gatherer.Convert.RenumberDatabaseZonesForBCExpansion(database) end Gatherer.Convert.ImportDatabase(database, MergeNode) end -------------------------------------------------------------------------- -- Warning Dialogs -------------------------------------------------------------------------- -- references to localization functions local _tr = Gatherer.Locale.Tr local _trC = Gatherer.Locale.TrClient local _trL = Gatherer.Locale.TrLocale StaticPopupDialogs["GATHERER_INVALID_DATABASE_VERSION"] = { text = _trL("WARNING!!!\nGatherer has detected that your database version is invalid. Please press accept to clear your database, or select ignore if you want to try to repair your database manually."), button1 = _trL("ACCEPT"), button2 = _trL("IGNORE"), OnAccept = function() corruptData = false end, timeout = 0, whileDead = 1, } StaticPopupDialogs["GATHERER_DATABASE_TOO_NEW"] = { text = _trL("Your saved Gatherer database is too new. Your current database has been set aside until you upgrade Gatherer. "), button1 = _trL("OK"), timeout = 0, whileDead = 1, }