Quantcast

Re-write of OvaleGUID.

Johnny C. Lam [12-09-13 - 02:14]
Re-write of OvaleGUID.

- Cleaner mappings between GUID <--> Unit ID <--> Unit name.

- Create "unit ID" priority so that the "best" unit ID is returned for a
  given GUID.

- Update mappings more aggressively to fix ticket 326.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1243 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleAura.lua
OvaleGUID.lua
diff --git a/OvaleAura.lua b/OvaleAura.lua
index c8ccc43..517844a 100644
--- a/OvaleAura.lua
+++ b/OvaleAura.lua
@@ -277,7 +277,7 @@ function OvaleAura:COMBAT_LOG_EVENT_UNFILTERED(event, ...)

 	if CLEU_AURA_EVENTS[event] then
 		local unitId = OvaleGUID:GetUnitId(destGUID)
-		if unitId and not OvaleGUID.UNIT_AURA_UNITS[unitId] then
+		if unitId and not OvaleGUID.UNIT_AURA_UNIT[unitId] then
 			Ovale:DebugPrintf(OVALE_AURA_DEBUG, "%s: %s", event, unitId)
 			self:ScanAurasOnGUID(destGUID)
 		end
@@ -335,7 +335,7 @@ end

 function OvaleAura:ScanAllUnitAuras()
 	-- Update auras on all visible units.
-	for unitId in pairs(OvaleGUID.UNIT_AURA_UNITS) do
+	for unitId in pairs(OvaleGUID.UNIT_AURA_UNIT) do
 		self:ScanAuras(unitId)
 	end
 end
diff --git a/OvaleGUID.lua b/OvaleGUID.lua
index 0396bd0..f32bd5c 100644
--- a/OvaleGUID.lua
+++ b/OvaleGUID.lua
@@ -8,66 +8,116 @@
     file accompanying this program.
 --]]--------------------------------------------------------------------

--- This addon translates a GUID to a target name
--- Usage: OvaleGUID:GetUnitId(guid)
+--[[
+	This addon manages mappings between GUID <--> unit ID <--> unit name.
+
+	A GUID can have multiple unit IDs.
+	A unit ID can only have one GUID.
+	A unit ID may not exist.
+
+	All primary unit IDs receive events.
+	No <unit>target IDs receive events.
+	No "mouseover" unit IDs receive events.
+--]]

 local _, Ovale = ...
-local OvaleGUID = Ovale:NewModule("OvaleGUID", "AceEvent-3.0", "AceConsole-3.0")
+local OvaleGUID = Ovale:NewModule("OvaleGUID", "AceEvent-3.0")
 Ovale.OvaleGUID = OvaleGUID

 --<private-static-properties>
-local strfind = string.find
-local strsub = string.sub
+local ipairs = ipairs
+local tinsert = table.insert
 local API_GetNumGroupMembers = GetNumGroupMembers
-local API_UnitExists = UnitExists
 local API_UnitGUID = UnitGUID
 local API_UnitName = UnitName

 local OVALE_GUID_DEBUG = "guid"
---</private-static-properties>

---<public-static-properties>
-OvaleGUID.unitId = {}
-OvaleGUID.guid = {}
-OvaleGUID.nameToGUID = {}
-OvaleGUID.nameToUnit = {}
+--[[
+	Unit IDs for which UNIT_AURA events are known to fire.

--- Units for which UNIT_AURA is known to fire.
--- These are unit IDs that correspond to unit frames in the default WoW UI.
-OvaleGUID.UNIT_AURA_UNITS = {}
+	UNIT_AURA_UNITS is an ordered list of unit ID priority.
+	UNIT_AURA_UNIT is a table that holds the reverse mapping of UNIT_AURA_UNITS.
+--]]
+local UNIT_AURA_UNITS = {}
 do
-	local self = OvaleGUID
-	self.UNIT_AURA_UNITS["focus"] = true
-	self.UNIT_AURA_UNITS["pet"] = true
-	self.UNIT_AURA_UNITS["player"] = true
-	self.UNIT_AURA_UNITS["target"] = true
-
+	tinsert(UNIT_AURA_UNITS, "player")
+	tinsert(UNIT_AURA_UNITS, "pet")
+	tinsert(UNIT_AURA_UNITS, "vehicle")
+	tinsert(UNIT_AURA_UNITS, "npc")
+	tinsert(UNIT_AURA_UNITS, "target")
+	tinsert(UNIT_AURA_UNITS, "focus")
 	for i = 1, 5 do
-		self.UNIT_AURA_UNITS["arena" .. i] = true
-		self.UNIT_AURA_UNITS["arenapet" .. i] = true
+		tinsert(UNIT_AURA_UNITS, "arena" .. i)
+		tinsert(UNIT_AURA_UNITS, "arenapet" .. i)
+	end
+	for i = 1, 40 do
+		tinsert(UNIT_AURA_UNITS, "raid" .. i)
+		tinsert(UNIT_AURA_UNITS, "raidpet" .. i)
 	end
 	for i = 1, 4 do
-		self.UNIT_AURA_UNITS["boss" .. i] = true
+		tinsert(UNIT_AURA_UNITS, "party" .. i)
+		tinsert(UNIT_AURA_UNITS, "partypet" .. i)
+	end
+	for i = 1, 4 do
+		tinsert(UNIT_AURA_UNITS, "boss" .. i)
+	end
+end
+
+local UNIT_AURA_UNIT = {}
+do
+	for i, unitId in ipairs(UNIT_AURA_UNITS) do
+		UNIT_AURA_UNIT[unitId] = i
+	end
+end
+
+-- PET_UNIT[unitId] = pet's unit ID
+local PET_UNIT = {}
+do
+	PET_UNIT["player"] = "pet"
+	for i = 1, 5 do
+		PET_UNIT["arena" .. i] = "arenapet" .. i
 	end
 	for i = 1, 4 do
-		self.UNIT_AURA_UNITS["party" .. i] = true
-		self.UNIT_AURA_UNITS["partypet" .. i] = true
+		PET_UNIT["party" .. i] = "partypet" .. i
 	end
 	for i = 1, 40 do
-		self.UNIT_AURA_UNITS["raid" .. i] = true
-		self.UNIT_AURA_UNITS["raidpet" .. i] = true
+		PET_UNIT["raid" .. i] = "raidpet" .. i
 	end
 end
+--</private-static-properties>
+
+--<public-static-properties>
+--[[
+	Unit ID --> GUID mapping.
+	unit ID can only have one GUID.
+	self.unitIdToGUID[unitId] = GUID
+--]]
+OvaleGUID.unitIdToGUID = {}
+
+--[[
+	GUID --> unit ID mapping.
+	A GUID can have multiple unit IDs.
+	self.GUIDtoUnitId[guid] = { unitId = true if it exists and points to guid; nil otherwise }
+--]]
+OvaleGUID.GUIDtoUnitId = {}
+
+--[[
+--]]
+OvaleGUID.nameToGUID = {}
+
+-- Export UNIT_AURA_UNIT table of units that receive UNIT_AURA events.
+OvaleGUID.UNIT_AURA_UNIT = UNIT_AURA_UNIT
 --</public-static-properties>

 --<public-static-methods>
 function OvaleGUID:OnEnable()
-	self:Update("player")
 	self:RegisterEvent("ARENA_OPPONENT_UPDATE")
 	self:RegisterEvent("GROUP_ROSTER_UPDATE")
 	self:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
+	self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdateAllUnits")
 	self:RegisterEvent("PLAYER_FOCUS_CHANGED")
-	self:RegisterEvent("PLAYER_LOGIN")
+	self:RegisterEvent("PLAYER_LOGIN", "UpdateAllUnits")
 	self:RegisterEvent("PLAYER_TARGET_CHANGED")
 	self:RegisterEvent("UNIT_PET")
 	self:RegisterEvent("UNIT_TARGET")
@@ -78,6 +128,7 @@ function OvaleGUID:OnDisable()
 	self:UnregisterEvent("ARENA_OPPONENT_UPDATE")
 	self:UnregisterEvent("GROUP_ROSTER_UPDATE")
 	self:UnregisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
+	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
 	self:UnregisterEvent("PLAYER_FOCUS_CHANGED")
 	self:UnregisterEvent("PLAYER_LOGIN")
 	self:UnregisterEvent("PLAYER_TARGET_CHANGED")
@@ -86,144 +137,136 @@ function OvaleGUID:OnDisable()
 	self:UnregisterEvent("UPDATE_MOUSEOVER_UNIT")
 end

-function OvaleGUID:Update(unitId)
-	local guid = API_UnitGUID(unitId)
-	local previousGuid = self.guid[unitId]
-	if previousGuid ~= guid then
-		if previousGuid and self.unitId[previousGuid] then
-			self.unitId[previousGuid][unitId] = nil
-			if not next(self.unitId[previousGuid]) then
-				self.unitId[previousGuid] = nil
-			end
-		end
-		self.guid[unitId] = guid
-		if guid then
-			if not self.unitId[guid] then
-				self.unitId[guid] = {}
-			end
-			Ovale:DebugPrintf(OVALE_GUID_DEBUG, "GUID %s is %s", guid, unitId)
-			self.unitId[guid][unitId] = true
-		end
-	end
-	local name = API_UnitName(unitId)
-	if name and (not self.nameToGUID[name] or unitId == "target"
-			or self.nameToUnit[name] == "mouseover") then
-		self.nameToGUID[name] = guid
-		self.nameToUnit[name] = unitId
+function OvaleGUID:ARENA_OPPONENT_UPDATE(event, unitId, eventType)
+	for i = 1, 5 do
+		local unit = "arena" .. i
+		self:UpdateUnitWithTarget(unit)
+		local pet = PET_UNIT[unit] or (unit .. "pet")
+		self:UpdateUnitWithTarget(pet)
 	end
 end

-function OvaleGUID:GetGUID(unitId)
-	if not unitId then return nil end
-	local guid = self.guid[unitId]
-	if not guid or strfind(unitId, "mouseover") == 1 then
-		self.guid[unitId] = API_UnitGUID(unitId)
-		guid = self.guid[unitId]
-	end
-	return guid
+function OvaleGUID:GROUP_ROSTER_UPDATE(event)
+	self:UpdateAllUnits()
+	self:SendMessage("Ovale_GroupChanged")
 end

-function OvaleGUID:GetGUIDForName(name)
-	return self.nameToGUID[name]
+function OvaleGUID:INSTANCE_ENCOUNTER_ENGAGE_UNIT(event)
+	for i= 1, 4 do
+		self:UpdateUnitWithTarget("boss" .. i)
+	end
 end

--- Return a unit Id associated with guid.
--- Prefer to return a unit Id for which the WoW servers fire UNIT_AURA events.
-function OvaleGUID:GetUnitId(guid)
-	local unitIdFound = nil
-	local unitIdTable = self.unitId[guid]
-	if unitIdTable then
-		for unitId in pairs(unitIdTable) do
-			if self.UNIT_AURA_UNITS[unitId] then
-				return unitId
-			elseif not unitIdFound then
-				if strfind(unitId, "mouseover") == 1 then
-					if API_UnitExists(unitId) then
-						unitIdFound = unitId
-					else
-						unitIdTable[unitId] = nil
-						self.guid[unitId] = nil
-					end
-				else
-					unitIdFound = unitId
-				end
-			end
-		end
-	end
-	return unitIdFound
+function OvaleGUID:PLAYER_FOCUS_CHANGED(event)
+	self:UpdateUnitWithTarget("focus")
 end

-function OvaleGUID:GetUnitIdForName(name)
-	local unitId = self.nameToUnit[name]
-	if strfind(unitId, "mouseover") == 1 then
-		if API_UnitExists("mouseover") then
-			return unitId
-		else
-			self.nameToUnit[name] = nil
-			return nil
-		end
-	end
-	return unitId
+function OvaleGUID:PLAYER_TARGET_CHANGED(event, cause)
+	self:UNIT_TARGET(event, "player")
 end

-function OvaleGUID:UpdateWithTarget(unitId)
-	self:Update(unitId)
-	self:Update(unitId.."target")
+function OvaleGUID:UNIT_PET(event, unitId)
+	local pet = PET_UNIT[unitiD] or (unitId .. "pet")
+	self:UpdateUnitWithTarget(pet)
+	self:SendMessage("Ovale_GroupChanged")
 end

-function OvaleGUID:PLAYER_LOGIN(event)
-	self:Update("player")
+function OvaleGUID:UNIT_TARGET(event, unitId)
+	local target = (unitId == "player") and "target" or (unitId .. "target")
+	self:UpdateUnit(target)
 end

-function OvaleGUID:PLAYER_TARGET_CHANGED(event)
-	self:UNIT_TARGET(event, "player")
+function OvaleGUID:UPDATE_MOUSEOVER_UNIT(event)
+	self:UpdateUnitWithTarget("mouseover")
 end

-function OvaleGUID:UNIT_TARGET(event, unitId)
-	self:Update(unitId .. "target")
-	if unitId == "player" then
-		self:Update("target")
+function OvaleGUID:UpdateAllUnits()
+	for _, unitId in pairs(UNIT_AURA_UNITS) do
+		self:UpdateUnitWithTarget(unitId)
 	end
 end

-function OvaleGUID:GROUP_ROSTER_UPDATE(event)
-	for i=1, API_GetNumGroupMembers() do
-		self:UpdateWithTarget("raid"..i)
-		self:UpdateWithTarget("raidpet"..i)
-	end
-	self:SendMessage("Ovale_GroupChanged")
+function OvaleGUID:UpdateUnitWithTarget(unitId)
+	self:UpdateUnit(unitId)
+	self:UpdateUnit(unitId .. "target")
 end

-function OvaleGUID:UNIT_PET(event, unitId)
-	if strfind(unitId, "party") == 0 then
-		local petId = "partypet" .. strsub(unitId, 6)
-		self:UpdateWithTarget(petId)
-	elseif strfind(unitId, "raid") == 0 then
-		local petId = "raidpet" .. strsub(unitId, 5)
-		self:UpdateWithTarget(petId)
-	elseif unitId == "player" then
-		self:UpdateWithTarget("pet")
+function OvaleGUID:UpdateUnit(unitId)
+	local guid = API_UnitGUID(unitId)
+	if guid then
+		local previousGUID = self.unitIdToGUID[unitId]
+		if previousGUID ~= guid then
+			-- Remove previous mappings for this unit ID.
+			if previousGUID and self.GUIDtoUnitId[previousGUID] then
+				self.GUIDtoUnitId[previousGUID][unitId] = nil
+				if not next(self.GUIDtoUnitId[previousGUID]) then
+					self.GUIDtoUnitId[previousGUID] = nil
+				end
+			end
+			-- Create new mappings this unit ID to the GUID.
+			self.unitIdToGUID[unitId] = guid
+			self.GUIDtoUnitId[guid] = self.GUIDtoUnitId[guid] or {}
+			self.GUIDtoUnitId[guid][unitId] = true
+
+			Ovale:DebugPrintf(OVALE_GUID_DEBUG, "GUID %s is %s", guid, unitId)
+
+			if unitId == "target" or self.unitIdToGUID.target ~= guid then
+				local name = API_UnitName(unitId)
+				self.nameToGUID[name] = self.nameToGUID[name] or guid
+			end
+		end
+	else
+		-- This unit ID doesn't point to a valid GUID.
+		self.unitIdToGUID[unitId] = nil
+		if self.GUIDtoUnitId[guid] then
+			self.GUIDtoUnitId[guid][unitId] = nil
+			if not next(self.GUIDtoUnitId[guid]) then
+				self.GUIDtoUnitId[guid] = nil
+			end
+		end
 	end
-	self:SendMessage("Ovale_GroupChanged")
 end

-function OvaleGUID:ARENA_OPPONENT_UPDATE(event)
-	for i=1, 5 do
-		self:UpdateWithTarget("arena"..i)
+function OvaleGUID:GetGUID(unitId)
+	if unitId then
+		-- If the unit ID doesn't receive events, then refresh it now.
+		if not UNIT_AURA_UNIT[unitId] then
+			self:UpdateUnit(unitId)
+		end
+		return self.unitIdToGUID[unitId]
 	end
+	return nil
 end

-function OvaleGUID:PLAYER_FOCUS_CHANGED(event)
-	self:UpdateWithTarget("focus")
+function OvaleGUID:GetUnitId(guid)
+	if self.GUIDtoUnitId[guid] then
+		-- Find the unit ID with the best (lowest) priority.
+		local bestUnitId, bestPriority
+		for unitId in pairs(self.GUIDtoUnitId[guid]) do
+			local priority = UNIT_AURA_UNIT[unitId]
+			if priority then
+				if not bestPriority or priority < bestPriority then
+					bestUnitId, bestPriority = unitId, priority
+				end
+			else
+				-- This isn't a unit ID that receives events, so refresh it to make
+				-- sure it still points to this GUID.
+				self:UpdateUnit(unitId)
+				if not bestPriority and self.unitIdToGUID[unitId] == guid then
+					bestUnitId = unitId
+				end
+			end
+		end
+		return bestUnitId
+	end
+	return nil
 end

-function OvaleGUID:UPDATE_MOUSEOVER_UNIT(event)
-	self:UpdateWithTarget("mouseover")
+function OvaleGUID:GetGUIDForName(name)
+	return self.nameToGUID[name]
 end

-function OvaleGUID:INSTANCE_ENCOUNTER_ENGAGE_UNIT(event)
-	for i=1, 4 do
-		self:UpdateWithTarget("boss"..i)
-	end
+function OvaleGUID:GetUnitIdForName(name)
+	return self:GetUnitId(self:GetGUIDForName(name))
 end
 --</public-static-methods>