Quantcast

Refactor latency calculation into a separate OvaleLatency module.

Johnny C. Lam [08-10-13 - 18:40]
Refactor latency calculation into a separate OvaleLatency module.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1003 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
Ovale.toc
OvaleCondition.lua
OvaleDamageTaken.lua
OvaleFuture.lua
OvaleLatency.lua
diff --git a/Ovale.toc b/Ovale.toc
index f5975d5..264835d 100644
--- a/Ovale.toc
+++ b/Ovale.toc
@@ -34,6 +34,7 @@ OvaleActionBar.lua
 OvaleEnemies.lua
 OvaleEquipement.lua
 OvaleGUID.lua
+OvaleLatency.lua
 OvalePool.lua
 OvalePoolGC.lua
 OvaleQueue.lua
@@ -41,21 +42,22 @@ OvaleRecount.lua
 OvaleSkada.lua
 OvaleStance.lua
 #
+OvaleDamageTaken.lua
 OvalePaperDoll.lua
 #
 OvaleData.lua
 OvaleScripts.lua
 defaut\Chaman.lua
 defaut\Chasseur.lua
+defaut\Chevalier.lua
 defaut\Demoniste.lua
 defaut\Druide.lua
 defaut\Guerrier.lua
 defaut\Mage.lua
+defaut\Moine.lua
 defaut\Paladin.lua
 defaut\Pretre.lua
 defaut\Voleur.lua
-defaut\Moine.lua
-defaut\Chevalier.lua
 OvaleSpellDamage.lua
 OvaleSwing.lua
 #
@@ -65,7 +67,6 @@ OvaleOptions.lua
 #
 OvaleFuture.lua
 #
-OvaleDamageTaken.lua
 OvaleState.lua
 #
 OvaleCondition.lua
diff --git a/OvaleCondition.lua b/OvaleCondition.lua
index bb617f1..5553053 100644
--- a/OvaleCondition.lua
+++ b/OvaleCondition.lua
@@ -24,6 +24,7 @@ local OvaleEnemies = Ovale.OvaleEnemies
 local OvaleEquipement = Ovale.OvaleEquipement
 local OvaleFuture = Ovale.OvaleFuture
 local OvaleGUID = Ovale.OvaleGUID
+local OvaleLatency = Ovale.OvaleLatency
 local OvalePaperDoll = Ovale.OvalePaperDoll
 local OvaleSpellDamage = Ovale.OvaleSpellDamage
 local OvaleStance = Ovale.OvaleStance
@@ -2297,8 +2298,7 @@ OvaleCondition.conditions.lastswing = function(condition)
 	return 0, nil, 0, OvaleSwing:GetLast(condition[1]), 1
 end

---- Get the most recent estimate of latency in milliseconds.
--- This condition is experimental and may not yield correct results.
+--- Get the most recent estimate of roundtrip latency in milliseconds.
 -- @name Latency
 -- @paramsig number or boolean
 -- @param operator Optional. Comparison operator: equal, less, more.
@@ -2310,7 +2310,7 @@ end
 -- if Latency(more 1000) Spell(sinister_strike)

 OvaleCondition.conditions.latency = function(condition)
-	return 0, nil, OvaleFuture.latency * 1000, 0, 0
+	return 0, nil, OvaleLatency:GetLatency() * 1000, 0, 0
 end

 --- Get the level of the target.
diff --git a/OvaleDamageTaken.lua b/OvaleDamageTaken.lua
index e160ef9..652dde4 100644
--- a/OvaleDamageTaken.lua
+++ b/OvaleDamageTaken.lua
@@ -15,7 +15,7 @@ Ovale.OvaleDamageTaken = OvaleDamageTaken

 --<private-static-properties>
 local OvaleGUID = Ovale.OvaleGUID
-local OvaleFuture = Ovale.OvaleFuture
+local OvaleLatency = Ovale.OvaleLatency
 local OvalePool = Ovale.OvalePool
 local OvaleQueue = Ovale.OvaleQueue

@@ -80,7 +80,7 @@ end
 function OvaleDamageTaken:GetRecentDamage(interval, lagCorrection)
 	local lowerBound = Ovale.now - interval
 	if lagCorrection then
-		lowerBound = lowerBound - OvaleFuture.latency
+		lowerBound = lowerBound - OvaleLatency:GetLatency()
 	end
 	self:RemoveExpiredEvents()

diff --git a/OvaleFuture.lua b/OvaleFuture.lua
index c03415c..aca9565 100644
--- a/OvaleFuture.lua
+++ b/OvaleFuture.lua
@@ -49,10 +49,6 @@ local self_lastTarget = nil
 -- Time at which a player aura was last added.
 local self_timeAuraAdded = nil

--- The spell requests that have been sent to the server and are awaiting a reply.
--- self_sentSpellcast[lineId] = timestamp
-local self_sentSpellcast = {}
-
 local OVALE_UNKNOWN_GUID = 0

 -- These CLEU events are eventually received after a successful spellcast.
@@ -69,8 +65,6 @@ local OVALE_CLEU_SPELLCAST_RESULTS = {
 --<public-static-properties>
 --spell counter (see Counter function)
 OvaleFuture.counter = {}
--- Most recent latency (time between UNIT_SPELLCAST_SENT and UNIT_SPELLCAST_SUCCEEDED events).
-OvaleFuture.latency = 0
 -- Debugging: spells to trace
 OvaleFuture.traceSpellList = nil
 --</public-static-properties>
@@ -167,24 +161,8 @@ local function AddSpellToQueue(spellId, lineId, startTime, endTime, channeled, a
 	else
 		spellcast.removeOnSuccess = true
 	end
-
 	tinsert(self_activeSpellcast, spellcast)

-	-- Update latency measurement.  API_GetTime() only updates on frame refresh (OnUpdate) so
-	-- this latency measurement has a lower bound of the 1/FPS, where FPS is the current frame
-	-- rate.
-	if self_sentSpellcast[lineId] then
-		local latency = Ovale.now - self_sentSpellcast[lineId]
-		local castTime = endTime - startTime
-		if castTime > 0 and latency > castTime then
-			latency = castTime
-		end
-		if latency > 0 then
-			self.latency = latency
-		end
-		self_sentSpellcast[lineId] = nil
-	end
-
 	ScoreSpell(spellId)
 	Ovale.refreshNeeded["player"] = true
 end
@@ -347,9 +325,6 @@ function OvaleFuture:UNIT_SPELLCAST_SENT(event, unit, spell, rank, target, lineI
 			self_lastTarget = OVALE_UNKNOWN_GUID
 		end
 		TracePrintf(spell, "%s: %f %s on %s, lineId=%d", event, Ovale.now, spell, self_lastTarget, lineId)
-
-		-- Note starting time for latency calculation.
-		self_sentSpellcast[lineId] = Ovale.now
 	end
 end

diff --git a/OvaleLatency.lua b/OvaleLatency.lua
new file mode 100644
index 0000000..fdb30c2
--- /dev/null
+++ b/OvaleLatency.lua
@@ -0,0 +1,77 @@
+--[[--------------------------------------------------------------------
+    Ovale Spell Priority
+    Copyright (C) 2013 Johnny C. Lam
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License in the LICENSE
+    file accompanying this program.
+--]]--------------------------------------------------------------------
+
+local _, Ovale = ...
+local OvaleLatency = Ovale:NewModule("OvaleLatency", "AceEvent-3.0")
+Ovale.OvaleLatency = OvaleLatency
+
+--<private-static-properties>
+local select = select
+local API_GetNetStats = GetNetStats
+
+-- The spell requests that have been sent to the server and are awaiting a reply.
+-- self_sentSpellcast[lineId] = GetTime() timestamp
+local self_sentSpellcast = {}
+
+local self_lastUpdateTime = nil
+local self_latency = nil
+--</private-static-properties>
+
+--<public-static-methods>
+function OvaleLatency:OnEnable()
+	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START", "UpdateLatency")
+	self:RegisterEvent("UNIT_SPELLCAST_SENT")
+	self:RegisterEvent("UNIT_SPELLCAST_START", "UpdateLatency")
+	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", "UpdateLatency")
+end
+
+function OvaleLatency:OnDisable()
+	self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START")
+	self:UnregisterEvent("UNIT_SPELLCAST_SENT")
+	self:UnregisterEvent("UNIT_SPELLCAST_START")
+	self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
+end
+
+-- Event handler for UNIT_SPELLCAST_* events that updates the current roundtrip latency.
+function OvaleLatency:UpdateLatency(event, unit, name, rank, lineId, spellId)
+	if unit == "player" and self_sentSpellcast[lineId] then
+		--[[
+			Assume an event loop looks like:
+
+				client --SENT--> server (processing) --CHANNEL_START/START/SUCCEEDED--> client
+
+			By taking the difference between the SENT and CHANNEL_START/START/SUCCEEDED events,
+			we are assuming that the processing time on the server is negligible compared to the
+			network latency.  As a result, this will always over-estimate the true latency.
+		]]--
+		local latency = Ovale.now - self_sentSpellcast[lineId]
+		if latency > 0 then
+			self_latency = latency
+			self_lastUpdateTime = Ovale.now
+		end
+		self_sentSpellcast[lineId] = nil
+	end
+end
+
+function OvaleLatency:UNIT_SPELLCAST_SENT(event, unit, spell, rank, target, lineId)
+	if unit == "player" then
+		-- Note starting time for latency calculation.
+		self_sentSpellcast[lineId] = Ovale.now
+	end
+end
+
+function OvaleLatency:GetLatency()
+	-- If we haven't cast a spell in a while, then get the average world roundtrip latency
+	-- using GetNetStats().
+	if not self_latency or not self_lastUpdateTime or Ovale.now - self_lastUpdateTime > 10 then
+		self_latency = select(4, API_GetNetStats()) / 1000
+	end
+	return self_latency
+end
+--</public-static-methods>