diff --git a/GuildGoldDeposits.lua b/GuildGoldDeposits.lua index d39fc4e..7ffdd7e 100644 --- a/GuildGoldDeposits.lua +++ b/GuildGoldDeposits.lua @@ -5,9 +5,13 @@ GuildGoldDeposits.name = "GuildGoldDeposits" GuildGoldDeposits.version = 1 GuildGoldDeposits.default = { enable_guild = { true, true, true, true, true } - , duration_days = 7 } GuildGoldDeposits.max_guild_ct = 5 +GuildGoldDeposits.event_list = {} -- event_list[guild_index] = { list of event strings } +GuildGoldDeposits.guild_name = {} -- guild_name[guild_index] = "My Aweseome Guild" +GuildGoldDeposits.retry_ct = { 0, 0, 0, 0, 0 } -- retry_ct[guild_index] = how many retries after + -- distrusting "nah, no more history" +GuildGoldDeposits.max_retry_ct = 3 -- Init ---------------------------------------------------------------------- @@ -19,7 +23,7 @@ function GuildGoldDeposits.OnAddOnLoaded(event, addonName) end function GuildGoldDeposits:Initialize() - self.savedVariables = ZO_SavedVars:New( + self.savedVariables = ZO_SavedVars:NewAccountWide( "GuildGoldDepositsVars" , self.version , nil @@ -31,12 +35,12 @@ end -- UI ------------------------------------------------------------------------ -function GuildGoldDeposits.ref_cb(i) - return "GuildGoldDeposits_cbg" .. i +function GuildGoldDeposits.ref_cb(guild_index) + return "GuildGoldDeposits_cbg" .. guild_index end -function GuildGoldDeposits.ref_desc(i) - return "GuildGoldDeposits_desc" .. i +function GuildGoldDeposits.ref_desc(guild_index) + return "GuildGoldDeposits_desc" .. guild_index end function GuildGoldDeposits:CreateSettingsWindow() @@ -60,32 +64,23 @@ function GuildGoldDeposits:CreateSettingsWindow() , func = function() self:SaveNow() end }, { type = "header" - , name = "Duration" - }, - { type = "slider" - , name = "Days to save" - , tooltip = "How many days' data to save?" - , min = 1 - , max = 21 - , step = 1 - , getFunc = function() return self.savedVariables.duration_days end - , setFunc = function(value) self.savedVariables.duration_days = value end - }, - { type = "header" , name = "Guilds" }, } - for i = 1, self.max_guild_ct do + for guild_index = 1, self.max_guild_ct do table.insert(optionsData, { type = "checkbox" - , name = "(guild " .. i .. ")" - , tooltip = "Save data for guild " .. i .. "?" - , getFunc = function() return self.savedVariables.enable_guild[i] end - , setFunc = function(e) self.savedVariables.enable_guild[i] = e end - , reference = self.ref_cb(i) + , name = "(guild " .. guild_index .. ")" + , tooltip = "Save data for guild " .. guild_index .. "?" + , getFunc = function() + return self.savedVariables.enable_guild[guild_index] + end + , setFunc = function(e) + self.savedVariables.enable_guild[guild_index] = e + end + , reference = self.ref_cb(guild_index) }) - -- HACK: for some reason, I cannot get "description" -- items to dynamically update their text. Color and -- hidden, yes, but text? Nope, it never changes. So @@ -94,8 +89,8 @@ function GuildGoldDeposits:CreateSettingsWindow() -- hack. Sorry. table.insert(optionsData, { type = "checkbox" - , name = "(desc " .. i .. ")" - , reference = self.ref_desc(i) + , name = "(desc " .. guild_index .. ")" + , reference = self.ref_desc(guild_index) , getFunc = function() return false end , setFunc = function() end }) @@ -111,20 +106,21 @@ end function GuildGoldDeposits.OnPanelControlsCreated(panel) self = GuildGoldDeposits guild_ct = GetNumGuilds() - for i = 1,self.max_guild_ct do - cb = _G[self.ref_cb(i)] - if i <= guild_ct then - guildId = GetGuildId(i) + for guild_index = 1,self.max_guild_ct do + cb = _G[self.ref_cb(guild_index)] + if guild_index <= guild_ct then + guildId = GetGuildId(guild_index) guildName = GetGuildName(guildId) cb.label:SetText(guildName) cb:SetHidden(false) + self.guild_name[guild_index] = guildName else -- If no guild #N, hide and disable it. cb:SetHidden(true) - self.savedVariables.enable_guild[i] = false + self.savedVariables.enable_guild[guild_index] = false end - desc = _G[self.ref_desc(i)] + desc = _G[self.ref_desc(guild_index)] self.ConvertCheckboxToText(desc) end end @@ -146,50 +142,111 @@ end -- Saving Guild Data --------------------------------------------------------- function GuildGoldDeposits:SaveNow() - -- self:DumpSettings() - for i = 1, self.max_guild_ct do - self:SaveGuildIndex(i) + self.savedVariables.history = {} + self.event_list = {} + for guild_index = 1, self.max_guild_ct do + if self.savedVariables.enable_guild[guild_index] then + self:SaveGuildIndex(guild_index) + else + self:SkipGuildIndex(guild_index) + end end end -function GuildGoldDeposits:SaveGuildIndex(i) - guildId = GetGuildId(i) - desc = _G[GuildGoldDeposits.ref_desc(i)].label - if self.savedVariables.enable_guild[i] then - color = ZO_DEFAULT_ENABLED_COLOR - text = "gonna do something" - else - color = ZO_DEFAULT_DISABLED_COLOR - text = "not doing nothing" - end - desc:SetText(text) +-- Update the per-guild text label with what's going on with that guild data. +function GuildGoldDeposits:SetStatus(guild_index, msg) + desc = _G[self.ref_desc(guild_index)].label + desc:SetText(" " .. msg) end -function GuildGoldDeposits:DumpSettings() - d("sv.days " .. self.savedVariables.duration_days) - for i = 1, self.max_guild_ct do - d("sv.eg[" .. i .. "] = " - .. tostring(self.savedVariables.enable_guild[i])) - end +-- User doesn't want this guild. Respond with "okay, skipping" +function GuildGoldDeposits:SkipGuildIndex(guild_index) + self:SetStatus(guild_index, "skipped") end --- GetGuildEventInfo(3, GUILD_HISTORY_BANK_DEPOSITS, 2) --- 21 1656 @J-man8898 5000 nil nil nil nil --- 2016-02-29 14:45:37 -0700 ziggr: "@J-man8898 deposited 5,000g 27 minutes ago --- 21 1741 @J-man8898 5000 nil nil nil nil --- 2016-02-29 14:46:44 -0700 ziggr: "@J-man8898 deposited 5,000g 27 minutes ago --- So field 2 is RELATIVE TIME AGO and likeluy SECONDS ago +-- Download one guild's history +function GuildGoldDeposits:SaveGuildIndex(guild_index) + guildId = GetGuildId(guild_index) + self:SetStatus(guild_index, "downloading history...") + event_ct = 0 + found_ct = 0 + loop_ct = 0 + loop_max = 100 + RequestGuildHistoryCategoryNewest(guildId, GUILD_HISTORY_BANK) --- GetGuildEventInfo(3, GUILD_HISTORY_BANK_DEPOSITS, 2) --- 0/nil at first, until I opened the Guild history. --- So there must be an open/init sequence. - --- GetTimeStamp() returns seconds since the epoch. That, minus above, is --- seconds-since-the-epoch of the item/event. + -- Start an asynchronous callback chain to slowly + -- poll ESO servers for all history. Chain will + -- callback itself until done, then callback + -- into the actual processing of that data. + self:ServerDataPoll(guild_index) +end +-- Async poll to fetch ALL guild bank history data from the ESO server +-- Calls ServerDataComplete() once all data is loaded. +function GuildGoldDeposits:ServerDataPoll(guild_index) + guildId = GetGuildId(guild_index) + more = DoesGuildHistoryCategoryHaveMoreEvents(guildId, GUILD_HISTORY_BANK) + event_ct = GetNumGuildEvents(guildId, GUILD_HISTORY_BANK) + self:SetStatus(guild_index, "fetching events: " .. event_ct .. " ...") + can_retry = (not self.retry_ct[guild_index]) + or (self.retry_ct[guild_index] < self.max_retry_ct) + if more or can_retry then + RequestGuildHistoryCategoryOlder(guildId, GUILD_HISTORY_BANK) + delay_ms = 0.5 * 1000 + zo_callLater(function() self:ServerDataPoll(guild_index) end, delay_ms) + if not more then + self.retry_ct[guild_index] = 1 + self.retry_ct[guild_index] + end + else + self:ServerDataComplete(guild_index) + end +end +-- Now that all data from the ESO server is loaded into the ESO client, +-- extract gold deposits and write to savedVars. +function GuildGoldDeposits:ServerDataComplete(guild_index) + guildId = GetGuildId(guild_index) + guild_name = self.guild_name[guild_index] + event_ct = GetNumGuildEvents(guildId, GUILD_HISTORY_BANK) + --self:SetStatus(guild_index, "scanning events: " .. event_ct .. " ...") + for i = 1, event_ct do + t, s, u, a = GetGuildEventInfo(guildId, GUILD_HISTORY_BANK, i) + if t == GUILD_EVENT_BANKGOLD_ADDED then + event = { type = t + , time = GetTimeStamp() - s + , user = u + , amount = a + } + self:RecordEvent(guild_index, event) + end + end + found_ct = 0 + if self.event_list[guild_index] then + found_ct = #self.event_list[guild_index] + end + self:SetStatus(guild_index, "scanned events: " .. event_ct + .. " gold deposits: " .. found_ct) + self.savedVariables.history[guild_name] = self.event_list[guild_index] +end +function GuildGoldDeposits:RecordEvent(guild_index, event) + if not self.event_list[guild_index] then + self.event_list[guild_index] = {} + end + t = self.event_list[guild_index] + table.insert(t, self:EventToString(event)) +end +-- Convert an event to a compact string that a line-parser can easily consume. +function GuildGoldDeposits:EventToString(event) + -- tab-delimited fields + -- date seconds since the epoch + -- amount + -- user unquoted, can contain all sorts of + -- noise but unlikely to contian a + -- tab character. + return string.format("%d\t%d\t%s", event.time, event.amount, event.user) +end -- Postamble ----------------------------------------------------------------- diff --git a/GuildGoldDeposits_to_csv.lua b/GuildGoldDeposits_to_csv.lua new file mode 100644 index 0000000..d409ad0 --- /dev/null +++ b/GuildGoldDeposits_to_csv.lua @@ -0,0 +1,67 @@ +-- Read the SavedVariables file that GuildGoldDeposits creates and convert +-- that to a spreadsheet-compabitle CSV (comma-separated value) file. + +IN_FILE_PATH = "../../SavedVariables/GuildGoldDeposits.lua" +OUT_FILE_PATH = "../../SavedVariables/GuildGoldDeposits.csv" +dofile(IN_FILE_PATH) +OUT_FILE = assert(io.open(OUT_FILE_PATH, "w")) + +-- Lua lacks a split() function. Here's a cheesy hardwired one that works +-- for our specific need. +function split(str) + t1 = string.find(str, '\t') + t2 = string.find(str, '\t', 1 + t1) + return string.sub(str, 1, t1 - 1) + , string.sub(str, 1 + t1, t2 - 1) + , string.sub(str, 1 + t2) +end + +-- Parse the ["history'] table +function TableHistory(history) + for k,v in pairs(history) do + guild_name = k + for _,line in pairs(v) do + time_secs, amount, user = split(line) + WriteLine(guild_name, time_secs, amount, user) + end + end +end + +function enquote(s) + return '"' .. s .. '"' +end + +-- Convert "1456709816" to "2016-02-28T17:36:56" ISO 8601 formatted time +-- Assume "local machine time" and ignore any incorrect offsets due to +-- Daylight Saving Time transitions. Ugh. +function iso_date(secs_since_1970) + t = os.date("*t", secs_since_1970) + return string.format("%04d-%02d-%02dT%02d:%02d:%02d" + , t.year + , t.month + , t.day + , t.hour + , t.min + , t.sec + ) +end + +function WriteLine(guild_name, time_secs, amount, user) + OUT_FILE:write( enquote(guild_name) + .. ',' .. iso_date(time_secs) + .. ',' .. amount + .. ',' .. enquote(user) + .. '\n' + ) +end + + +-- For each account +for k, v in pairs(GuildGoldDepositsVars["Default"]) do + if ( GuildGoldDepositsVars["Default"][k]["$AccountWide"] + and GuildGoldDepositsVars["Default"][k]["$AccountWide"]["history"]) then + TableHistory(GuildGoldDepositsVars["Default"][k]["$AccountWide"]["history"]) + end +end +OUT_FILE:close() +