Mercurial > wow > ouroloot
comparison core.lua @ 114:67bf97136273
- Start paying attention to cross-realm names. New 'fname' and 'realm' keys
in snapshot (now indexed by name only), 'person_realm' key in loot entries.
Realm data not stored if it's the same as the player's realm. Manual
rebroadcasting does not include realm data (do we care?).
- New history_suppress_LFR and history_ignore_xrealm options.
- This implementation depends on no two cross-realm players sharing the same
player name. Is that guaranteed by Blizzard, or merely "unlikely"?
- Gather history suppression knobs into a single function.
- If restoring loot data, make sure the item cache has their values; fix up
any missing data on load.
- Memory tweaks for player history sorting.
- Handle some long-standing FIXME's: reassigning loot to the same player,
using expunge() for history, no partial duplication of effort for
addHistoryEntry.
- Try to be more graceful when discovering messed up history during column 3
display loops. Don't leave History tab in player-focused mode when all
that player's data have been removed elsewhere.
author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
---|---|
date | Mon, 13 Aug 2012 21:49:08 -0400 |
parents | f93c1a93923b |
children | 289c7667adab |
comparison
equal
deleted
inserted
replaced
113:d1b914bbed8b | 114:67bf97136273 |
---|---|
6 - forum saved text from forum markup window, default nil | 6 - forum saved text from forum markup window, default nil |
7 - attend saved text from raid attendence window, default nil | 7 - attend saved text from raid attendence window, default nil |
8 - printed.FOO last loot index formatted into text window FOO, default 0 | 8 - printed.FOO last loot index formatted into text window FOO, default 0 |
9 - raiders accumulating raid roster data as we see raid members; indexed | 9 - raiders accumulating raid roster data as we see raid members; indexed |
10 by player name with subtable fields: | 10 by player name with subtable fields: |
11 - fname fully-qualified name; "PlayerName" or "PlayerName-RealmName" | |
11 - class capitalized English codename ("WARRIOR", "DEATHKNIGHT", etc) | 12 - class capitalized English codename ("WARRIOR", "DEATHKNIGHT", etc) |
12 - subgroup 1-8 (NUM_RAID_GROUPS), NRG+1 if something's wrong | 13 - subgroup 1-8 (NUM_RAID_GROUPS), NRG+1 if something's wrong |
13 - race English codename ("BloodElf", etc) | 14 - race English codename ("BloodElf", etc) |
14 - sex 1 = unknown/error, 2 = male, 3 = female | 15 - sex 1 = unknown/error, 2 = male, 3 = female |
15 - level can be 0 if player was offline at the time | 16 - level can be 0 if player was offline at the time |
16 - guild guild name, or missing if unguilded | 17 - guild guild name, or missing if unguilded |
18 - realm realm name, or missing if same realm as player at the time | |
17 - online 'online', 'offline', 'no_longer' [no longer in raid group] | 19 - online 'online', 'offline', 'no_longer' [no longer in raid group] |
18 [both of these next two fields use time_t values:] | 20 [both of these next two fields use time_t values:] |
19 - join time player joined the raid (or first time we've seen them) | 21 - join time player joined the raid (or first time we've seen them) |
20 - leave time player left the raid (or time we've left the raid, if | 22 - leave time player left the raid (or time we've left the raid, if |
21 'online' is not 'no_longer') | 23 'online' is not 'no_longer') |
46 | 48 |
47 Loot specific g_loot indices: | 49 Loot specific g_loot indices: |
48 - person recipient | 50 - person recipient |
49 - person_class class of recipient if available; may be missing; | 51 - person_class class of recipient if available; may be missing; |
50 will be classID-style (e.g., DEATHKNIGHT) | 52 will be classID-style (e.g., DEATHKNIGHT) |
53 - person_realm recipient's realm if different from the player's; missing | |
54 otherwise | |
51 - itemname not including square brackets | 55 - itemname not including square brackets |
52 - id itemID as number | 56 - id itemID as number |
53 - itemlink full clickable link | 57 - itemlink full clickable link |
54 - itexture icon path (e.g., Interface\Icons\INV_Misc_Rune_01) | 58 - itexture icon path (e.g., Interface\Icons\INV_Misc_Rune_01) |
55 - quality ITEM_QUALITY_* number | 59 - quality ITEM_QUALITY_* number |
114 --['datarev'] = 20, -- cheating, this isn't actually an option | 118 --['datarev'] = 20, -- cheating, this isn't actually an option |
115 ['popup_on_join'] = true, | 119 ['popup_on_join'] = true, |
116 ['register_slash_synonyms'] = false, | 120 ['register_slash_synonyms'] = false, |
117 ['slash_synonyms'] = '/ol,/oloot', | 121 ['slash_synonyms'] = '/ol,/oloot', |
118 ['scroll_to_bottom'] = true, | 122 ['scroll_to_bottom'] = true, |
123 ['history_suppress_LFR'] = false, | |
124 ['history_ignore_xrealm'] = true, | |
119 ['gui_noob'] = true, | 125 ['gui_noob'] = true, |
120 ['chatty_on_kill'] = false, | 126 ['chatty_on_kill'] = false, |
121 ['no_tracking_wipes'] = false, | 127 ['no_tracking_wipes'] = false, |
122 ['snarky_boss'] = true, | 128 ['snarky_boss'] = true, |
123 ['keybinding'] = false, | 129 ['keybinding'] = false, |
387 local GetNumRaidMembers = GetNumGroupMembers or GetNumRaidMembers | 393 local GetNumRaidMembers = GetNumGroupMembers or GetNumRaidMembers |
388 local IsInRaid = IsInRaid or (function() return GetNumRaidMembers() > 0 end) | 394 local IsInRaid = IsInRaid or (function() return GetNumRaidMembers() > 0 end) |
389 -- En masse forward decls of symbols defined inside local blocks | 395 -- En masse forward decls of symbols defined inside local blocks |
390 local _register_bossmod, makedate, create_new_cache, _init, _log, _do_loot_metas | 396 local _register_bossmod, makedate, create_new_cache, _init, _log, _do_loot_metas |
391 local _history_by_loot_id, _setup_unique_replace, _unavoidable_collision | 397 local _history_by_loot_id, _setup_unique_replace, _unavoidable_collision |
392 local _notify_about_change | 398 local _notify_about_change, _LFR_suppressing |
393 | 399 |
394 -- Try to extract numbers from the .toc "Version" and munge them into an | 400 -- Try to extract numbers from the .toc "Version" and munge them into an |
395 -- integral form for comparison. The result doesn't need to be meaningful as | 401 -- integral form for comparison. The result doesn't need to be meaningful as |
396 -- long as we can reliably feed two of them to "<" and get useful answers. | 402 -- long as we can reliably feed two of them to "<" and get useful answers. |
397 -- | 403 -- |
509 local name, typeof, diffcode, diffstr, _, perbossheroic, isdynamic = GetInstanceInfo() | 515 local name, typeof, diffcode, diffstr, _, perbossheroic, isdynamic = GetInstanceInfo() |
510 local t, r | 516 local t, r |
511 name = addon.instance_abbrev[name] or name | 517 name = addon.instance_abbrev[name] or name |
512 if typeof == "none" then return name, MAX_RAID_MEMBERS end | 518 if typeof == "none" then return name, MAX_RAID_MEMBERS end |
513 -- diffstr is "5 Player", "10 Player (Heroic)", etc. ugh. | 519 -- diffstr is "5 Player", "10 Player (Heroic)", etc. ugh. |
514 if (GetLFGMode()) and (GetLFGModeType() == 'raid') then | 520 if GetLFGMode() and (GetLFGModeType() == 'raid') then |
515 t,r = 'LFR', 25 | 521 t,r = 'LFR', 25 |
516 elseif diffcode == 1 then | 522 elseif diffcode == 1 then |
517 if IsInRaid() then | 523 if IsInRaid() then |
518 t,r = "10",10 | 524 t,r = "10",10 |
519 else | 525 else |
536 end | 542 end |
537 return name .. "(" .. t .. ")", r | 543 return name .. "(" .. t .. ")", r |
538 end | 544 end |
539 addon.instance_tag = instance_tag -- grumble | 545 addon.instance_tag = instance_tag -- grumble |
540 addon.latest_instance = nil -- spelling reminder, assigned elsewhere | 546 addon.latest_instance = nil -- spelling reminder, assigned elsewhere |
547 | |
548 -- Whether we're recording anything at all in the loot histories | |
549 local function _history_suppress() | |
550 return _LFR_suppressing or addon.history_suppress | |
551 end | |
541 | 552 |
542 -- Memoizing cache of unique IDs as we generate or search for them. Keys are | 553 -- Memoizing cache of unique IDs as we generate or search for them. Keys are |
543 -- the uniques, values are the following: | 554 -- the uniques, values are the following: |
544 -- 'history' active player name in self.history | 555 -- 'history' active player name in self.history |
545 -- 'history_may' index into player's uniques list, CAN QUICKLY BE OUTDATED | 556 -- 'history_may' index into player's uniques list, CAN QUICKLY BE OUTDATED |
1265 local time, difftime = time, difftime | 1276 local time, difftime = time, difftime |
1266 local R_ACTIVE, R_OFFLINE, R_LEFT = 'online', 'offline', 'no_longer' | 1277 local R_ACTIVE, R_OFFLINE, R_LEFT = 'online', 'offline', 'no_longer' |
1267 | 1278 |
1268 local lastevent, now = 0, 0 | 1279 local lastevent, now = 0, 0 |
1269 local redo_count = 0 | 1280 local redo_count = 0 |
1270 local redo, timer_handle | 1281 local redo, timer_handle, my_realm |
1271 | 1282 |
1272 function addon:CheckRoster (leaving_p, now_a) | 1283 function addon:CheckRoster (leaving_p, now_a) |
1273 if not g_loot.raiders then return end -- bad transition | 1284 if not g_loot.raiders then return end -- bad transition |
1274 | 1285 |
1275 now = now_a or time() | 1286 now = now_a or time() |
1299 end | 1310 end |
1300 redo = false | 1311 redo = false |
1301 for i = 1, GetNumRaidMembers() do | 1312 for i = 1, GetNumRaidMembers() do |
1302 local unit = 'raid'..i | 1313 local unit = 'raid'..i |
1303 -- We grab a bunch of return values here, but only pay attention to | 1314 -- We grab a bunch of return values here, but only pay attention to |
1304 -- them under specific circumstances. | 1315 -- them under specific circumstances. Try to use as many of these |
1305 local name, connected, subgroup, level, class, _ | 1316 -- values as possible rather than multiple Unit* calls. |
1306 name, _, subgroup, level, _, class, connected = GetRaidRosterInfo(i) | 1317 local fname, connected, subgroup, level, class, _ |
1318 fname, _, subgroup, level, _, class, connected = GetRaidRosterInfo(i) | |
1307 -- No, that's not my typo, it really is "uknownbeing" in Blizzard's code. | 1319 -- No, that's not my typo, it really is "uknownbeing" in Blizzard's code. |
1308 if name and name ~= UNKNOWN and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING then | 1320 if fname and fname ~= UNKNOWN and fname ~= UNKNOWNOBJECT and fname ~= UKNOWNBEING then |
1321 local name,realm = fname:match("(%S+)-(%S+)") | |
1322 if realm and realm == my_realm then -- shouldn't happen | |
1323 realm = nil | |
1324 end | |
1325 if not name then | |
1326 assert(realm == nil) | |
1327 name = fname | |
1328 end | |
1309 if not g_loot.raiders[name] then | 1329 if not g_loot.raiders[name] then |
1310 g_loot.raiders[name] = { needinfo=true } | 1330 g_loot.raiders[name] = { needinfo=true } |
1311 end | 1331 end |
1312 local r = g_loot.raiders[name] | 1332 local r = g_loot.raiders[name] |
1313 r.subgroup = subgroup or (NUM_RAID_GROUPS+1) | 1333 r.subgroup = subgroup or (NUM_RAID_GROUPS+1) |
1314 if r.needinfo and UnitIsVisible(unit) then | 1334 if r.needinfo and UnitIsVisible(unit) then |
1315 r.needinfo = nil | 1335 r.needinfo = nil |
1336 r.fname = fname | |
1316 r.class = class --select(2,UnitClass(unit)) | 1337 r.class = class --select(2,UnitClass(unit)) |
1317 r.race = select(2,UnitRace(unit)) | 1338 r.race = select(2,UnitRace(unit)) |
1318 r.sex = UnitSex(unit) | 1339 r.sex = UnitSex(unit) |
1319 r.level = level --UnitLevel(unit) | 1340 r.level = level --UnitLevel(unit) |
1320 r.guild = GetGuildInfo(unit) | 1341 r.guild = GetGuildInfo(unit) |
1342 r.realm = realm | |
1321 end | 1343 end |
1322 --local connected = UnitIsConnected(unit) | 1344 --local connected = UnitIsConnected(unit) |
1323 if connected and r.online ~= R_ACTIVE then | 1345 if connected and r.online ~= R_ACTIVE then |
1324 r.join = r.join or now | 1346 r.join = r.join or now |
1325 r.online = R_ACTIVE | 1347 r.online = R_ACTIVE |
1353 self.dprint('flow', "enabled, leaving raid") | 1375 self.dprint('flow', "enabled, leaving raid") |
1354 self.popped = nil | 1376 self.popped = nil |
1355 self:Deactivate() | 1377 self:Deactivate() |
1356 self:CheckRoster(--[[leaving raid]]true) | 1378 self:CheckRoster(--[[leaving raid]]true) |
1357 end | 1379 end |
1380 _LFR_suppressing = nil | |
1358 return | 1381 return |
1359 end | 1382 end |
1360 | 1383 |
1361 local inside,whatkind = IsInInstance() | 1384 local inside,whatkind = IsInInstance() |
1362 if inside and (whatkind == "pvp" or whatkind == "arena") then | 1385 if inside and (whatkind == "pvp" or whatkind == "arena") then |
1364 return | 1387 return |
1365 end | 1388 end |
1366 | 1389 |
1367 local docheck = self.enabled | 1390 local docheck = self.enabled |
1368 if event == "Activate" then | 1391 if event == "Activate" then |
1369 -- dispatched manually from Activate | 1392 -- dispatched from Activate |
1393 if opts.history_suppress_LFR | |
1394 and GetLFGMode() and (GetLFGModeType() == 'raid') | |
1395 then | |
1396 _LFR_suppressing = true | |
1397 end | |
1398 my_realm = self.history.realm | |
1399 _register_bossmod(self) | |
1370 self:RegisterEvent("CHAT_MSG_LOOT") | 1400 self:RegisterEvent("CHAT_MSG_LOOT") |
1371 _register_bossmod(self) | |
1372 docheck = true | 1401 docheck = true |
1373 elseif event == RAID_ROSTER_UPDATE_EVENT then | 1402 elseif event == RAID_ROSTER_UPDATE_EVENT then |
1374 -- hot code path, be careful | 1403 -- hot code path, be careful |
1375 | 1404 |
1376 -- event registration from onload, joined a raid, maybe show popup | 1405 -- event registration from onload, joined a raid, maybe show popup |
1499 maybe_trash_kill_entry() -- Generate *some* kind of boss/location entry | 1528 maybe_trash_kill_entry() -- Generate *some* kind of boss/location entry |
1500 candidates[sig] = nil | 1529 candidates[sig] = nil |
1501 local looti = addon._addLootEntry(loot) | 1530 local looti = addon._addLootEntry(loot) |
1502 addon.dprint('loot', "entry", i, "was found, added at index", looti) | 1531 addon.dprint('loot', "entry", i, "was found, added at index", looti) |
1503 if addon:_test_disposition (loot.disposition, 'affects_history') | 1532 if addon:_test_disposition (loot.disposition, 'affects_history') |
1504 and (not addon.history_suppress) | 1533 and not _history_suppress() |
1505 then | 1534 then |
1506 addon:_addHistoryEntry(looti) | 1535 addon:_addHistoryEntry(looti) |
1507 elseif #loot.unique > 0 then | 1536 elseif #loot.unique > 0 then |
1508 g_uniques[loot.unique] = -- stub entry | 1537 g_uniques[loot.unique] = -- stub entry |
1509 { loot = looti, history = g_uniques.NOTFOUND } | 1538 { loot = looti, history = g_uniques.NOTFOUND } |
1589 self.dprint('loot', "remote", prefix, "<", signature, | 1618 self.dprint('loot', "remote", prefix, "<", signature, |
1590 "> already in cache, skipping from", from) | 1619 "> already in cache, skipping from", from) |
1591 break | 1620 break |
1592 end | 1621 end |
1593 | 1622 |
1623 -- Most of the time this will already be in place and filled out, | |
1624 -- but there are situations where not. Even if we can't get data | |
1625 -- back, at least avoid errors (or the need for existence checks). | |
1626 if not g_loot.raiders[recipient] then | |
1627 g_loot.raiders[recipient] = { needinfo=true } | |
1628 end | |
1629 | |
1594 -- There is some redundancy in all this, in the interests of ease-of-coding | 1630 -- There is some redundancy in all this, in the interests of ease-of-coding |
1595 i = { | 1631 i = { |
1596 kind = 'loot', | 1632 kind = 'loot', |
1597 person = recipient, | 1633 person = recipient, |
1598 person_class= select(2,UnitClass(recipient)), | 1634 person_class= g_loot.raiders[recipient].class, |
1635 person_realm= g_loot.raiders[recipient].realm, | |
1599 cache_miss = cache_miss, | 1636 cache_miss = cache_miss, |
1600 quality = iquality, | 1637 quality = iquality, |
1601 itemname = iname, | 1638 itemname = iname, |
1602 id = itemid, | 1639 id = itemid, |
1603 itemlink = ilink, | 1640 itemlink = ilink, |
1624 end | 1661 end |
1625 end | 1662 end |
1626 end | 1663 end |
1627 local looti = self._addLootEntry(i) | 1664 local looti = self._addLootEntry(i) |
1628 if self:_test_disposition (i.disposition, 'affects_history') | 1665 if self:_test_disposition (i.disposition, 'affects_history') |
1629 and (not self.history_suppress) | 1666 and not _history_suppress() |
1630 then | 1667 then |
1631 self:_addHistoryEntry(looti) | 1668 self:_addHistoryEntry(looti) |
1632 else | 1669 else |
1633 g_uniques[i.unique] = -- stub entry | 1670 g_uniques[i.unique] = -- stub entry |
1634 { loot = looti, history = g_uniques.NOTFOUND } | 1671 { loot = looti, history = g_uniques.NOTFOUND } |
1798 self:Print[['/ouroloot fix cache' updates loot that wasn't in the cache]] | 1835 self:Print[['/ouroloot fix cache' updates loot that wasn't in the cache]] |
1799 self:Print[['/ouroloot fix history' repairs inconsistent data on the History tab]] | 1836 self:Print[['/ouroloot fix history' repairs inconsistent data on the History tab]] |
1800 self:Print[['/ouroloot fix' changes no stored data, only allows the window to be displayed again (this is built into all fixes above)]] | 1837 self:Print[['/ouroloot fix' changes no stored data, only allows the window to be displayed again (this is built into all fixes above)]] |
1801 return | 1838 return |
1802 elseif arg == "cache" then | 1839 elseif arg == "cache" then |
1803 self:do_item_cache_fixup() | 1840 self:do_item_cache_fixup(--[[force_silent=]]false) |
1804 elseif arg == "history" then | 1841 elseif arg == "history" then |
1805 self:repair_history_integrity() | 1842 self:repair_history_integrity() |
1806 end | 1843 end |
1807 self.NOLOAD = nil | 1844 self.NOLOAD = nil |
1808 self:Print("Window unlocked, best of luck.") | 1845 self:Print("Window unlocked, best of luck.") |
1841 self.dprint('flow', ":Activate is running") | 1878 self.dprint('flow', ":Activate is running") |
1842 self:RegisterEvent(RAID_ROSTER_UPDATE_EVENT,"RAID_ROSTER_UPDATE") | 1879 self:RegisterEvent(RAID_ROSTER_UPDATE_EVENT,"RAID_ROSTER_UPDATE") |
1843 self:RegisterEvent("PLAYER_ENTERING_WORLD", | 1880 self:RegisterEvent("PLAYER_ENTERING_WORLD", |
1844 function() self:ScheduleTimer("RAID_ROSTER_UPDATE", 5, "PLAYER_ENTERING_WORLD") end) | 1881 function() self:ScheduleTimer("RAID_ROSTER_UPDATE", 5, "PLAYER_ENTERING_WORLD") end) |
1845 self.popped = true | 1882 self.popped = true |
1883 if self.DO_ITEMID_FIX then | |
1884 self.DO_ITEMID_FIX = nil | |
1885 self:do_item_cache_fixup(--[[force_silent=]]not self.author_debug) | |
1886 end | |
1846 if IsInRaid() then | 1887 if IsInRaid() then |
1847 self.dprint('flow', ">:Activate calling RRU") | 1888 self.dprint('flow', ">:Activate calling RRU") |
1848 self:RAID_ROSTER_UPDATE("Activate") | 1889 self:RAID_ROSTER_UPDATE("Activate") |
1849 elseif self.debug.notraid then | 1890 elseif self.debug.notraid then |
1850 self.dprint('flow', ">:(notraid) Activate registering loot and bossmods") | 1891 self.dprint('flow', ">:(notraid) Activate registering loot and bossmods") |
1889 self.enabled = false | 1930 self.enabled = false |
1890 self.rebroadcast = false | 1931 self.rebroadcast = false |
1891 self:UnregisterEvent(RAID_ROSTER_UPDATE_EVENT) | 1932 self:UnregisterEvent(RAID_ROSTER_UPDATE_EVENT) |
1892 self:UnregisterEvent("PLAYER_ENTERING_WORLD") | 1933 self:UnregisterEvent("PLAYER_ENTERING_WORLD") |
1893 self:UnregisterEvent("CHAT_MSG_LOOT") | 1934 self:UnregisterEvent("CHAT_MSG_LOOT") |
1935 _LFR_suppressing = nil | |
1894 self:Fire ('Deactivate') | 1936 self:Fire ('Deactivate') |
1895 self:Print("Deactivated.") | 1937 self:Print("Deactivated.") |
1896 end | 1938 end |
1897 | 1939 |
1898 function addon:Clear(verbose_p) | 1940 function addon:Clear(verbose_p) |
2169 self.hist_clean = nil | 2211 self.hist_clean = nil |
2170 if g_restore_p then | 2212 if g_restore_p then |
2171 g_loot = _G.OuroLootSV | 2213 g_loot = _G.OuroLootSV |
2172 self.popped = #g_loot > 0 | 2214 self.popped = #g_loot > 0 |
2173 self.dprint('flow', "restoring", #g_loot, "entries") | 2215 self.dprint('flow', "restoring", #g_loot, "entries") |
2216 -- paranoia: make sure the GUI isn't stumbling over these later | |
2217 local dofix, GetItemInfo = false, GetItemInfo | |
2218 for i,e in self:filtered_loot_iter('loot') do | |
2219 local missing_data = not GetItemInfo(e.id) | |
2220 e.cache_miss = (e.cache_miss or missing_data) or nil | |
2221 dofix = dofix or e.cache_miss | |
2222 end | |
2223 self.DO_ITEMID_FIX = dofix or nil | |
2174 _do_loot_metas() | 2224 _do_loot_metas() |
2175 self:ScheduleTimer("Activate", 12, opts.threshold) | 2225 self:ScheduleTimer("Activate", 12, opts.threshold) |
2176 -- FIXME printed could be too large if entries were deleted, how much do we care? | 2226 -- FIXME printed could be too large if entries were deleted, how much do we care? |
2177 self.sharder = opts.autoshard | 2227 self.sharder = opts.autoshard |
2178 else | 2228 else |
2652 end | 2702 end |
2653 end | 2703 end |
2654 | 2704 |
2655 -- In the rare case of items getting put into the loot table without current | 2705 -- In the rare case of items getting put into the loot table without current |
2656 -- item cache data (which will have arrived by now). | 2706 -- item cache data (which will have arrived by now). |
2657 function addon:do_item_cache_fixup() | 2707 function addon:do_item_cache_fixup (silent_p) |
2658 self:Print("Fixing up missing item cache data...") | 2708 if not silent_p then |
2709 self:Print("Fixing up missing item cache data...") | |
2710 end | |
2659 | 2711 |
2660 local numfound = 0 | 2712 local numfound = 0 |
2713 local earliest_fixed | |
2661 local borkedpat = '^'..UNKNOWN..': (%S+)' | 2714 local borkedpat = '^'..UNKNOWN..': (%S+)' |
2662 | 2715 |
2663 -- 'while true' so that we can use (inner) break as (outer) continue | 2716 -- 'while true' so that we can use (inner) break as (outer) continue |
2664 for i,e in self:filtered_loot_iter('loot') do while true do | 2717 for i,e in self:filtered_loot_iter('loot') do while true do |
2665 if not e.cache_miss then break end | 2718 if not e.cache_miss then break end |
2666 local borked_id = e.itemname:match(borkedpat) | 2719 local borked_id = e.itemname:match(borkedpat) or e.id |
2667 if not borked_id then break end | 2720 if not borked_id then break end |
2668 numfound = numfound + 1 | 2721 numfound = numfound + 1 |
2669 -- Best to use the safest and most flexible API here, which is GII and | 2722 -- Best to use the safest and most flexible API here, which is GII and |
2670 -- its assload of return values. | 2723 -- its assload of return values. |
2671 local iname, ilink, iquality, _,_,_,_,_,_, itexture = GetItemInfo(borked_id) | 2724 local iname, ilink, iquality, _,_,_,_,_,_, itexture = GetItemInfo(borked_id) |
2688 if player_i then | 2741 if player_i then |
2689 player_h.id[e.unique] = e.id | 2742 player_h.id[e.unique] = e.id |
2690 msg = [[ Entry %d (and history) patched up with %s.]] | 2743 msg = [[ Entry %d (and history) patched up with %s.]] |
2691 end | 2744 end |
2692 end | 2745 end |
2693 self:Print(msg, i, ilink) | 2746 earliest_fixed = earliest_fixed or i |
2747 if not silent_p then self:Print(msg, i, ilink) end | |
2694 end | 2748 end |
2695 break | 2749 break |
2696 end end | 2750 end end |
2697 | 2751 |
2698 self:Print("...finished. Found %d |4entry:entries; with weird data.", numfound) | 2752 if earliest_fixed then |
2753 self.loot_clean = earliest_fixed-1 -- this works out even at i == 1 | |
2754 end | |
2755 if not silent_p then | |
2756 self:Print("...finished. Found %d |4entry:entries; with weird data.", numfound) | |
2757 end | |
2699 end | 2758 end |
2700 | 2759 |
2701 do | 2760 do |
2702 local gur | 2761 local gur |
2703 | 2762 |
2947 -- }, ...... | 3006 -- }, ...... |
2948 -- }, | 3007 -- }, |
2949 -- ["OtherRealm"] = ...... | 3008 -- ["OtherRealm"] = ...... |
2950 -- } | 3009 -- } |
2951 -- | 3010 -- |
2952 -- Up through 2.81.4 (specifically through rev 95), an individual player's | 3011 -- Up through 2.18.4 (specifically through rev 95), an individual player's |
2953 -- table looked like this: | 3012 -- table looked like this: |
2954 -- ["name"] = "Farmbuyer", | 3013 -- ["name"] = "Farmbuyer", |
2955 -- [1] = { id = nnnnn, when = "formatted timestamp for displaying" } -- most recent loot | 3014 -- -- most recent loot: |
2956 -- [2] = { ......., [count = "x3"] } -- previous loot | 3015 -- [1] = { id = nnnnn, when = "formatted timestamp for displaying" } |
3016 -- -- previous loot: | |
3017 -- [2] = { ......., [count = "x3"] } | |
2957 -- which was much easier to manipulate, but had a ton of memory overhead. | 3018 -- which was much easier to manipulate, but had a ton of memory overhead. |
2958 do | 3019 do |
3020 local new, del, date = flib.new, flib.del, date | |
3021 | |
2959 -- Sorts a player's history from newest to oldest, according to the | 3022 -- Sorts a player's history from newest to oldest, according to the |
2960 -- formatted timestamp. This is expensive, and destructive for P.unique. | 3023 -- formatted timestamp. This is expensive, and destructive for P.unique. |
2961 local function compare_timestamps (L, R) | 3024 local function compare_timestamps (L, R) |
2962 return L > R -- reverse of normal order, newest first | 3025 return L > R -- reverse of normal order, newest first |
2963 end | 3026 end |
2964 local function sort_player (p) | 3027 local function sort_player (p) |
2965 local new_uniques, uniques_bywhen, when_array = {}, {}, {} | 3028 local new_uniques, uniques_bywhen, when_array = new(), new(), new() |
2966 for u,tstamp in pairs(p.when) do | 3029 for u,tstamp in pairs(p.when) do |
2967 uniques_bywhen[tstamp] = u | 3030 uniques_bywhen[tstamp] = u |
2968 when_array[#when_array+1] = tstamp | 3031 when_array[#when_array+1] = tstamp |
2969 end | 3032 end |
2970 table.sort (when_array, compare_timestamps) | 3033 table.sort (when_array, compare_timestamps) |
2971 for i,tstamp in ipairs(when_array) do | 3034 for i,tstamp in ipairs(when_array) do |
2972 new_uniques[i] = uniques_bywhen[tstamp] | 3035 new_uniques[i] = uniques_bywhen[tstamp] |
2973 end | 3036 end |
2974 p.unique = new_uniques | 3037 p.unique = new_uniques |
3038 del(new_uniques) del(uniques_bywhen) del(when_array) | |
2975 end | 3039 end |
2976 | 3040 |
2977 function addon:repair_history_integrity() | 3041 function addon:repair_history_integrity() |
2978 local rcount, pcount, hcount, errors = 0, 0, 0, 0 | 3042 local rcount, pcount, hcount, errors = 0, 0, 0, 0 |
2979 local empties_to_delete = {} | 3043 local empties_to_delete = {} |
2996 end | 3060 end |
2997 hcount = hcount + 1 | 3061 hcount = hcount + 1 |
2998 end | 3062 end |
2999 player.id, player.when, player.unique, player.count = | 3063 player.id, player.when, player.unique, player.count = |
3000 id, when, unique, count | 3064 id, when, unique, count |
3001 player.person_class = g_loot.raiders[player.name] | 3065 player.person_class = player.person_class or |
3002 and g_loot.raiders[player.name].class | 3066 (g_loot.raiders[player.name] and g_loot.raiders[player.name].class) |
3003 if #player.unique > 1 then | 3067 if #player.unique > 1 then |
3004 sort_player(player) | 3068 sort_player(player) |
3005 elseif #player.unique == 0 then | 3069 elseif #player.unique == 0 then |
3006 tinsert (empties_to_delete, 1, pk) | 3070 tinsert (empties_to_delete, 1, pk) |
3007 end | 3071 end |
3149 self.history.byname[name] = i | 3213 self.history.byname[name] = i |
3150 end | 3214 end |
3151 return i, self.history[i] | 3215 return i, self.history[i] |
3152 end | 3216 end |
3153 | 3217 |
3154 -- Prepends data from the loot entry at LOOTINDEX to be the new most | 3218 -- Prepends data from the loot entry at LOOTINDEX to the history for that |
3155 -- recent history entry for that player. | 3219 -- player, making it the "most recent" entry regardless of actual data. |
3156 function addon:_addHistoryEntry (lootindex) | 3220 -- In the usual case, builds a formatted timestamp string from g_today and |
3221 -- the loot entry's recorded time (thus the formatted string really *will* | |
3222 -- be the most recent entry). If g_today has not been set, then falls | |
3223 -- back on formatting LOOTINDEX's time_t 'stamp' field. | |
3224 -- | |
3225 -- If RESORT_P is true-valued, then re-sorts the player's history based on | |
3226 -- formatted timestmps, instead of leaving the new entry as the latest. | |
3227 function addon:_addHistoryEntry (lootindex, resort_p) | |
3157 local e = g_loot[lootindex] | 3228 local e = g_loot[lootindex] |
3158 if e.kind ~= 'loot' then return end | 3229 if e.kind ~= 'loot' then return end |
3159 | 3230 |
3231 if e.person_realm and opts.history_ignore_xrealm then | |
3232 return | |
3233 end | |
3234 | |
3160 local i,h = self:get_loot_history(e.person) | 3235 local i,h = self:get_loot_history(e.person) |
3161 local when = self:format_timestamp (g_today, e) | 3236 -- If we've added anything at all into g_loot this session, g_today |
3237 -- will be set. If we've logged on simply to manipulate history, then | |
3238 -- try and fake a timestamp (it'll be "close enough"). | |
3239 local when = g_today and self:format_timestamp (g_today,e) | |
3240 or date("%Y/%m/%d %H:%M",e.stamp) | |
3162 assert(h.name==e.person) | 3241 assert(h.name==e.person) |
3163 | 3242 |
3164 -- If any of these change, update the end of history_handle_disposition. | 3243 -- Should rarely happen anymore: |
3165 if (not e.unique) or (#e.unique==0) then | 3244 if (not e.unique) or (#e.unique==0) then |
3166 e.unique = e.id .. e.person .. when | 3245 e.unique = e.id .. e.person .. when |
3167 end | 3246 end |
3168 local U = e.unique | 3247 local U = e.unique |
3169 tinsert (h.unique, 1, U) | 3248 tinsert (h.unique, 1, U) |
3170 h.when[U] = when | 3249 h.when[U] = when |
3171 h.id[U] = e.id | 3250 h.id[U] = e.id |
3172 h.count[U] = e.count | 3251 h.count[U] = e.count |
3173 | 3252 if resort_p then |
3253 sort_player(h) | |
3254 end | |
3174 g_uniques[U] = { loot = lootindex, history = e.person } | 3255 g_uniques[U] = { loot = lootindex, history = e.person } |
3175 self:Fire ('NewHistory', e.person, U) | 3256 self:Fire ('NewHistory', e.person, U) |
3176 end | 3257 end |
3177 | 3258 |
3178 -- Create new history table based on current loot. | 3259 -- Create new history table based on current loot. |
3274 end | 3355 end |
3275 end | 3356 end |
3276 | 3357 |
3277 if not errtxt then | 3358 if not errtxt then |
3278 -- The cache finder got a hit, but now it's gone? WTF? | 3359 -- The cache finder got a hit, but now it's gone? WTF? |
3279 errtxt = "ZOMG! %s was in history but now is gone. Possibly your history tables have been corrupted and should be recreated. This is likely a bug. Tell Farmbuyer what steps you took to cause this." | 3360 errtxt = "ZOMG! %s was in history but now is gone. Possibly your history tables have been corrupted and should be recreated. This is likely a bug. Tell Farmbuyer what steps you took to cause this, with as many details as possible." |
3280 end | 3361 end |
3281 return nil, errtxt | 3362 return nil, errtxt |
3282 end | 3363 end |
3283 | 3364 |
3284 -- Handles reassigning loot between players. Arguments depend on who's | 3365 -- Handles reassigning loot between players. Arguments depend on who's |
3287 -- "remote", sender, unique_id, item_id, old_recipient, new_recipient | 3368 -- "remote", sender, unique_id, item_id, old_recipient, new_recipient |
3288 -- In the local case, must also broadcast a trigger. In the remote case, | 3369 -- In the local case, must also broadcast a trigger. In the remote case, |
3289 -- must figure out the corresponding loot entry (if it exists). In both | 3370 -- must figure out the corresponding loot entry (if it exists). In both |
3290 -- cases, must update history appropriately. Returns nil if anything odd | 3371 -- cases, must update history appropriately. Returns nil if anything odd |
3291 -- happens; returns the affected loot index on success. | 3372 -- happens; returns the affected loot index on success. |
3292 --function addon:reassign_loot (index, to_name) | |
3293 function addon:reassign_loot (how, ...) | 3373 function addon:reassign_loot (how, ...) |
3294 -- This must all be filled out in all cases: | 3374 -- This must all be filled out in all cases: |
3295 local e, index, from_name, to_name, unique, id | 3375 local e, index, from_name, to_name, unique, id |
3296 -- Only set in remote case: | 3376 -- Only set in remote case: |
3297 local sender | 3377 local sender |
3353 end | 3433 end |
3354 else | 3434 else |
3355 -- XXX do some sanity checks here? from_name == from_h.name, etc? | 3435 -- XXX do some sanity checks here? from_name == from_h.name, etc? |
3356 -- If something were wrong at this point, what could we do? | 3436 -- If something were wrong at this point, what could we do? |
3357 | 3437 |
3358 local U = tremove (from_h.unique, hist_i) | 3438 -- the Book of Job 1:21: "Naked I came from my faction capital |
3359 -- The loot master giveth... | 3439 -- city, and naked I shall return thither." |
3360 to_h.unique[#to_h.unique+1] = U | 3440 if from_h ~= to_h then |
3361 to_h.when[U] = from_h.when[U] | 3441 local U = tremove (from_h.unique, hist_i) |
3362 to_h.id[U] = from_h.id[U] | 3442 -- "The loot master giveth..." |
3363 to_h.count[U] = from_h.count[U] | 3443 to_h.unique[#to_h.unique+1] = U |
3364 sort_player(to_h) | 3444 to_h.when[U] = from_h.when[U] |
3365 -- ...and the loot master taketh away. | 3445 to_h.id[U] = from_h.id[U] |
3366 from_h.when[U] = nil | 3446 to_h.count[U] = from_h.count[U] |
3367 from_h.id[U] = nil | 3447 sort_player(to_h) |
3368 from_h.count[U] = nil | 3448 -- "...and the loot master taketh away." |
3369 -- Blessed be the lookup cache of the loot master. | 3449 from_h.when[U] = nil |
3450 from_h.id[U] = nil | |
3451 from_h.count[U] = nil | |
3452 end | |
3453 -- "Blessed be the lookup cache of the loot master." | |
3370 g_uniques[U] = { loot = index, history = to_name } | 3454 g_uniques[U] = { loot = index, history = to_name } |
3371 end | 3455 end |
3372 local from_person_class = e.person_class or from_h.person_class | 3456 local from_person_class = e.person_class or from_h.person_class |
3373 or (g_loot.raiders[from_name] and g_loot.raiders[from_name].class) | 3457 or (g_loot.raiders[from_name] and g_loot.raiders[from_name].class) |
3374 or select(2,UnitClass(from_name)) | 3458 or select(2,UnitClass(from_name)) |
3454 return 0 | 3538 return 0 |
3455 end | 3539 end |
3456 | 3540 |
3457 -- Any extra work for the "Mark as <x>" dropdown actions. The | 3541 -- Any extra work for the "Mark as <x>" dropdown actions. The |
3458 -- corresponding <x> will already have been assigned in the loot entry. | 3542 -- corresponding <x> will already have been assigned in the loot entry. |
3459 local deleted_cache = {} | |
3460 function addon:history_handle_disposition (index, olddisp) | 3543 function addon:history_handle_disposition (index, olddisp) |
3461 local e = g_loot[index] | 3544 local e = g_loot[index] |
3462 -- Standard disposition has a nil entry, but that's tedious in debug | 3545 -- Standard disposition has a nil entry, but that's tedious in debug |
3463 -- output, so force to a string instead. | 3546 -- output, so force to a string instead. |
3464 olddisp = olddisp or 'normal' | 3547 olddisp = olddisp or 'normal' |
3467 if olddisp == newdisp then return end | 3550 if olddisp == newdisp then return end |
3468 | 3551 |
3469 local name = e.person | 3552 local name = e.person |
3470 | 3553 |
3471 if not self:_test_disposition (newdisp, 'affects_history') then | 3554 if not self:_test_disposition (newdisp, 'affects_history') then |
3555 -- remove history entry if it exists | |
3472 local name_i, name_h, hist_i = _history_by_loot_id (e, "mark") | 3556 local name_i, name_h, hist_i = _history_by_loot_id (e, "mark") |
3473 -- remove history entry if it exists | |
3474 -- FIXME revist this and use expunge | |
3475 if hist_i then | 3557 if hist_i then |
3476 local c = flib.new() | 3558 -- clears g_uniques and fires DelHistory |
3477 local hist_u = tremove (name_h.unique, hist_i) | 3559 expunge (name_h, hist_i) |
3478 c.when = name_h.when[hist_u] | |
3479 c.id = name_h.id[hist_u] | |
3480 c.count = name_h.count[hist_u] | |
3481 deleted_cache[hist_u] = c | |
3482 name_h.when[hist_u] = nil | |
3483 name_h.id[hist_u] = nil | |
3484 name_h.count[hist_u] = nil | |
3485 self.hist_clean = nil | |
3486 elseif not self:_test_disposition (olddisp, 'affects_history') then | 3560 elseif not self:_test_disposition (olddisp, 'affects_history') then |
3487 -- Sharding a vault item, or giving the auto-sharder something to bank, | 3561 -- Sharding a vault item, or giving the auto-sharder something to bank, |
3488 -- etc, wouldn't necessarily have had a history entry to begin with. | 3562 -- etc, wouldn't necessarily have had a history entry to begin with. |
3489 -- So this isn't treated as an error. | 3563 -- So this isn't treated as an error. |
3490 else | 3564 else |
3494 end | 3568 end |
3495 | 3569 |
3496 if (not self:_test_disposition (olddisp, 'affects_history')) | 3570 if (not self:_test_disposition (olddisp, 'affects_history')) |
3497 and self:_test_disposition (newdisp, 'affects_history') | 3571 and self:_test_disposition (newdisp, 'affects_history') |
3498 then | 3572 then |
3573 -- Must create a new history entry. | |
3499 local name_i, name_h = self:get_loot_history(name) | 3574 local name_i, name_h = self:get_loot_history(name) |
3500 | 3575 -- puts entry into g_uniques and fires NewHistory |
3501 -- Must create a new history entry. Could call '_addHistoryEntry(index)' | 3576 self:_addHistoryEntry (index, --[[re-sort entries=]]true) |
3502 -- but that would duplicate a lot of effort. To start with, check the | |
3503 -- cache of stuff we've already deleted; if it's not there then just do | |
3504 -- the same steps as _addHistoryEntry. | |
3505 -- FIXME The deleted cache isn't nearly as useful now with the new data structures. | |
3506 local when | |
3507 if (not e.unique) or (#e.unique==0) then | |
3508 when = g_today and self:format_timestamp (g_today, e) or date("%Y/%m/%d %H:%M",e.stamp) | |
3509 e.unique = e.id .. name .. when | |
3510 end | |
3511 local U = e.unique | |
3512 local c = deleted_cache[U] | |
3513 deleted_cache[U] = nil | |
3514 name_h.unique[#name_h.unique+1] = U | |
3515 name_h.when[U] = c and c.when or when or date("%Y/%m/%d %H:%M",e.stamp) | |
3516 name_h.id[U] = e.id -- c.id | |
3517 name_h.count[U] = c and c.count or e.count | |
3518 sort_player(name_h) | |
3519 g_uniques[U] = { loot = index, history = name } | |
3520 self.hist_clean = nil | 3577 self.hist_clean = nil |
3521 | |
3522 if c then flib.del(c) end | |
3523 | |
3524 return | 3578 return |
3525 end | 3579 end |
3526 end | 3580 end |
3527 | 3581 |
3528 -- This is not entirely "history" but not completely anything else either. | 3582 -- This is not entirely "history" but not completely anything else either. |