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,