comparison core.lua @ 71:fb330a1fb6e9

Add a unique field to loot (extracted from uniqueID field when possible, randomly generated when not) and include it in broadcasts. Use this as a history identifier for future loot. Will need to tweak using this in signatures (and finally avoid problems looting multiples of an item to a single person).
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Sat, 12 May 2012 07:17:55 +0000
parents cdee65c1bd8c
children 32eb24fb2ebf
comparison
equal deleted inserted replaced
70:cdee65c1bd8c 71:fb330a1fb6e9
51 - itemname not including square brackets 51 - itemname not including square brackets
52 - id itemID as number 52 - id itemID as number
53 - itemlink full clickable link 53 - itemlink full clickable link
54 - itexture icon path (e.g., Interface\Icons\INV_Misc_Rune_01) 54 - itexture icon path (e.g., Interface\Icons\INV_Misc_Rune_01)
55 - quality ITEM_QUALITY_* number 55 - quality ITEM_QUALITY_* number
56 - unique an almost-certainly-unique string, content meaningless
56 - disposition offspec/gvault/shard; missing otherwise; can be set from 57 - disposition offspec/gvault/shard; missing otherwise; can be set from
57 the extratext field 58 the extratext field
58 - count e.g., "x3"; missing otherwise; can be set/removed from 59 - count e.g., "x3"; missing otherwise; can be set/removed from
59 extratext; triggers only for a stack of items, not "the boss 60 extratext; triggers only for a stack of items, not "the boss
60 dropped double axes today" 61 dropped double axes today"
107 OuroLootSV_log = {} 108 OuroLootSV_log = {}
108 109
109 110
110 ------ Constants 111 ------ Constants
111 local option_defaults = { 112 local option_defaults = {
112 ['datarev'] = 19, -- cheating, this isn't actually an option 113 ['datarev'] = 20, -- cheating, this isn't actually an option
113 ['popup_on_join'] = true, 114 ['popup_on_join'] = true,
114 ['register_slashloot'] = true, 115 ['register_slashloot'] = true,
115 ['scroll_to_bottom'] = true, 116 ['scroll_to_bottom'] = true,
116 ['chatty_on_kill'] = false, 117 ['chatty_on_kill'] = false,
117 ['no_tracking_wipes'] = false, 118 ['no_tracking_wipes'] = false,
159 addon.author_debug = flib.author_debug 160 addon.author_debug = flib.author_debug
160 161
161 -- Play cute games with namespaces here just to save typing. WTB Lua 5.2 PST. 162 -- Play cute games with namespaces here just to save typing. WTB Lua 5.2 PST.
162 do local _G = _G setfenv (1, addon) 163 do local _G = _G setfenv (1, addon)
163 164
164 commrev = '16' 165 commrev = '17'
165 revision = _G.GetAddOnMetadata(nametag,"Version") or "?" -- "x.yy.z", etc 166 revision = _G.GetAddOnMetadata(nametag,"Version") or "?" -- "x.yy.z", etc
166 ident = "OuroLoot2" 167 ident = "OuroLoot2"
167 identTg = "OuroLoot2Tg" 168 identTg = "OuroLoot2Tg"
168 status_text = nil 169 status_text = nil
169 170
561 562
562 self.history_all = self.history_all or OuroLootSV_hist or {} 563 self.history_all = self.history_all or OuroLootSV_hist or {}
563 local r = self:load_assert (GetRealmName(), "how the freak does GetRealmName() fail?") 564 local r = self:load_assert (GetRealmName(), "how the freak does GetRealmName() fail?")
564 self.history_all[r] = self:_prep_new_history_category (self.history_all[r], r) 565 self.history_all[r] = self:_prep_new_history_category (self.history_all[r], r)
565 self.history = self.history_all[r] 566 self.history = self.history_all[r]
567
568 local histformat = self.history_all.HISTFORMAT
569 self.history_all.HISTFORMAT = nil -- don't keep this in live data
566 if (not InCombatLockdown()) and OuroLootSV_hist and 570 if (not InCombatLockdown()) and OuroLootSV_hist and
567 (OuroLootSV_hist.HISTFORMAT == nil) -- restored data but it's older 571 (histformat == nil or histformat < 3) -- restored data but it's older
568 then 572 then
569 -- Big honkin' loop 573 -- Big honkin' loop
570 for rname,realm in pairs(self.history_all) do 574 for rname,realm in pairs(self.history_all) do
571 for pk,player in ipairs(realm) do 575 for pk,player in ipairs(realm) do
572 for lk,loot in ipairs(player) do 576 for lk,loot in ipairs(player) do
573 if loot.count == "" then 577 if loot.count == "" then
574 loot.count = nil 578 loot.count = nil
575 end 579 end
580 if not loot.unique then
581 loot.unique = loot.id .. ' ' .. loot.when
582 end
576 end 583 end
577 end 584 end
578 end 585 end
579 end 586 end
580 self.history_all.HISTFORMAT = nil -- don't keep this in live data
581 --OuroLootSV_hist = nil 587 --OuroLootSV_hist = nil
582 588
583 -- Handle changes to the stored data format in stages from oldest to newest. 589 -- Handle changes to the stored data format in stages from oldest to newest.
584 if OuroLootSV then 590 if OuroLootSV then
585 local dirty = false 591 local dirty = false
624 630
625 bumpers[18] = bumpers[16] 631 bumpers[18] = bumpers[16]
626 -- In the not-very-many days between 16 and 19, I managed to break 632 -- In the not-very-many days between 16 and 19, I managed to break
627 -- the exact same data in the exact same way. At least they're 633 -- the exact same data in the exact same way. At least they're
628 -- not actually running the same loop twice... probably... sigh. 634 -- not actually running the same loop twice... probably... sigh.
635
636 bumpers[19] = function()
637 for i,e in ipairs(OuroLootSV) do
638 if e.kind == 'loot' and e.history_unique then
639 e.unique, e.history_unique = e.history_unique, nil
640 end
641 end
642 end
629 643
630 --[===[ 644 --[===[
631 local real = bumpers 645 local real = bumpers
632 bumpers = newproxy(true) 646 bumpers = newproxy(true)
633 local mt = getmetatable(bumpers) 647 local mt = getmetatable(bumpers)
860 t.byname = nil 874 t.byname = nil
861 end 875 end
862 end end 876 end end
863 if worth_saving then 877 if worth_saving then
864 OuroLootSV_hist = self.history_all 878 OuroLootSV_hist = self.history_all
865 OuroLootSV_hist.HISTFORMAT = 2 879 OuroLootSV_hist.HISTFORMAT = 3
866 else 880 else
867 OuroLootSV_hist = nil 881 OuroLootSV_hist = nil
868 end 882 end
869 OuroLootSV_log = #OuroLootSV_log > 0 and OuroLootSV_log or nil 883 OuroLootSV_log = #OuroLootSV_log > 0 and OuroLootSV_log or nil
870 end 884 end
1048 end 1062 end
1049 wipe(candidates) 1063 wipe(candidates)
1050 end 1064 end
1051 addon.recent_loot = create_new_cache ('loot', comm_cleanup_ttl+3, prefer_local_loots) 1065 addon.recent_loot = create_new_cache ('loot', comm_cleanup_ttl+3, prefer_local_loots)
1052 1066
1053 local GetItemInfo, GetItemIcon = GetItemInfo, GetItemIcon 1067 local GetItemInfo, GetItemIcon, random = GetItemInfo, GetItemIcon, math.random
1054 1068
1055 -- 'from' and onwards only present if this is triggered by a broadcast 1069 -- 'from' only present if this is triggered by a broadcast
1056 function addon:_do_loot (local_override, recipient, itemid, count, from, extratext) 1070 function addon:_do_loot (local_override, recipient, unique, itemid, count, from, extratext)
1057 local itexture = GetItemIcon(itemid) 1071 local itexture = GetItemIcon(itemid)
1058 local iname, ilink, iquality = GetItemInfo(itemid) 1072 local iname, ilink, iquality = GetItemInfo(itemid)
1059 local i 1073 local i
1060 if (not iname) or (not itexture) then 1074 if (not iname) or (not itexture) then
1061 i = true 1075 i = true
1062 iname, ilink, iquality, itexture = 1076 iname, ilink, iquality, itexture =
1063 UNKNOWN..': '..itemid, 'item:6948', ITEM_QUALITY_COMMON, [[ICONS\INV_Misc_QuestionMark]] 1077 UNKNOWN..': '..itemid, 'item:6948', ITEM_QUALITY_COMMON, [[ICONS\INV_Misc_QuestionMark]]
1064 end 1078 end
1065 self.dprint('loot',">>_do_loot, R:", recipient, "I:", itemid, "C:", count, "frm:", from, "ex:", extratext, "q:", iquality) 1079 self.dprint('loot',">>_do_loot, R:", recipient, "U:", unique, "I:",
1080 itemid, "C:", count, "frm:", from, "ex:", extratext, "q:", iquality)
1066 1081
1067 itemid = tonumber(ilink:match("item:(%d+)") or 0) 1082 itemid = tonumber(ilink:match("item:(%d+)") or 0)
1083 unique = tostring(unique or random(8675309)) -- also, xkcd.com/1047
1068 -- This is only a 'while' to make jumping out of it easy and still do cleanup below. 1084 -- This is only a 'while' to make jumping out of it easy and still do cleanup below.
1069 while local_override or ((iquality >= self.threshold) and not opts.itemfilter[itemid]) do 1085 while local_override or ((iquality >= self.threshold) and not opts.itemfilter[itemid]) do
1070 if (self.rebroadcast and (not from)) and not local_override then 1086 if (self.rebroadcast and (not from)) and not local_override then
1071 self:vbroadcast('loot', recipient, itemid, count) 1087 self:vbroadcast('loot', recipient, unique, itemid, count)
1072 end 1088 end
1073 if (not self.enabled) and (not local_override) then break end 1089 if (not self.enabled) and (not local_override) then break end
1074 local signature = recipient .. iname .. (count or "") 1090 local signature = unique .. recipient .. iname .. (count or "")
1075 if from and self.recent_loot:test(signature) then 1091 if from and self.recent_loot:test(signature) then
1076 self.dprint('cache', "remote loot <",signature,"> already in cache, skipping") 1092 self.dprint('cache', "remote loot <",signature,"> already in cache, skipping")
1077 else 1093 else
1078 -- There is some redundancy in all this, in the interests of ease-of-coding 1094 -- There is some redundancy in all this, in the interests of ease-of-coding
1079 i = { 1095 i = {
1084 quality = iquality, 1100 quality = iquality,
1085 itemname = iname, 1101 itemname = iname,
1086 id = itemid, 1102 id = itemid,
1087 itemlink = ilink, 1103 itemlink = ilink,
1088 itexture = itexture, 1104 itexture = itexture,
1105 unique = unique,
1089 count = (count and count ~= "") and count or nil, 1106 count = (count and count ~= "") and count or nil,
1090 bcast_from = from, 1107 bcast_from = from,
1091 extratext = extratext, 1108 extratext = extratext,
1092 variant = self:is_variant_item(ilink), 1109 variant = self:is_variant_item(ilink),
1093 } 1110 }
1163 person = person:match("|c%x%x%x%x%x%x%x%x(%S+)") or person 1180 person = person:match("|c%x%x%x%x%x%x%x%x(%S+)") or person
1164 else 1181 else
1165 person = my_name -- UNIT_YOU / You 1182 person = my_name -- UNIT_YOU / You
1166 end 1183 end
1167 1184
1168 --local id = tonumber((select(2, strsplit(":", itemstring)))) 1185 --local id = tonumber(itemstring:match('|Hitem:(%d+):'))
1169 local id = tonumber(itemstring:match('|Hitem:(%d+):')) 1186 local id,unique,_
1170 1187 _,id,_,_,_,_,_,_,unique = strsplit (":", itemstring)
1171 return self:_do_loot (false, person, id, count) 1188 if unique == 0 then unique = nil end
1189
1190 return self:_do_loot (false, person, unique, id, count)
1172 1191
1173 elseif event == "broadcast" then 1192 elseif event == "broadcast" then
1174 return self:_do_loot(false, ...) 1193 return self:_do_loot(false, ...)
1175 1194
1176 elseif event == "manual" then 1195 elseif event == "manual" then
1177 local r,i,n = ... 1196 local r,i,n = ...
1178 return self:_do_loot(true, r,i,nil,nil,n) 1197 return self:_do_loot(true, r, --[[unique=]]nil, i,
1198 --[[count=]]nil, --[[from=]]nil, n)
1179 end 1199 end
1180 end 1200 end
1181 end 1201 end
1182 1202
1183 1203
2055 id = e.id, 2075 id = e.id,
2056 when = self:format_timestamp (g_today, e), 2076 when = self:format_timestamp (g_today, e),
2057 count = e.count, 2077 count = e.count,
2058 } 2078 }
2059 tinsert (h, 1, n) 2079 tinsert (h, 1, n)
2060 e.history_unique = n.id .. ' ' .. n.when 2080 if (not e.unique) or (#e.unique==0) then
2081 e.unique = n.id .. ' ' .. n.when
2082 end
2083 n.unique = e.unique
2061 end 2084 end
2062 2085
2063 -- Create new history table based on current loot. 2086 -- Create new history table based on current loot.
2064 function addon:rewrite_history (realmname) 2087 function addon:rewrite_history (realmname)
2065 local r = assert(realmname) 2088 local r = assert(realmname)
2107 if loot.kind ~= 'loot' then 2130 if loot.kind ~= 'loot' then
2108 error("trying to "..operation_text.." something that isn't loot") 2131 error("trying to "..operation_text.." something that isn't loot")
2109 end 2132 end
2110 2133
2111 local player = loot.person 2134 local player = loot.person
2112 local tag = loot.history_unique 2135 local tag = loot.unique
2113 local errtxt 2136 local errtxt
2114 local player_i, player_h, hist_i 2137 local player_i, player_h, hist_i
2115 2138
2116 if not tag then 2139 if not tag then
2117 errtxt = "Entry for %s is missing a history tag!" 2140 errtxt = "Entry for %s is missing a history tag!"
2118 else 2141 else
2119 player_i,player_h = self:get_loot_history(player) 2142 player_i,player_h = self:get_loot_history(player)
2120 for i,h in ipairs(player_h) do 2143 for i,h in ipairs(player_h) do
2121 local unique = h.id .. ' ' .. h.when 2144 if h.unique == tag then
2122 if unique == tag then
2123 hist_i = i 2145 hist_i = i
2124 break 2146 break
2125 end 2147 end
2126 end 2148 end
2127 if not hist_i then 2149 if not hist_i then
2198 if (newdisp == 'shard' or newdisp == 'gvault') then 2220 if (newdisp == 'shard' or newdisp == 'gvault') then
2199 local name_i, name_h, hist_i = self:_history_by_loot_id (e, "mark") 2221 local name_i, name_h, hist_i = self:_history_by_loot_id (e, "mark")
2200 -- remove history entry 2222 -- remove history entry
2201 if hist_i then 2223 if hist_i then
2202 local hist_h = tremove (name_h, hist_i) 2224 local hist_h = tremove (name_h, hist_i)
2203 deleted_cache[e.history_unique] = hist_h 2225 deleted_cache[e.unique] = hist_h
2204 self.hist_clean = nil 2226 self.hist_clean = nil
2205 elseif (olddisp == 'shard' or olddisp == 'gvault') then 2227 elseif (olddisp == 'shard' or olddisp == 'gvault') then
2206 -- Sharding a vault item, or giving the auto-sharder something to bank, 2228 -- Sharding a vault item, or giving the auto-sharder something to bank,
2207 -- etc, wouldn't necessarily have had a history entry to begin with. 2229 -- etc, wouldn't necessarily have had a history entry to begin with.
2208 else 2230 else
2219 -- Must create a new history entry. Could call '_addHistoryEntry(index)' 2241 -- Must create a new history entry. Could call '_addHistoryEntry(index)'
2220 -- but that would duplicate a lot of effort. To start with, check the 2242 -- but that would duplicate a lot of effort. To start with, check the
2221 -- cache of stuff we've already deleted; if it's not there then just do 2243 -- cache of stuff we've already deleted; if it's not there then just do
2222 -- the same steps as _addHistoryEntry. 2244 -- the same steps as _addHistoryEntry.
2223 local entry 2245 local entry
2224 if e.history_unique and deleted_cache[e.history_unique] then 2246 if e.unique and deleted_cache[e.unique] then
2225 entry = deleted_cache[e.history_unique] 2247 entry = deleted_cache[e.unique]
2226 deleted_cache[e.history_unique] = nil 2248 deleted_cache[e.unique] = nil
2227 end 2249 end
2228 local when = g_today and self:format_timestamp (g_today, e) or tostring(e.stamp) 2250 local when = g_today and self:format_timestamp (g_today, e) or tostring(e.stamp)
2229 entry = entry or { 2251 entry = entry or {
2230 id = e.id, 2252 id = e.id,
2231 when = when, 2253 when = when,
2232 count = e.count, 2254 count = e.count,
2233 } 2255 }
2234 tinsert (name_h, 1, entry) 2256 tinsert (name_h, 1, entry)
2235 e.history_unique = e.history_unique or (entry.id .. ' ' .. entry.when) 2257 if (not e.unique) or (#e.unique==0) then
2258 e.unique = entry.id .. ' ' .. entry.when
2259 end
2260 entry.unique = e.unique
2236 self.hist_clean = nil 2261 self.hist_clean = nil
2237 return 2262 return
2238 end 2263 end
2239 end 2264 end
2240 end 2265 end
2254 local function assemble(t,...) 2279 local function assemble(t,...)
2255 if select('#',...) > 0 then 2280 if select('#',...) > 0 then
2256 local msg = {t,...} 2281 local msg = {t,...}
2257 -- tconcat requires strings, but T is known to be one already 2282 -- tconcat requires strings, but T is known to be one already
2258 for i = 2, #msg do 2283 for i = 2, #msg do
2259 msg[i] = tostring(msg[i]) or "" 2284 msg[i] = tostring(msg[i] or "")
2260 end 2285 end
2261 return tconcat (msg, '\a') 2286 return tconcat (msg, '\a')
2262 end 2287 end
2263 return t 2288 return t
2264 end 2289 end
2304 OCR_funcs.revcheck = function (sender, _, revlarge) 2329 OCR_funcs.revcheck = function (sender, _, revlarge)
2305 addon.dprint('comm', "revcheck, sender", sender) 2330 addon.dprint('comm', "revcheck, sender", sender)
2306 addon:_check_revision (revlarge) 2331 addon:_check_revision (revlarge)
2307 end 2332 end
2308 2333
2309 OCR_funcs.loot = function (sender, _, recip, item, count, extratext) 2334 OCR_funcs['16loot'] = function (sender, _, recip, item, count, extratext)
2310 addon.dprint('comm', "DOTloot, sender", sender, "recip", recip, "item", item, "count", count) 2335 addon.dprint('comm', "DOTloot/16, sender", sender, "recip", recip, "item", item, "count", count)
2311 if not addon.enabled then return end 2336 if not addon.enabled then return end
2312 adduser (sender, nil, true) 2337 adduser (sender, nil, true)
2313 addon:CHAT_MSG_LOOT ("broadcast", recip, item, count, sender, extratext) 2338 addon:CHAT_MSG_LOOT ("broadcast", recip, --[[unique=]]"", item, count, sender, extratext)
2314 end 2339 end
2315 OCR_funcs['16loot'] = OCR_funcs.loot 2340 OCR_funcs.loot = OCR_funcs['16loot'] -- old unversioned stuff goes to 16
2341 OCR_funcs['17loot'] = function (sender, _, recip, unique, item, count, extratext)
2342 addon.dprint('comm', "DOTloot, sender", sender, "recip", recip,
2343 "unique", unique, "item", item, "count", count)
2344 if not addon.enabled then return end
2345 adduser (sender, nil, true)
2346 addon:CHAT_MSG_LOOT ("broadcast", recip, unique, item, count, sender, extratext)
2347 end
2316 2348
2317 OCR_funcs.boss = function (sender, _, reason, bossname, instancetag) 2349 OCR_funcs.boss = function (sender, _, reason, bossname, instancetag)
2318 addon.dprint('comm', "DOTboss, sender", sender, "reason", reason, 2350 addon.dprint('comm', "DOTboss, sender", sender, "reason", reason,
2319 "name", bossname, "it", instancetag) 2351 "name", bossname, "it", instancetag)
2320 if not addon.enabled then return end 2352 if not addon.enabled then return end
2326 "name", bossname, "it", instancetag, "size", maxsize) 2358 "name", bossname, "it", instancetag, "size", maxsize)
2327 if not addon.enabled then return end 2359 if not addon.enabled then return end
2328 adduser (sender, nil, true) 2360 adduser (sender, nil, true)
2329 addon:on_boss_broadcast (reason, bossname, instancetag, maxsize) 2361 addon:on_boss_broadcast (reason, bossname, instancetag, maxsize)
2330 end 2362 end
2363 OCR_funcs['17boss'] = OCR_funcs['16boss']
2331 2364
2332 OCR_funcs.bcast_req = function (sender) 2365 OCR_funcs.bcast_req = function (sender)
2333 if addon.debug.comm or ((not g_wafer_thin) and (not addon.rebroadcast)) 2366 if addon.debug.comm or ((not g_wafer_thin) and (not addon.rebroadcast))
2334 then 2367 then
2335 addon:Print("%s has requested additional broadcasters! Choose %s to enable rebroadcasting, or %s to remain off and also ignore rebroadcast requests for as long as you're logged in.", 2368 addon:Print("%s has requested additional broadcasters! Choose %s to enable rebroadcasting, or %s to remain off and also ignore rebroadcast requests for as long as you're logged in.",