Mercurial > wow > ouroloot
diff 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 |
line wrap: on
line diff
--- a/core.lua Wed Aug 01 06:51:52 2012 +0000 +++ b/core.lua Sat Aug 04 22:03:05 2012 +0000 @@ -14,11 +14,11 @@ - sex 1 = unknown/error, 2 = male, 3 = female - level can be 0 if player was offline at the time - guild guild name, or missing if unguilded -- online 1 = online, 2 = offline, 3 = no longer in raid +- online 'online', 'offline', 'no_longer' [no longer in raid group] [both of these next two fields use time_t values:] - join time player joined the raid (or first time we've seen them) - leave time player left the raid (or time we've left the raid, if - 'online' is not 3) + 'online' is not 'no_longer') Common g_loot entry indices: - kind time/boss/loot @@ -77,7 +77,7 @@ ------ Addon member data ------ Globals ------ Expiring caches ------- Ace3 framework stuff +------ Ace3 framework stuff (callback 'events', search for LCALLBACK) ------ Event handlers ------ Slash command handler ------ On/off @@ -101,7 +101,7 @@ OuroLootSV = nil -- possible copy of g_loot OuroLootSV_saved = nil -- table of copies of saved texts, default nil; keys -- are numeric indices of tables, subkeys of those - -- are name/forum/attend/date + -- are name/forum/attend/date OuroLootSV_hist = nil OuroLootSV_log = {} @@ -629,61 +629,72 @@ cache:test(foo) -- returns false cache:test(bar) -- returns true ....5 seconds pass - ....bar also gone, cleanup() called + ....bar also gone, cleanup() called: +function cleanup (expired_entries) + for i = 1, #expired_entries do -- this table is in strict FIFO order + print(i, expired_entries[i]) + -- 1 foo + -- 2 bar + end +end ]] do local caches = {} local cleanup_group = _G.AnimTimerFrame:CreateAnimationGroup() - local time = _G.time + local time, next = _G.time, _G.next + local new, del = flib.new, flib.del cleanup_group:SetLooping("REPEAT") cleanup_group:SetScript("OnLoop", function(cg) addon.dprint('cache',"OnLoop firing") local now = time() - local alldone = true + local actives = 0 + local expired = new() -- this is ass-ugly - for name,c in pairs(caches) do + for name,c in next, caches do local fifo = c.fifo local active = #fifo > 0 + actives = actives + (active and 1 or 0) while (#fifo > 0) and (now > fifo[1].t) do - addon.dprint('cache', name, "cache removing", fifo[1].t, "<", fifo[1].m, ">") - tremove(fifo,1) + local datum = tremove(fifo,1) + addon.dprint('cache', name, "cache removing", datum.t, "<", datum.m, ">") + c.hash[datum.m] = nil + tinsert(expired,datum.m) + del(datum) end if active and #fifo == 0 and c.func then addon.dprint('cache', name, "empty, firing cleanup") - c:func() + c.func(expired) end - alldone = alldone and (#fifo == 0) + wipe(expired) end - if alldone then + del(expired) + if actives == 0 then addon.dprint('cache',"OnLoop FINISHING animation group") cleanup_group:Finish() - _G.collectgarbage() else addon.dprint('cache',"OnLoop done, not yet finished") end end) local function _add (cache, x) - local datum = { t=time()+cache.ttl, m=x } + assert(type(x)~='number') + local datum = new() + datum.m = x + datum.t = time() + cache.ttl cache.hash[x] = datum tinsert (cache.fifo, datum) if not cleanup_group:IsPlaying() then addon.dprint('cache', cache.name, "with entry", datum.t, "<", datum.m, "> STARTING animation group") - cache.cleanup:SetDuration(1) -- hmmm cleanup_group:Play() end end local function _test (cache, x) - -- FIXME This can return false positives, if called after the onloop - -- fifo has been removed but before the GC has removed the weak entry. - -- What to do, what to do... try forcing a GC during alldone. return cache.hash[x] ~= nil end function create_new_cache (name, ttl, on_alldone) -- setting OnFinished for cleanup fires at the end of each inner loop, -- with no 'requested' argument to distinguish cases. thus, on_alldone. - -- FWIW, on_alldone is passed this table as its sole argument: local c = { ttl = ttl, name = name, @@ -691,10 +702,13 @@ test = _test, cleanup = cleanup_group:CreateAnimation("Animation"), func = on_alldone, + -- Testing merging these two (_add's 'x' must not be numeric) fifo = {}, - hash = _G.setmetatable({}, {__mode='kv'}), + --hash = {}, } - c.cleanup:SetOrder(1) + c.hash = c.fifo + c.cleanup:SetOrder(1) -- [1,100] range within parent animation + c.cleanup:SetDuration(0.8) -- hmmm caches[name] = c return c end @@ -1052,34 +1066,91 @@ end end --- We don't want to trigger plugins and another addons as soon as something --- interesting happens, because a nontrivial amount of work happens "quickly" --- after the interesting event: cleanups/fixups, improvs from network, etc. --- So firing a callback is delayed ever so briefly by human standards. --- --- Can't *quite* use the expiring caches for this, but that's okay. +--[[ LCALLBACK +Standard ace3-style callback registration and dispatching. All player names +are simple (uncolored) strings. The "uniqueID" always refers to the unique +tag string stored as 'unique' in loot entries and used as keys in history. +Item IDs are always of numeric type. + +'Activate', enabled_p, rebroadcast_p, threshold + The two boolean predicates are self-explanatory. The threshold is an + ITEM_QUALITY_* constant integer. + +'Deactivate' + After all system events have been unregistered. + +'Reset' + Clicking "Clear Loot", after all data manipulation is finished. + +'NewBoss', boss + Boss event triggered by a local bossmod (DBM, etc) or a remote OL tracker. + Argument is a g_loot table entry of kind=='boss'. + +'NewBossEntry', boss + New row in primary EOI table of kind=='boss'. Includes all 'NewBoss' + occasions, plus manual boss additions, testing, etc. Arg same as NewBoss. + +'NewLootEntry', loot +'DelLootEntry', loot + New or removed row in primary EOI table of kind=='loot'. Argument is a + g_loot table entry of kind=='loot'. + +'NewEOIEntry', entry +'DelEOIEntry', entry + New or removed row in primary EOI table, of any kind. Argument is the + g_loot entry, already inserted into or removed from g_loot. + +'NewHistory', player_name, uniqueID +'DelHistory', player_name, uniqueID + New or removed entry in player history. Name argument self-explanatory. + ID is the corresponding loot, already inserted into or removed from the + history structures. + +'Reassign', uniqueID, itemID, loot, from_player_name, to_player_name + Loot reassigned from one player to another. Loot represented by the + unique & item IDs, and the g_loot entry of kind=='loot'. The player + names are self-explanatory. + +'MarkAs', uniqueID, itemID, loot, old_disposition, new_disposition + The "Mark as <x>" action (as if from the item right-click menu, possibly + from a remote tracker) has finished. ID & loot arguments are as in + 'Reassign'. The old/new dispositions are those of the g_loot index + "disposition" (described at the top of core.lua), with the added possible + value of "normal" meaning exactly that. +]] do - local mtnewindex = function() error("This table is read-only", 3) end + -- We don't want to trigger plugins or other addons as soon as something + -- interesting happens, because a nontrivial amount of work happens "soon" + -- after the interesting event: cleanups/fixups, improvs from network, + -- etc. So firing a callback is delayed ever so briefly by human scales. + -- + -- For data safety, we replace any table arguments with read-only proxies + -- before passing them to the callbacks. The goal is to prevent accidents, + -- not fraud. + local unpack, setmetatable = _G.unpack, _G.setmetatable + local mtnewindex = function() --[[local]]error("This table is read-only", 3) end local function make_readonly (t) - return _G.setmetatable({}, { + return setmetatable({}, { __newindex = mtnewindex, __index = t, __metatable = false, + __tostring = getmetatable(t) and getmetatable(t).__tostring, }) end - local unpack = _G.unpack - local function F (t) - addon.callbacks:Fire (unpack(t)) - flib.del(t) - end + local queue = create_new_cache ('callbacks', 1.2, function (allcbs) + for _,c in ipairs(allcbs) do + addon.callbacks:Fire (unpack(c)) + flib.del(c) + end + end) function addon:Fire (...) self.dprint('callback', ...) local capture = flib.new(...) for k,v in ipairs(capture) do if type(v) == 'table' then capture[k] = make_readonly(v) end end - self:ScheduleTimer (F, 1.2, capture) + queue:add(capture) end end @@ -1139,7 +1210,7 @@ IsInInstance, UnitIsConnected, UnitClass, UnitRace, UnitSex, UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo local time, difftime = time, difftime - local R_ACTIVE, R_OFFLINE, R_LEFT = 1, 2, 3 + local R_ACTIVE, R_OFFLINE, R_LEFT = 'online', 'offline', 'no_longer' local lastevent, now = 0, 0 local redo_count = 0 @@ -2140,6 +2211,8 @@ end wipe(candidates) end + -- Ten seconds is a long time, but occasionally DBM takes for-EVAH to + -- decide that a fight is over. local recent_boss = create_new_cache ('boss', 10, fixup_durations) -- Similar to _do_loot, but duration+ parms only present when locally generated.