Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 82 additions & 54 deletions DataStore/API/Core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -325,30 +325,39 @@ local function GetModuleTable(module)
end


-- Tables that account sharing is allowed to transfer, per module.
-- Only list tables that are indexed by character id (ie: registered as characterTables or characterIdTables).
-- Guild tables are indexed by guild id, and reference tables by a currency id, a category id, a class name, etc..
-- an entry of theirs at [charID] belongs to something else entirely, and sending it would overwrite
-- unrelated data on the receiving side.
-- The index of a table in this list is what identifies it in the transferred payload, so both ends must
-- run the same version. Append new tables at the end of a module rather than inserting them.
local sharedTables = {
DataStore = {
-- "DataStore_GuildIDs",
-- "DataStore_GuildFactions",
-- "DataStore_CharacterIDs",
"DataStore_CharacterGUIDs",
-- "DataStore_CharacterGuilds",
DataStore = {
-- "DataStore_GuildIDs",
-- "DataStore_GuildFactions",
-- "DataStore_CharacterIDs",
"DataStore_CharacterGUIDs",
-- "DataStore_CharacterGuilds",
-- "DataStore_AltGroups", "DataStore_ConnectedRealms", "DataStore_RealmNames"
},
DataStore_Achievements = { "DataStore_Achievements_Characters" },
DataStore_Auctions = { "DataStore_Auctions_Characters", "DataStore_Auctions_AuctionsList", "DataStore_Auctions_BidsList" },
DataStore_Characters = { "DataStore_Characters_Info" },
DataStore_Containers = {
"DataStore_Containers_Characters", "DataStore_Containers_Banks", "DataStore_Containers_Guilds", "DataStore_Containers_Reagents",
DataStore_Containers = {
-- "DataStore_Containers_Guilds" is a guild table
"DataStore_Containers_Characters", "DataStore_Containers_Banks", "DataStore_Containers_Reagents",
"DataStore_Containers_VoidStorage", "DataStore_Containers_Keystones", "DataStore_Containers_BankTypes"
},
DataStore_Crafts = { "DataStore_Crafts_Characters", "DataStore_Crafts_ArcheologyItems", "DataStore_Crafts_RecipeCategories" },
DataStore_Currencies = {
"DataStore_Currencies_Characters", "DataStore_Currencies_Catalog", "DataStore_Currencies_Info", "DataStore_Currencies_Max",
"DataStore_Currencies_Headers", "DataStore_Currencies_Archeology"
-- "DataStore_Crafts_RecipeCategories" is indexed by category id
DataStore_Crafts = { "DataStore_Crafts_Characters", "DataStore_Crafts_ArcheologyItems" },
DataStore_Currencies = {
-- "DataStore_Currencies_Catalog" & "_Headers" are Set/List references, "_Info" & "_Max" are indexed by currency id
"DataStore_Currencies_Characters", "DataStore_Currencies_Archeology"
},
DataStore_Garrisons = {
"DataStore_Garrisons_Characters", "DataStore_Garrisons_Missions", "DataStore_Garrisons_MissionInfos",
"DataStore_Garrisons_Followers", "DataStore_Garrisons_FollowerNamesToID", "DataStore_Garrisons_Buildings",
DataStore_Garrisons = {
"DataStore_Garrisons_Characters", "DataStore_Garrisons_Missions", "DataStore_Garrisons_MissionInfos",
"DataStore_Garrisons_Followers", "DataStore_Garrisons_FollowerNamesToID", "DataStore_Garrisons_Buildings",
"DataStore_Garrisons_CovenantSanctum", "DataStore_Garrisons_CypherEquipment", "DataStore_Garrisons_Shipments"
},
DataStore_Inventory = { "DataStore_Inventory_Characters" },
Expand All @@ -357,50 +366,58 @@ local sharedTables = {
--, "DataStore_Quests_History", "DataStore_Quests_Progress", "DataStore_Quests_Dailies", "DataStore_Quests_Weeklies", "DataStore_Quests_Colors", "DataStore_Quests_Infos"
},
DataStore_Reputations = { "DataStore_Reputations_Characters" },
DataStore_Spells = { "DataStore_Spells_Characters", "DataStore_Spells_Tabs" },
-- "DataStore_Spells_Tabs" is indexed by class name
DataStore_Spells = { "DataStore_Spells_Characters" },
DataStore_Stats = { "DataStore_Stats_Characters"
--, "DataStore_Stats_Weekly", "DataStore_Stats_Dungeons"
},
DataStore_Talents = {
"DataStore_Talents_Characters", "DataStore_Talents_Specializations", "DataStore_Talents_SpecializationInfos",
"DataStore_Talents_Covenant", "DataStore_Talents_Conduits", "DataStore_Talents_ConduitSpecs",
DataStore_Talents = {
-- "DataStore_Talents_SpecializationInfos" is indexed by specialization id
"DataStore_Talents_Characters", "DataStore_Talents_Specializations",
"DataStore_Talents_Covenant", "DataStore_Talents_Conduits", "DataStore_Talents_ConduitSpecs",
"DataStore_Talents_Soulbinds", "DataStore_Talents_Reasons"
},
}

function addon:GetCharacterTable(module, name, realm, account)
-- Can the module be shared?
if not sharedTables[module] then return {} end

local moduleTables = sharedTables[module]
if not moduleTables then return {} end

local charTable = {}
local key = GetKey(name, realm, account)
local charID = addon:GetCharacterID(key)

-- Iterate tables in the current module
local moduleTables = sharedTables[module]

for moduleIndex, tableName in ipairs(moduleTables) do
-- the table may not exist at all, several of them are only created on retail
local sourceTable = _G[tableName]

-- do we have data for this character in the current table ?
if _G[tableName][charID] then
-- we do, link it
if sourceTable and sourceTable[charID] then

-- we do, link it
charTable[module] = charTable[module] or {}
charTable[module][moduleIndex] = _G[tableName][charID]
charTable[module][moduleIndex] = sourceTable[charID]
end
end

return charTable
-- return module.Characters[GetKey(name, realm, account)]

end

function addon:GetModuleLastUpdate(module, name, realm, account)
module = GetModuleTable(module)
-- Can the module be shared?
local moduleTables = sharedTables[module]
if not moduleTables then return end

local key = GetKey(name, realm, account)
if key then
return module.Characters[key].lastUpdate
end
-- the first table of a module is the one holding its character data
local characterTable = _G[moduleTables[1]]
if not characterTable then return end

local charID = addon:GetCharacterID(GetKey(name, realm, account))
if not charID then return end

return characterTable[charID] and characterTable[charID].lastUpdate
end

function addon:GetModuleLastUpdateByKey(moduleName, key)
Expand All @@ -419,31 +436,42 @@ function addon:GetModuleLastUpdateByKey(moduleName, key)
end

function addon:ImportData(module, data, name, realm, account)
module = GetModuleTable(module)

-- CopyTable is necessary rather than assignment, without it, ace DB wildcards are not applied.
addon:CopyTable(data, module.Characters[GetKey(name, realm, account)])
-- Can the module be shared?
local moduleTables = sharedTables[module]
if not moduleTables then return end

-- data was packed by GetCharacterTable: data[module][moduleIndex] = the module table of that character
local importedTables = data and data[module]
if not importedTables then return end

-- be sure an id exists for the imported character, every module table is indexed by it
local charID = addon:StoreToSetAndList(DataStore_CharacterIDs, GetKey(name, realm, account))

for moduleIndex, importedData in pairs(importedTables) do
local tableName = moduleTables[moduleIndex]
local destination = tableName and _G[tableName]

if destination then
if type(importedData) == "table" then
-- CopyTable is necessary rather than assignment, the source table belongs to the serializer.
destination[charID] = {}
addon:CopyTable(importedData, destination[charID])
else
-- character id tables may hold a plain value (ex: a specialization id)
destination[charID] = importedData
end
end
end
end

function addon:ImportCharacter(key, faction, guild)
function addon:ImportCharacter(key)
-- after data has been imported, add a player entry to the DB, so that it becomes "visible" to the outside world.
-- in other words, the correct sequence of operations should be something like:
-- DataStore:ImportData(DataStore_Talents)
-- DataStore:ImportData(DataStore_Spells)
-- DataStore:ImportCharacter(key, faction, guild)
-- DataStore:ImportCharacter(key)

local characters = addon.db.global.Characters

characters[key].faction = faction
characters[key].guildName = guild

-- Ensure a key is created for every module, even those which were not imported.
-- Required for proper UI support without extra validation of every method
addon:IterateDBModules(function(moduleDB)
if moduleDB.Characters then
moduleDB.Characters[key].lastUpdate = time()
end
end)
return addon:StoreToSetAndList(DataStore_CharacterIDs, key)
end


Expand Down
19 changes: 11 additions & 8 deletions DataStore/API/GuildComm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ local function GetKey(name, realm, account)
return format("%s.%s.%s", account, realm, name)
end

local function GetAlts()
local function GetAlts(guildID)
-- guildID = the guild of the current character. Callers that have just resolved it pass it in,
-- the others ask for the one this character is registered under.
-- Do not rebuild a key from ThisRealm here: on connected realms the guild may be homed on another
-- realm of the group, and OnPlayerGuildUpdate() registered it under that one. Rebuilding it would
-- compare our alts against the wrong id, so none of them would ever match, and it would also
-- register a second id in DataStore_GuildIDs for a guild that already has one.
guildID = guildID or addon:GetCharacterGuildID(addon.ThisCharKey)
if not guildID then return end

local guild = GetGuildInfo("player")
if not guild then return end

local guildKey = GetKey(guild)
local guildID = addon:StoreToSetAndList(DataStore_GuildIDs, guildKey)

local out = {}
for k, charID in pairs(DataStore_CharacterIDs.Set) do
local account, realm, char = strsplit(".", k)
Expand Down Expand Up @@ -131,7 +133,8 @@ local function OnPlayerGuildUpdate()
DataStore_GuildFactions[guildID] = addon.ThisFaction

-- the first time a valid value is found, broadcast to guild, it must happen here for a standard login, but won't work here after a reloadui since this event is not triggered
addon:GuildBroadcast(commPrefix, MSG_ANNOUNCELOGIN, GetAlts())
-- pass the id we just resolved, it is only written to DataStore_CharacterGuilds below
addon:GuildBroadcast(commPrefix, MSG_ANNOUNCELOGIN, GetAlts(guildID))
AddonFactory:Broadcast("DATASTORE_ANNOUNCELOGIN", currentGuildName)
end

Expand Down
12 changes: 9 additions & 3 deletions DataStore/API/Utility.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,24 @@ function addon:SortedArrayClone(array)
return clone
end

function addon:CopyTable(source, destination)
local function CopyTableInto(source, destination)
for k, v in pairs(source) do

if type(v) == "table" then
destination[k] = {}
CopyTable(v, destination[k])
CopyTableInto(v, destination[k])
else
destination[k] = v
end
end
end

function addon:CopyTable(source, destination)
-- note: do not call the global CopyTable() for nested tables, it returns a new table
-- instead of filling the destination, which silently dropped every sub-table.
CopyTableInto(source, destination)
end

function addon:ArrayInsertUnique(array, value)
-- Check if the element already exists in the array
for _, v in ipairs(array) do
Expand Down
14 changes: 13 additions & 1 deletion DataStore/DataStore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,21 @@ end
-- *** Guild functions ***

function addon:GetGuild(name, realm, account)
-- No name ? the caller wants the guild of the current character, so answer with the guild it is
-- actually registered in, instead of rebuilding a key from its own realm. On connected realms the
-- guild may live on another realm of the group, and OnPlayerGuildUpdate() registered it under that
-- realm. Rebuilding the key from ThisRealm then creates a second key for the same guild, with a
-- second id, and every module that stored its data under the registered one looks empty.
if not name then
local guildID = _GetCharacterGuildID(addon.ThisCharKey)
local guildKey = guildID and allGuilds.List[guildID]

if guildKey then return guildKey end
end

name = name or GetGuildInfo("player")
local key = GetKey(name, realm, account)

if allGuilds.Set[key] then -- if the key is known, return it to caller, it can be passed to other modules
return key
else -- if the key is not known, try checking the connected realm info
Expand Down