From 3afe77d58bdb9b6cff76c018ef9bb6908ca804f0 Mon Sep 17 00:00:00 2001 From: "Johnny C. Lam" Date: Wed, 10 Apr 2013 07:20:26 +0000 Subject: [PATCH] Change the way that auras are scanned and kept in the aura database. Keep a separate aura age for each GUID so we can tell when an aura previously present is missing in a new aura scan of that GUID without affecting the determination of other unit's aura ages. The age is simply the number of times the unit's auras were scanned. Be more strict about deciding when an aura found on a unit matches one that was recently scanned on the same unit. An aura is the same if the following details match: caster, duration, expiration time, stack count Being more strict helps to "age" the auras properly, instead of tossing them out and getting new scans each time UpdateAuras/ScanUnitAuras is run. These changes will allow us to store more metadata in the aura properties about the player's state (e.g., tick length, crit chance, etc.) at the time the aura was gained. Also, store the caster's GUID in the aura properties instead of the caster's unit ID. The former is invariant and better suited to aura scans triggered by CLEU events. Rename two methods to be more descriptive about what they do: UpdateAuras -> ScanUnitAuras AddAura -> UnitGainedAura Re-use code to remove expired auras and be more DRY. Be smarter about when to trigger an Ovale refresh event by checking whether auras have actually changed on a unit since they were last scanned. git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@896 d5049fe3-3747-40f7-a4b5-f36d6801af5f --- OvaleAura.lua | 247 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 103 deletions(-) diff --git a/OvaleAura.lua b/OvaleAura.lua index 7581ecf..b0b24d4 100644 --- a/OvaleAura.lua +++ b/OvaleAura.lua @@ -28,10 +28,14 @@ local tinsert = table.insert local tsort = table.sort local API_UnitAura = UnitAura +-- aura pool local self_pool = OvalePool:NewPool("OvaleAura_pool") +-- self_aura[guid] pool +local self_aura_pool = OvalePool:NewPool("OvaleAura_aura_pool") -- self_aura[guid][filter][spellId]["mine" or "other"] = { aura properties } local self_aura = {} -local self_serial = 0 +-- self_serial[guid] = aura age +local self_serial = {} local OVALE_AURA_DEBUG = "aura" -- Units for which UNIT_AURA is known to fire. @@ -61,46 +65,54 @@ end -- -- -local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name, value) - if not self_aura[unitGUID][filter] then - self_aura[unitGUID][filter] = {} +local function UnitGainedAura(guid, spellId, filter, casterGUID, icon, count, debuffType, duration, expirationTime, isStealable, name, value) + if not self_aura[guid][filter] then + self_aura[guid][filter] = {} end - local auraList = self_aura[unitGUID][filter] - if not auraList[spellId] then - auraList[spellId] = {} + if not self_aura[guid][filter][spellId] then + self_aura[guid][filter][spellId] = {} end - -- Re-use existing aura by updating its serial number and adding new information - -- if it differs from the old aura. - local mine = (unitCaster == "player") - local aura, oldAura + local mine = (casterGUID == OvaleGUID:GetGUID("player")) + local existingAura, aura if mine then - oldAura = auraList[spellId].mine - if oldAura then - aura = oldAura + existingAura = self_aura[guid][filter][spellId].mine + if existingAura then + aura = existingAura else aura = self_pool:Get() aura.gain = Ovale.now - auraList[spellId].mine = aura + self_aura[guid][filter][spellId].mine = aura end else - oldAura = auraList[spellId].other - if oldAura then - aura = oldAura + existingAura = self_aura[guid][filter][spellId].other + if existingAura then + aura = existingAura else aura = self_pool:Get() aura.gain = Ovale.now - auraList[spellId].other = aura + self_aura[guid][filter][spellId].other = aura end end - aura.serial = self_serial + aura.serial = self_serial[guid] if count == 0 then count = 1 end - local isSameAura = oldAura and oldAura.duration == duration and oldAura.ending == expirationTime and oldAura.stacks == count - if not isSameAura and not aura.ending or aura.ending < expirationTime or aura.stacks ~= count then + -- Only overwrite an existing aura's information if the aura has changed. + -- An aura's "fingerprint" is its: + -- caster, duration, expiration time, stack count. + local auraIsUnchanged = ( + existingAura and + (aura.source == casterGUID) and + ((not aura.duration and duration == 0) or aura.duration == duration) and + ((not aura.ending and expirationTime == 0) or aura.ending == expirationTime) and + (aura.stacks == count) + ) + local addAura = not existingAura or not auraIsUnchanged + if addAura then + Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Adding %s %s (%s) to %s, aura.serial=%d", filter, name, spellId, guid, aura.serial) aura.icon = icon aura.stacks = count aura.debuffType = debuffType @@ -114,7 +126,7 @@ local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuf aura.start = expirationTime - duration aura.stealable = isStealable aura.mine = mine - aura.source = unitCaster + aura.source = casterGUID aura.name = name aura.value = value if mine then @@ -125,23 +137,53 @@ local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuf end end end + return addAura end -local function RemoveAurasForGUID(guid) - -- Return all auras for the given GUID to the aura pool. - if not guid or not self_aura[guid] then return end - Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Removing auras for guid %s", guid) - for filter, auraList in pairs(self_aura[guid]) do +local function RemoveAuraIfExpired(guid, spellId, filter, aura, serial) + if aura and serial and aura.serial ~= serial then + Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Removing expired %s %s (%s) from %s, serial=%d aura.serial=%d", + filter, aura.name, spellId, guid, serial, aura.serial) + self_pool:Release(aura) + return true + end + return false +end + +-- Return all auras for the given GUID to the aura pool. +local function RemoveAurasForGUID(guid, expired) + if not guid or not self_aura[guid] or not self_serial[guid] then return end + if not expired then + Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Removing auras from guid %s", guid) + end + local serial = self_serial[guid] + local auraTable = self_aura[guid] + for filter, auraList in pairs(auraTable) do for spellId, whoseTable in pairs(auraList) do for whose, aura in pairs(whoseTable) do - whoseTable[whose] = nil - self_pool:Release(aura) + if expired then + if RemoveAuraIfExpired(guid, spellId, filter, aura, serial) then + whoseTable[whose] = nil + end + else + Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Removing %s %s (%s) from %s, serial=%d aura.serial=%d", + filter, aura.name, spellId, guid, serial, aura.serial) + whoseTable[whose] = nil + self_pool:Release(aura) + end end - auraList[spellId] = nil + if not next(whoseTable) then + auraList[spellId] = nil + end + end + if not next(auraList) then + auraTable[filter] = nil end - self_aura[guid][filter] = nil end - self_aura[guid] = nil + if not next(auraTable) then + self_aura[guid] = nil + self_aura_pool:Release(auraTable) + end local unitId = OvaleGUID:GetUnitId(guid) if unitId then @@ -149,39 +191,43 @@ local function RemoveAurasForGUID(guid) end end +-- Remove all auras from GUIDs that can no longer be referenced by a unit ID, +-- i.e., not in the group or not targeted by anyone in the group or focus. local function RemoveAurasForMissingUnits() - -- Remove all auras from GUIDs that can no longer be referenced by a unit ID, - -- i.e., not in the group or not targeted by anyone in the group or focus. for guid in pairs(self_aura) do - if not OvaleGUID:GetUnitId(guid) then + local unitId = OvaleGUID:GetUnitId(guid) + if not unitId then RemoveAurasForGUID(guid) + self_serial[guid] = nil end end end -local function UpdateAuras(unitId, unitGUID) - self_serial = self_serial + 1 - +-- Scan auras on the given unit and update the aura database. +local function ScanUnitAuras(event, unitId, guid) if not unitId then return end - if not unitGUID then - unitGUID = OvaleGUID:GetGUID(unitId) + if not guid then + guid = OvaleGUID:GetGUID(unitId) end - if not unitGUID then + if not guid then return end - - if not self_aura[unitGUID] then - self_aura[unitGUID] = {} + if not self_aura[guid] then + self_aura[guid] = self_aura_pool:Get() + end + -- Advance the age of the unit's auras. + if not self_serial[guid] then + self_serial[guid] = 0 end - + self_serial[guid] = self_serial[guid] + 1 + Ovale:DebugPrintf(OVALE_AURA_DEBUG, "%s: advancing age of auras for %s (%s) to %d.", event, guid, unitId, self_serial[guid]) + local i = 1 local filter = "HELPFUL" - local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId - local canApplyAura, isBossDebuff, isCastByPlayer, value1, value2, value3 - while (true) do - name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId, + while true do + local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId, canApplyAura, isBossDebuff, isCastByPlayer, value1, value2, value3 = API_UnitAura(unitId, i, filter) if not name then if filter == "HELPFUL" then @@ -191,39 +237,20 @@ local function UpdateAuras(unitId, unitGUID) break end else - AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name, value1) + local casterGUID = OvaleGUID:GetGUID(unitCaster) + local added = UnitGainedAura(guid, spellId, filter, casterGUID, icon, count, debuffType, duration, expirationTime, isStealable, name, value1) + if added then + Ovale.refreshNeeded[unitId] = true + end if debuffType then -- TODO: not very clean -- should be computed by OvaleState:GetAura - AddAura(unitGUID, debuffType, filter, unitCaster, icon, count, debuffType, duration, expirationTime, isStealable, name, value1) + UnitGainedAura(guid, debuffType, filter, casterGUID, icon, count, debuffType, duration, expirationTime, isStealable, name, value1) end i = i + 1 end end - - --Removes expired auras - for filter, auraList in pairs(self_aura[unitGUID]) do - for spellId, whoseTable in pairs(auraList) do - for whose, aura in pairs(whoseTable) do - if aura.serial ~= self_serial then - Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Removing %s %s from %s, serial=%d aura.serial=%d", filter, aura.name, whose, self_serial, aura.serial) - whoseTable[whose] = nil - self_pool:Release(aura) - end - end - if not next(whoseTable) then - auraList[spellId] = nil - end - end - if not next(auraList) then - self_aura[unitGUID][filter] = nil - end - end - if not next(self_aura[unitGUID]) then - self_aura[unitGUID] = nil - end - - Ovale.refreshNeeded[unitId] = true + RemoveAurasForGUID(guid, true) end -- @@ -253,22 +280,23 @@ function OvaleAura:COMBAT_LOG_EVENT_UNFILTERED(event, ...) -- KNOWN BUG: an aura refreshed by a spell other than then one that applies it won't cause the CLEU event to fire. local unitId = OvaleGUID:GetUnitId(destGUID) if unitId and not OVALE_UNIT_AURA_UNITS[unitId] then - UpdateAuras(unitId, destGUID) + ScanUnitAuras(event, unitId, destGUID) end end end function OvaleAura:PLAYER_ENTERING_WORLD(event) + -- Update auras on all visible units. + for unitId in pairs(OVALE_UNIT_AURA_UNITS) do + ScanUnitAuras(event, unitId, OvaleGUID:GetGUID(unitId)) + end RemoveAurasForMissingUnits() self_pool:Drain() + self_aura_pool:Drain() end function OvaleAura:UNIT_AURA(event, unitId) - if unitId == "player" then - UpdateAuras("player", OvaleGUID:GetGUID("player")) - elseif unitId then - UpdateAuras(unitId) - end + ScanUnitAuras(event, unitId, OvaleGUID:GetGUID(unitId)) end function OvaleAura:Ovale_InactiveUnit(event, guid) @@ -290,41 +318,42 @@ function OvaleAura:GetAuraByGUID(guid, spellId, filter, mine, unitId) Ovale:Logf("Unable to get unitId from %s", guid) return nil end - UpdateAuras(unitId, guid) + -- This GUID has no auras previously cached, so do an aura scan. + if not self_serial[guid] then + ScanUnitAuras("GetAuraByGUID", unitId, guid) + end auraTable = self_aura[guid] if not auraTable then - -- no aura on target Ovale:Logf("Target %s has no aura", guid) return nil end end - local whose, aura - if filter then - if auraTable[filter] then - local whoseTable = auraTable[filter][spellId] - if whoseTable then - if mine then - aura = whoseTable.mine - else - whose, aura = next(whoseTable) - end - end - end - else - local whoseTable - for _, auraList in pairs(auraTable) do - whoseTable = auraList[spellId] + local aura + local serial = self_serial[guid] + for auraFilter, auraList in pairs(auraTable) do + if not filter or (filter == auraFilter) then + local whoseTable = auraList[spellId] if whoseTable then if mine then + if RemoveAuraIfExpired(guid, spellId, filter, whoseTable.mine, serial) then + whoseTable.mine = nil + end aura = whoseTable.mine else - whose, aura = next(whoseTable) + for k, v in pairs(whoseTable) do + if RemoveAuraIfExpired(guid, spellId, filter, v, serial) then + whoseTable[k] = nil + end + aura = whoseTable[k] + if aura then break end + end end if aura then break end end end end + if not aura then return nil end return aura.start, aura.ending, aura.stacks, aura.tick, aura.value, aura.gain end @@ -353,7 +382,8 @@ function OvaleAura:GetAura(unitId, spellId, filter, mine) end function OvaleAura:GetStealable(unitId) - local auraTable = self_aura[OvaleGUID:GetGUID(unitId)] + local guid = OvaleGUID:GetGUID(unitId) + local auraTable = self_aura[guid] if not auraTable then return nil end -- only buffs are stealable @@ -361,7 +391,11 @@ function OvaleAura:GetStealable(unitId) if not auraList then return nil end local start, ending + local serial = self_serial[guid] for spellId, whoseTable in pairs(auraList) do + if RemoveAuraIfExpired(guid, spellId, "HELPFUL", whoseTable.other, serial) then + whoseTable.other = nil + end local aura = whoseTable.other if aura and aura.stealable then if aura.start and (not start or aura.start < start) then @@ -382,9 +416,13 @@ function OvaleAura:GetMyAuraOnAnyTarget(spellId, filter, excludingGUID) local count = 0 for guid, auraTable in pairs(self_aura) do if guid ~= excludingGUID then + local serial = self_serial[guid] for auraFilter, auraList in pairs(auraTable) do if not filter or auraFilter == filter then if auraList[spellId] then + if RemoveAuraIfExpired(guid, spellId, filter, auraList[spellId].mine, serial) then + auraList[spellId].mine = nil + end local aura = auraList[spellId].mine if aura.start and (not start or aura.start < start) then start = aura.start @@ -435,11 +473,13 @@ end function OvaleAura:Debug() self_pool:Debug() + self_aura_pool:Debug() for guid, auraTable in pairs(self_aura) do + Ovale:FormatPrint("Auras for %s:", guid) for filter, auraList in pairs(auraTable) do for spellId, whoseTable in pairs(auraList) do for whose, aura in pairs(whoseTable) do - Ovale:FormatPrint("%s %s %s %s %s stacks=%d tick=%s", guid, filter, whose, spellId, aura.name, aura.stacks, aura.tick) + Ovale:FormatPrint("%s %s %s %s %s stacks=%d tick=%s serial=%d", guid, filter, whose, spellId, aura.name, aura.stacks, aura.tick, aura.serial) end end end @@ -449,6 +489,7 @@ end -- Print the auras matching the filter on the unit in alphabetical order. function OvaleAura:DebugListAura(unitId, filter) local guid = OvaleGUID:GetGUID(unitId) + RemoveAurasForGUID(guid, true) if self_aura[guid] and self_aura[guid][filter] then local array = {} for spellId, whoseTable in pairs(self_aura[guid][filter]) do -- 1.7.9.5