changeset 147:e1a90e398231

Add unique seconds to history timestamp. Rewrite :format_timestamp to suck less. Include seconds in the formatted history timestamp, because preening fails horribly when the same person has received multiple pieces of loot within the same "minute". Ensure the timestamps are unique if needed; fast looting easily results in multiple loot events per second. Tweak wording of some GUI warnings.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Tue, 30 Dec 2014 20:26:41 -0500
parents 543fcf15add7
children 113dd7c86222
files core.lua options.lua
diffstat 2 files changed, 50 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/core.lua	Tue Dec 30 17:52:30 2014 -0500
+++ b/core.lua	Tue Dec 30 20:26:41 2014 -0500
@@ -28,7 +28,7 @@
 - kind          time/boss/loot
 - hour          0-23, on the *physical instance server*, not the realm server
 - minute        0-59, ditto
-- stamp         time_t on the local computer
+- stamp         time_t on the local computer, possibly tweaked for uniqueness
 
 Time specific g_loot indices:
 - startday      table with month/day/year/text fields from makedate()
@@ -179,6 +179,8 @@
 local my_name				= UnitName('player')
 local comm_cleanup_ttl		= 4   -- seconds in the communications cache
 local version_large			= nil -- defaults to 1, possibly changed by version
+local timestamp_fmt_unique	= '%Y/%m/%dT%H:%M:%S'
+local timestamp_fmt_history	= '%Y/%m/%d %H:%M:%S'
 local g_LOOT_ITEM_ss, g_LOOT_ITEM_MULTIPLE_sss, g_LOOT_ITEM_SELF_s
 local g_LOOT_ITEM_SELF_MULTIPLE_ss, g_LOOT_ITEM_WHILE_PLAYER_INELIGIBLE_ss
 
@@ -1820,7 +1822,7 @@
 				person = my_name    -- UNIT_YOU / You
 			end
 
-			return _do_loot (self, --[[override=]]false, person, unique,
+			return _do_loot (self, --[[override=]]false, person, --[[unique=]]nil,
 				match(itemlink,"item[%-?%d:]+"), count)
 
 		elseif event == "broadcast" then
@@ -2617,7 +2619,7 @@
 
 -- Adding entries to the loot record, and tracking the corresponding timestamp.
 do
-	local rawget, setmetatable = rawget, setmetatable
+	local date, time, rawget, setmetatable = date, time, rawget, setmetatable
 
 	--@debug@
 	local tos = {}
@@ -2674,28 +2676,34 @@
 	end
 
 	-- format_timestamp (["format_string"], Day, [Loot])
+	-- FORMAT_STRING may contain $x (x in TmdHMS) tokens, with the same
+	--    meanings as in Lua/strftime but restricted formatting:
+	--      Y   year, 4 digit
+	--      m   month, 2 digit
+	--      d   day, 2 digit
+	--      H   hour, 2 digit, 24-hour clock
+	--      M   minute
+	--      S   second
 	-- DAY is a loot entry with kind=='time', and controls the date printed.
 	-- LOOT may be any kind of entry in the g_loot table.  If present, it
-	--    overrides the hour and minute printed; if absent, those values are
+	--    overrides the clock values printed; if absent, those values are
 	--    taken from the DAY entry.
-	-- FORMAT_STRING may contain $x (x in Y/M/D/h/m) tokens.
-	-- FIXME this is horribabble
 	local format_timestamp_values, point2dee = {}, "%.2d"
 	function addon:format_timestamp (fmt_opt, day_entry, time_entry_opt)
 		if not time_entry_opt then
 			if type(fmt_opt) == 'table' then        -- Two entries, default format
 				time_entry_opt, day_entry = day_entry, fmt_opt
-				fmt_opt = "$Y/$M/$D $h:$m"
+				fmt_opt = "$Y/$m/$d $H:$M"
 			--elseif type(fmt_opt) == "string" then   -- Day entry only, caller-specified format
 			end
 		end
-		--format_timestamp_values.Y = point2dee:format (day_entry.startday.year % 100)
 		format_timestamp_values.Y = ("%.4d"):format (day_entry.startday.year)
-		format_timestamp_values.M = point2dee:format (day_entry.startday.month)
-		format_timestamp_values.D = point2dee:format (day_entry.startday.day)
-		format_timestamp_values.h = point2dee:format ((time_entry_opt or day_entry).hour)
-		format_timestamp_values.m = point2dee:format ((time_entry_opt or day_entry).minute)
-		return fmt_opt:gsub ('%$([YMDhm])', format_timestamp_values)
+		format_timestamp_values.m = ("%.2d"):format (day_entry.startday.month)
+		format_timestamp_values.d = ("%.2d"):format (day_entry.startday.day)
+		format_timestamp_values.H = ("%.2d"):format ((time_entry_opt or day_entry).hour)
+		format_timestamp_values.M = ("%.2d"):format ((time_entry_opt or day_entry).minute)
+		format_timestamp_values.S = date ("%S", (time_entry_opt or day_entry).stamp)
+		return fmt_opt:gsub ('%$([YmdHMS])', format_timestamp_values)
 	end
 
 	local done_todays_date
@@ -2733,16 +2741,33 @@
 
 		if not done_todays_date then do_todays_date() end
 
+		-- All kinds of things go awry (especially history preening) if two
+		-- entries share the exact same timestamp, and we can't get any better
+		-- than one second resolution.
+		--
+		-- Well, the only API in-game with finer precision is GetTime but the
+		-- computer uptime doesn't help us.  Stripping the fractional part off
+		-- and gluing it to time_t would be too risky as they don't cycle at
+		-- the same time.  We could do something involving a count incrementing
+		-- with the refresh rate, but... blech.
+		--
+		-- So we sort of cheat.  New entries are pushed into the future one
+		-- second at a time, if needed, so that no two time_t's are equal.
 		local h, m = GetGameTime()
-		--local localuptime = math.floor(GetTime())
 		local time_t = time()
+		local index = #g_loot  -- note, previous entry
 		e.hour = h
 		e.minute = m
-		e.stamp = time_t --localuptime
-		local index = #g_loot + 1
+		if index > 0 and g_loot[index].stamp >= time_t then
+			time_t = g_loot[index].stamp + 1
+			addon.dprint('flow', "bumping timestamp to", time_t)
+		end
+		e.stamp = time_t
+
+		index = index + 1
 		if e.kind == 'loot' then
 			if (not e.unique) or (#e.unique==0) then
-				e.unique = e.id .. (e.disposition or e.person) .. date("%Y/%m/%d %H:%M",e.stamp)
+				e.unique = e.id .. (e.disposition or e.person) .. date (timestamp_fmt_unique, e.stamp)
 			end
 			addon:Fire ('NewLootEntry', e, index)
 		end
@@ -3162,7 +3187,7 @@
 --       -- sorted array:
 --       ["unique"] = { most_recent_tag, previous_tag, .... },
 --       -- these are indexed by unique tags, and 'count' may be missing:
---       ["when"] = { ["tag"] = "formatted timestamp for displaying loot" },
+--       ["when"] = { ["tag"] = "formatted timestamp for displaying loot history" },
 --       ["id"] = { ["tag"] = 11111 },
 --       ["count"] = { ["tag"] = "x3", .... },
 --     },
@@ -3193,6 +3218,7 @@
 	local function sort_player (p)
 		local new_uniques, uniques_bywhen, when_array = {}, new(), new()
 		for u,tstamp in pairs(p.when) do
+			-- XXX multiple identical tstamps
 			uniques_bywhen[tstamp] = u
 			when_array[#when_array+1] = tstamp
 		end
@@ -3384,6 +3410,7 @@
 	--
 	-- If RESORT_P is true-valued, then re-sorts the player's history based on
 	-- formatted timestmps, instead of leaving the new entry as the latest.
+	local tfmt_hist = timestamp_fmt_history:gsub('%%','$')
 	function addon:_addHistoryEntry (lootindex, resort_p)
 		local e = g_loot[lootindex]
 		if e.kind ~= 'loot' then return end
@@ -3396,8 +3423,10 @@
 		-- If we've added anything at all into g_loot this session, g_today
 		-- will be set.  If we've logged on simply to manipulate history, then
 		-- try and fake a timestamp (it'll be "close enough").
-		local when = g_today and self:format_timestamp (g_today,e)
-			or date("%Y/%m/%d %H:%M",e.stamp)
+		-- (WoD:  This logic may be backwards now that loot entries track time_t.)
+		local when = g_today
+			         and self:format_timestamp (tfmt_hist, g_today, e)
+			         or date (timestamp_fmt_history, e.stamp)
 		assert(h.name==e.person)
 
 		-- Should rarely happen anymore:
--- a/options.lua	Tue Dec 30 17:52:30 2014 -0500
+++ b/options.lua	Tue Dec 30 20:26:41 2014 -0500
@@ -703,7 +703,7 @@
 
 	w = AceGUI:Create("Spacer") w:SetRelativeWidth(0.65) w:SetHeight(15) container:AddChild(w)
 	w = mkbutton("Clear All & Reload",
-		[[No confirmation!  |cffff1010Erases absolutely all> Ouro Loot saved variables and reloads the UI.]])
+		[[No confirmation!  |cffff1010Erases absolutely all> Ouro Loot saved variables and immediately reloads the UI.]])
 	w:SetRelativeWidth(0.3)
 	w:SetCallback("OnClick", function()
 		addon:_clear_SVs()  -- reloads