Mercurial > wow > ouroloot
comparison core.lua @ 102:fe04f5c4114a
- expiring cache callback arg, handle new entries coming in during the
same loop at the previous ones ALL being removed
TODO: rework other "candidate" caches to use this instead
- raider snapshots use string tokens for 'online' field
- document callback args
- CheckBoxSmallLabel preemptively apply proposed fix for ace3 ticket 304
- now that option toggles are not wedged into tight space, revert to
using standard CheckBox widget instead of small-label variant
(go back if ticket 304 is not fixed for release)
author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
---|---|
date | Sat, 04 Aug 2012 22:03:05 +0000 |
parents | f7162a1cadc7 |
children | dc8a23a47b03 |
comparison
equal
deleted
inserted
replaced
101:f7162a1cadc7 | 102:fe04f5c4114a |
---|---|
12 - subgroup 1-8 (NUM_RAID_GROUPS), NRG+1 if something's wrong | 12 - subgroup 1-8 (NUM_RAID_GROUPS), NRG+1 if something's wrong |
13 - race English codename ("BloodElf", etc) | 13 - race English codename ("BloodElf", etc) |
14 - sex 1 = unknown/error, 2 = male, 3 = female | 14 - sex 1 = unknown/error, 2 = male, 3 = female |
15 - level can be 0 if player was offline at the time | 15 - level can be 0 if player was offline at the time |
16 - guild guild name, or missing if unguilded | 16 - guild guild name, or missing if unguilded |
17 - online 1 = online, 2 = offline, 3 = no longer in raid | 17 - online 'online', 'offline', 'no_longer' [no longer in raid group] |
18 [both of these next two fields use time_t values:] | 18 [both of these next two fields use time_t values:] |
19 - join time player joined the raid (or first time we've seen them) | 19 - 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 | 20 - leave time player left the raid (or time we've left the raid, if |
21 'online' is not 3) | 21 'online' is not 'no_longer') |
22 | 22 |
23 Common g_loot entry indices: | 23 Common g_loot entry indices: |
24 - kind time/boss/loot | 24 - kind time/boss/loot |
25 - hour 0-23, on the *physical instance server*, not the realm server | 25 - hour 0-23, on the *physical instance server*, not the realm server |
26 - minute 0-59, ditto | 26 - minute 0-59, ditto |
75 ------ Saved variables | 75 ------ Saved variables |
76 ------ Constants | 76 ------ Constants |
77 ------ Addon member data | 77 ------ Addon member data |
78 ------ Globals | 78 ------ Globals |
79 ------ Expiring caches | 79 ------ Expiring caches |
80 ------ Ace3 framework stuff | 80 ------ Ace3 framework stuff (callback 'events', search for LCALLBACK) |
81 ------ Event handlers | 81 ------ Event handlers |
82 ------ Slash command handler | 82 ------ Slash command handler |
83 ------ On/off | 83 ------ On/off |
84 ------ Behind the scenes routines | 84 ------ Behind the scenes routines |
85 ------ Saved texts | 85 ------ Saved texts |
99 | 99 |
100 ------ Saved variables | 100 ------ Saved variables |
101 OuroLootSV = nil -- possible copy of g_loot | 101 OuroLootSV = nil -- possible copy of g_loot |
102 OuroLootSV_saved = nil -- table of copies of saved texts, default nil; keys | 102 OuroLootSV_saved = nil -- table of copies of saved texts, default nil; keys |
103 -- are numeric indices of tables, subkeys of those | 103 -- are numeric indices of tables, subkeys of those |
104 -- are name/forum/attend/date | 104 -- are name/forum/attend/date |
105 OuroLootSV_hist = nil | 105 OuroLootSV_hist = nil |
106 OuroLootSV_log = {} | 106 OuroLootSV_log = {} |
107 | 107 |
108 | 108 |
109 ------ Constants | 109 ------ Constants |
627 cache:add(bar) | 627 cache:add(bar) |
628 ....10 seconds pass | 628 ....10 seconds pass |
629 cache:test(foo) -- returns false | 629 cache:test(foo) -- returns false |
630 cache:test(bar) -- returns true | 630 cache:test(bar) -- returns true |
631 ....5 seconds pass | 631 ....5 seconds pass |
632 ....bar also gone, cleanup() called | 632 ....bar also gone, cleanup() called: |
633 function cleanup (expired_entries) | |
634 for i = 1, #expired_entries do -- this table is in strict FIFO order | |
635 print(i, expired_entries[i]) | |
636 -- 1 foo | |
637 -- 2 bar | |
638 end | |
639 end | |
633 ]] | 640 ]] |
634 do | 641 do |
635 local caches = {} | 642 local caches = {} |
636 local cleanup_group = _G.AnimTimerFrame:CreateAnimationGroup() | 643 local cleanup_group = _G.AnimTimerFrame:CreateAnimationGroup() |
637 local time = _G.time | 644 local time, next = _G.time, _G.next |
645 local new, del = flib.new, flib.del | |
638 cleanup_group:SetLooping("REPEAT") | 646 cleanup_group:SetLooping("REPEAT") |
639 cleanup_group:SetScript("OnLoop", function(cg) | 647 cleanup_group:SetScript("OnLoop", function(cg) |
640 addon.dprint('cache',"OnLoop firing") | 648 addon.dprint('cache',"OnLoop firing") |
641 local now = time() | 649 local now = time() |
642 local alldone = true | 650 local actives = 0 |
651 local expired = new() | |
643 -- this is ass-ugly | 652 -- this is ass-ugly |
644 for name,c in pairs(caches) do | 653 for name,c in next, caches do |
645 local fifo = c.fifo | 654 local fifo = c.fifo |
646 local active = #fifo > 0 | 655 local active = #fifo > 0 |
656 actives = actives + (active and 1 or 0) | |
647 while (#fifo > 0) and (now > fifo[1].t) do | 657 while (#fifo > 0) and (now > fifo[1].t) do |
648 addon.dprint('cache', name, "cache removing", fifo[1].t, "<", fifo[1].m, ">") | 658 local datum = tremove(fifo,1) |
649 tremove(fifo,1) | 659 addon.dprint('cache', name, "cache removing", datum.t, "<", datum.m, ">") |
660 c.hash[datum.m] = nil | |
661 tinsert(expired,datum.m) | |
662 del(datum) | |
650 end | 663 end |
651 if active and #fifo == 0 and c.func then | 664 if active and #fifo == 0 and c.func then |
652 addon.dprint('cache', name, "empty, firing cleanup") | 665 addon.dprint('cache', name, "empty, firing cleanup") |
653 c:func() | 666 c.func(expired) |
654 end | 667 end |
655 alldone = alldone and (#fifo == 0) | 668 wipe(expired) |
656 end | 669 end |
657 if alldone then | 670 del(expired) |
671 if actives == 0 then | |
658 addon.dprint('cache',"OnLoop FINISHING animation group") | 672 addon.dprint('cache',"OnLoop FINISHING animation group") |
659 cleanup_group:Finish() | 673 cleanup_group:Finish() |
660 _G.collectgarbage() | |
661 else | 674 else |
662 addon.dprint('cache',"OnLoop done, not yet finished") | 675 addon.dprint('cache',"OnLoop done, not yet finished") |
663 end | 676 end |
664 end) | 677 end) |
665 | 678 |
666 local function _add (cache, x) | 679 local function _add (cache, x) |
667 local datum = { t=time()+cache.ttl, m=x } | 680 assert(type(x)~='number') |
681 local datum = new() | |
682 datum.m = x | |
683 datum.t = time() + cache.ttl | |
668 cache.hash[x] = datum | 684 cache.hash[x] = datum |
669 tinsert (cache.fifo, datum) | 685 tinsert (cache.fifo, datum) |
670 if not cleanup_group:IsPlaying() then | 686 if not cleanup_group:IsPlaying() then |
671 addon.dprint('cache', cache.name, "with entry", datum.t, "<", datum.m, "> STARTING animation group") | 687 addon.dprint('cache', cache.name, "with entry", datum.t, "<", datum.m, "> STARTING animation group") |
672 cache.cleanup:SetDuration(1) -- hmmm | |
673 cleanup_group:Play() | 688 cleanup_group:Play() |
674 end | 689 end |
675 end | 690 end |
676 local function _test (cache, x) | 691 local function _test (cache, x) |
677 -- FIXME This can return false positives, if called after the onloop | |
678 -- fifo has been removed but before the GC has removed the weak entry. | |
679 -- What to do, what to do... try forcing a GC during alldone. | |
680 return cache.hash[x] ~= nil | 692 return cache.hash[x] ~= nil |
681 end | 693 end |
682 | 694 |
683 function create_new_cache (name, ttl, on_alldone) | 695 function create_new_cache (name, ttl, on_alldone) |
684 -- setting OnFinished for cleanup fires at the end of each inner loop, | 696 -- setting OnFinished for cleanup fires at the end of each inner loop, |
685 -- with no 'requested' argument to distinguish cases. thus, on_alldone. | 697 -- with no 'requested' argument to distinguish cases. thus, on_alldone. |
686 -- FWIW, on_alldone is passed this table as its sole argument: | |
687 local c = { | 698 local c = { |
688 ttl = ttl, | 699 ttl = ttl, |
689 name = name, | 700 name = name, |
690 add = _add, | 701 add = _add, |
691 test = _test, | 702 test = _test, |
692 cleanup = cleanup_group:CreateAnimation("Animation"), | 703 cleanup = cleanup_group:CreateAnimation("Animation"), |
693 func = on_alldone, | 704 func = on_alldone, |
705 -- Testing merging these two (_add's 'x' must not be numeric) | |
694 fifo = {}, | 706 fifo = {}, |
695 hash = _G.setmetatable({}, {__mode='kv'}), | 707 --hash = {}, |
696 } | 708 } |
697 c.cleanup:SetOrder(1) | 709 c.hash = c.fifo |
710 c.cleanup:SetOrder(1) -- [1,100] range within parent animation | |
711 c.cleanup:SetDuration(0.8) -- hmmm | |
698 caches[name] = c | 712 caches[name] = c |
699 return c | 713 return c |
700 end | 714 end |
701 end | 715 end |
702 | 716 |
1050 end | 1064 end |
1051 return self:NewModule(modname) | 1065 return self:NewModule(modname) |
1052 end | 1066 end |
1053 end | 1067 end |
1054 | 1068 |
1055 -- We don't want to trigger plugins and another addons as soon as something | 1069 --[[ LCALLBACK |
1056 -- interesting happens, because a nontrivial amount of work happens "quickly" | 1070 Standard ace3-style callback registration and dispatching. All player names |
1057 -- after the interesting event: cleanups/fixups, improvs from network, etc. | 1071 are simple (uncolored) strings. The "uniqueID" always refers to the unique |
1058 -- So firing a callback is delayed ever so briefly by human standards. | 1072 tag string stored as 'unique' in loot entries and used as keys in history. |
1059 -- | 1073 Item IDs are always of numeric type. |
1060 -- Can't *quite* use the expiring caches for this, but that's okay. | 1074 |
1075 'Activate', enabled_p, rebroadcast_p, threshold | |
1076 The two boolean predicates are self-explanatory. The threshold is an | |
1077 ITEM_QUALITY_* constant integer. | |
1078 | |
1079 'Deactivate' | |
1080 After all system events have been unregistered. | |
1081 | |
1082 'Reset' | |
1083 Clicking "Clear Loot", after all data manipulation is finished. | |
1084 | |
1085 'NewBoss', boss | |
1086 Boss event triggered by a local bossmod (DBM, etc) or a remote OL tracker. | |
1087 Argument is a g_loot table entry of kind=='boss'. | |
1088 | |
1089 'NewBossEntry', boss | |
1090 New row in primary EOI table of kind=='boss'. Includes all 'NewBoss' | |
1091 occasions, plus manual boss additions, testing, etc. Arg same as NewBoss. | |
1092 | |
1093 'NewLootEntry', loot | |
1094 'DelLootEntry', loot | |
1095 New or removed row in primary EOI table of kind=='loot'. Argument is a | |
1096 g_loot table entry of kind=='loot'. | |
1097 | |
1098 'NewEOIEntry', entry | |
1099 'DelEOIEntry', entry | |
1100 New or removed row in primary EOI table, of any kind. Argument is the | |
1101 g_loot entry, already inserted into or removed from g_loot. | |
1102 | |
1103 'NewHistory', player_name, uniqueID | |
1104 'DelHistory', player_name, uniqueID | |
1105 New or removed entry in player history. Name argument self-explanatory. | |
1106 ID is the corresponding loot, already inserted into or removed from the | |
1107 history structures. | |
1108 | |
1109 'Reassign', uniqueID, itemID, loot, from_player_name, to_player_name | |
1110 Loot reassigned from one player to another. Loot represented by the | |
1111 unique & item IDs, and the g_loot entry of kind=='loot'. The player | |
1112 names are self-explanatory. | |
1113 | |
1114 'MarkAs', uniqueID, itemID, loot, old_disposition, new_disposition | |
1115 The "Mark as <x>" action (as if from the item right-click menu, possibly | |
1116 from a remote tracker) has finished. ID & loot arguments are as in | |
1117 'Reassign'. The old/new dispositions are those of the g_loot index | |
1118 "disposition" (described at the top of core.lua), with the added possible | |
1119 value of "normal" meaning exactly that. | |
1120 ]] | |
1061 do | 1121 do |
1062 local mtnewindex = function() error("This table is read-only", 3) end | 1122 -- We don't want to trigger plugins or other addons as soon as something |
1123 -- interesting happens, because a nontrivial amount of work happens "soon" | |
1124 -- after the interesting event: cleanups/fixups, improvs from network, | |
1125 -- etc. So firing a callback is delayed ever so briefly by human scales. | |
1126 -- | |
1127 -- For data safety, we replace any table arguments with read-only proxies | |
1128 -- before passing them to the callbacks. The goal is to prevent accidents, | |
1129 -- not fraud. | |
1130 local unpack, setmetatable = _G.unpack, _G.setmetatable | |
1131 local mtnewindex = function() --[[local]]error("This table is read-only", 3) end | |
1063 local function make_readonly (t) | 1132 local function make_readonly (t) |
1064 return _G.setmetatable({}, { | 1133 return setmetatable({}, { |
1065 __newindex = mtnewindex, | 1134 __newindex = mtnewindex, |
1066 __index = t, | 1135 __index = t, |
1067 __metatable = false, | 1136 __metatable = false, |
1137 __tostring = getmetatable(t) and getmetatable(t).__tostring, | |
1068 }) | 1138 }) |
1069 end | 1139 end |
1070 | 1140 |
1071 local unpack = _G.unpack | 1141 local queue = create_new_cache ('callbacks', 1.2, function (allcbs) |
1072 local function F (t) | 1142 for _,c in ipairs(allcbs) do |
1073 addon.callbacks:Fire (unpack(t)) | 1143 addon.callbacks:Fire (unpack(c)) |
1074 flib.del(t) | 1144 flib.del(c) |
1075 end | 1145 end |
1146 end) | |
1076 function addon:Fire (...) | 1147 function addon:Fire (...) |
1077 self.dprint('callback', ...) | 1148 self.dprint('callback', ...) |
1078 local capture = flib.new(...) | 1149 local capture = flib.new(...) |
1079 for k,v in ipairs(capture) do if type(v) == 'table' then | 1150 for k,v in ipairs(capture) do if type(v) == 'table' then |
1080 capture[k] = make_readonly(v) | 1151 capture[k] = make_readonly(v) |
1081 end end | 1152 end end |
1082 self:ScheduleTimer (F, 1.2, capture) | 1153 queue:add(capture) |
1083 end | 1154 end |
1084 end | 1155 end |
1085 | 1156 |
1086 | 1157 |
1087 ------ Event handlers | 1158 ------ Event handlers |
1137 local IsInInstance, UnitIsConnected, UnitClass, UnitRace, UnitSex, | 1208 local IsInInstance, UnitIsConnected, UnitClass, UnitRace, UnitSex, |
1138 UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo = | 1209 UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo = |
1139 IsInInstance, UnitIsConnected, UnitClass, UnitRace, UnitSex, | 1210 IsInInstance, UnitIsConnected, UnitClass, UnitRace, UnitSex, |
1140 UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo | 1211 UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo |
1141 local time, difftime = time, difftime | 1212 local time, difftime = time, difftime |
1142 local R_ACTIVE, R_OFFLINE, R_LEFT = 1, 2, 3 | 1213 local R_ACTIVE, R_OFFLINE, R_LEFT = 'online', 'offline', 'no_longer' |
1143 | 1214 |
1144 local lastevent, now = 0, 0 | 1215 local lastevent, now = 0, 0 |
1145 local redo_count = 0 | 1216 local redo_count = 0 |
1146 local redo, timer_handle | 1217 local redo, timer_handle |
1147 | 1218 |
2138 addon:Print("Registered kill for '%s' in %s!", boss.bossname, boss.instance) | 2209 addon:Print("Registered kill for '%s' in %s!", boss.bossname, boss.instance) |
2139 end | 2210 end |
2140 end | 2211 end |
2141 wipe(candidates) | 2212 wipe(candidates) |
2142 end | 2213 end |
2214 -- Ten seconds is a long time, but occasionally DBM takes for-EVAH to | |
2215 -- decide that a fight is over. | |
2143 local recent_boss = create_new_cache ('boss', 10, fixup_durations) | 2216 local recent_boss = create_new_cache ('boss', 10, fixup_durations) |
2144 | 2217 |
2145 -- Similar to _do_loot, but duration+ parms only present when locally generated. | 2218 -- Similar to _do_loot, but duration+ parms only present when locally generated. |
2146 local function _do_boss (self, reason, bossname, intag, maxsize, duration) | 2219 local function _do_boss (self, reason, bossname, intag, maxsize, duration) |
2147 self.dprint('loot',">>_do_boss, R:", reason, "B:", bossname, | 2220 self.dprint('loot',">>_do_boss, R:", reason, "B:", bossname, |