Quantcast

Refactored the dependency on Skada to be replacable.

Peter Eliasson [01-16-15 - 16:53]
Refactored the dependency on Skada to be replacable.

* Changed to using a module-based approach for the parses. This means that it should be possible to support additional damage mods in the future, like Recount.
* Added a parse provider module for Skada, with much the same functionallity as was previously done in the main file.
* Changed from ending encounters after Skada's EndSegment to using the ENCOUNTER_END event. The ENCOUNTER_END should be reliable (the ENCOUNTER_START does seem to have issues still though).
Filename
GuildSkadaHighScore.toc
src/main.lua
src/parse_modules/parse_modules_core.lua
src/parse_modules/parse_modules_include.xml
src/parse_modules/skada.lua
src/src_include.xml
diff --git a/GuildSkadaHighScore.toc b/GuildSkadaHighScore.toc
index c6c6c00..70c0f0a 100644
--- a/GuildSkadaHighScore.toc
+++ b/GuildSkadaHighScore.toc
@@ -1,10 +1,10 @@
 ## Interface: 60000
 ## Author: Verath (Peter Eliasson)
-## Version: 0.0.2
+## Version: 0.0.3
 ## Title: Guild Skada High Score
 ## Notes: Tracking and ranking of dps/hps in a guild.
 ## SavedVariables: GuildSkadaHighScoreDB
-## Dependencies: Skada
+## Dependencies:

 lib\lib_include.xml
 src\src_include.xml
diff --git a/src/main.lua b/src/main.lua
index 1bbcf1a..c9360a7 100644
--- a/src/main.lua
+++ b/src/main.lua
@@ -4,7 +4,7 @@ local tinsert = tinsert;
 local tremove = tremove;

 -- Create ACE3 addon
-local addon = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0")
+local addon = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0")

 tinsert(addonTable, addon);
 _G[addonName] = addon
@@ -130,16 +130,33 @@ function addon:GetGuildPlayersFromSet(skadaSet)
 	return players
 end

-function addon:SetRoleForPlayers(players)
-	for _, player in ipairs(players) do
-		player.role = UnitGroupRolesAssigned(player.name);
+function addon:OnEncounterEndSuccess()
+	self:Debug("OnEncounterEndSuccess")
+
+	local guildName = self.guildName;
+	if not guildName then
+		self:Debug("Not in a guild");
+		return;
 	end
-end

-function addon:SetClassForPlayers(players)
-	for _, player in ipairs(players) do
-		player.class = UnitClass(player.name);
+	local encounter = self.currentEncounter;
+	if not encounter then
+		self:Debug("No current encounter");
+		return
 	end
+
+	local pmc = self.parseModulesCore;
+	pmc:GetParsesForEncounter(encounter, function(success, startTime, duration, players)
+		if not success then return end;
+
+		encounter.startTime = startTime;
+		encounter.duration = duration;
+		self.inspect:GetInspectDataForPlayers(players, function()
+			self.highscore:AddEncounterParsesForPlayers(guildName, encounter, players);
+		end)
+	end)
+
+	self:UnsetCurrentEncounter();
 end

 function addon:PLAYER_GUILD_UPDATE(evt, unitId)
@@ -156,9 +173,10 @@ end
 function addon:ENCOUNTER_END(evt, encounterId, encounterName, difficultyId, raidSize, endStatus)
 	self:Debug("ENCOUNTER_END", encounterId, encounterName, difficultyId, raidSize, endStatus)
 	if endStatus == 1 then -- Success
-		self:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize)
+		self:SetCurrentEncounter(encounterId, encounterName, difficultyId, raidSize);
+		self:OnEncounterEndSuccess();
 	else
-		self:UnsetCurrentEncounter()
+		self:UnsetCurrentEncounter();
 	end
 end

@@ -166,43 +184,6 @@ function addon:ZONE_CHANGED_NEW_AREA(evt)
 	self:UpdateCurrentZone();
 end

-function addon:EndSegment()
-	self:Debug("EndSegment")
-
-	-- Find the skada set matching the encounter
-	-- Looking only at the lastest set should make sense,
-	-- as that set should be the boss segment we just ended.
-	local _, skadaSet = next(Skada:GetSets());
-	local encounter = self.currentEncounter;
-
-	if not self.guildName then
-		self:Debug("Not in a guild");
-		return;
-	end
-
-	if not encounter then
-		self:Debug("No current encounter");
-		return
-	end
-
-	if not skadaSet or not skadaSet.gotboss or skadaSet.mobname ~= encounter.name then
-		self:Debug("No Skada set found for boss");
-		return;
-	end
-
-	encounter.duration = skadaSet.time;
-	encounter.startTime = skadaSet.starttime;
-
-	local players = self:GetGuildPlayersFromSet(skadaSet);
-	self:SetRoleForPlayers(players);
-	self:SetClassForPlayers(players);
-	self.inspect:GetInspectDataForPlayers(players, function()
-		self.highscore:AddEncounterParsesForPlayers(self.guildName, encounter, players);
-	end)
-
-	self:UnsetCurrentEncounter();
-end
-
 function addon:OnInitialize()
 	self.db = LibStub("AceDB-3.0"):New("GuildSkadaHighScoreDB", addon.dbDefaults, true)

@@ -237,8 +218,6 @@ function addon:OnEnable()
 	self:RegisterEvent("PLAYER_GUILD_UPDATE")
 	self:RegisterEvent("ZONE_CHANGED_NEW_AREA")

-	self:SecureHook(Skada, "EndSegment")
-
 	self:RegisterChatCommand("gshs", function()
 		self.gui:ShowMainFrame();
 	end)
@@ -257,7 +236,5 @@ function addon:OnDisable()
 	self:UnregisterEvent("PLAYER_GUILD_UPDATE")
 	self:UnregisterEvent("ZONE_CHANGED_NEW_AREA")

-	self:UnHook(Skada, "EndSegment");
-
 	self:UnregisterChatCommand("gshs");
 end
diff --git a/src/parse_modules/parse_modules_core.lua b/src/parse_modules/parse_modules_core.lua
new file mode 100644
index 0000000..ff21f3f
--- /dev/null
+++ b/src/parse_modules/parse_modules_core.lua
@@ -0,0 +1,105 @@
+--
+-- The parseModulesCore is a container module for our
+-- parse provider modules.
+--
+
+local addonName, addonTable = ...
+local addon = addonTable[1];
+
+-- Global functions for faster access
+local tinsert = tinsert;
+
+-- Create the parseModulesCore
+local pmc = addon:NewModule("parseModulesCore")
+addon.parseModulesCore = pmc;
+
+-- Set up the prototype for parse providers
+do
+	local proto = {}
+
+	function proto:Debug(...)
+		pmc:Debug(...);
+	end
+
+	-- Function called by the Parse Module Core to determine
+	-- if this module can be enabled or not. Should test that
+	-- all dependencies are loaded.
+	function proto:IsActivatable()
+		return false;
+	end
+
+	-- Function that must be called for each player before it
+	-- can be included in the parses returned.
+	-- Returns true if the player should be included.
+	function proto:ShouldIncludePlayer(playerId, playerName)
+		return addon:IsInMyGuild(playerName);
+	end
+
+	-- Should get a list of player parses for the specified encounter.
+	--
+	-- The callback function should be called with (success, startTime, duration, parses)
+	-- 		success 	- boolean indicating if parses were found.
+	--		startTime 	- the time when the encounter started.
+	--		duration 	- the time in seconds of the encounter.
+	-- 		players 	- list of player parse objects ({id="", name="", damage=0, healing=0})
+	--					  of players passing the ShouldIncludePlayer test.
+	--
+	-- Params:
+	-- 		encounter - An object describing the encounter with the following keys:
+	--					zoneId, zoneName, id, name, difficultyId, difficultyName, raidSize
+	--		callback  - The function to be called when the parse is ready.
+	function proto:GetParsesForEncounter(encounter, callback)
+		callback(false);
+	end
+
+	pmc:SetDefaultModulePrototype(proto);
+	-- Parse provider modules are disabled by default,
+	-- as only one should be used at a time
+	pmc:SetDefaultModuleState(false);
+end
+
+
+local function setAdditionalDataForPlayers(players)
+	for _, player in ipairs(players) do
+		player.role = UnitGroupRolesAssigned(player.name);
+		player.class = UnitClass(player.name);
+	end
+end
+
+-- Function for getting parses for an encounter. This
+-- function will forward the call to the selected parse
+-- provider if available. Will also add additional information
+-- like spec and role to successful parse fetches
+function pmc:GetParsesForEncounter(encounter, callback)
+	local parseProvider = self.selectedParseProvider
+	if parseProvider then
+		parseProvider:GetParsesForEncounter(encounter, function(success, startTime, duration, players)
+			if success then
+				setAdditionalDataForPlayers(players);
+			end
+			callback(success, startTime, duration, players);
+		end);
+	else
+		callback(false);
+	end
+end
+
+function pmc:SelectParseProvider()
+	for name, mod in self:IterateModules() do
+		if mod:IsActivatable() then
+			self:Debug("Using " .. name .. " as parse provider.");
+			self.selectedParseProvider = mod;
+			mod:Enable();
+			break;
+		end
+	end
+end
+
+function pmc:OnEnable()
+	self.selectedParseProvider = nil;
+	self:SelectParseProvider();
+end
+
+function pmc:OnDisable()
+	self.selectedParseProvider = nil;
+end
\ No newline at end of file
diff --git a/src/parse_modules/parse_modules_include.xml b/src/parse_modules/parse_modules_include.xml
new file mode 100644
index 0000000..747f042
--- /dev/null
+++ b/src/parse_modules/parse_modules_include.xml
@@ -0,0 +1,4 @@
+<Ui xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd">
+	<Script file="parse_modules_core.lua"/>
+	<Script file="skada.lua"/>
+</Ui>
\ No newline at end of file
diff --git a/src/parse_modules/skada.lua b/src/parse_modules/skada.lua
new file mode 100644
index 0000000..a60e56b
--- /dev/null
+++ b/src/parse_modules/skada.lua
@@ -0,0 +1,88 @@
+local addonName, addonTable = ...
+local addon = addonTable[1];
+local pmc = addon.parseModulesCore;
+
+local mod = pmc:NewModule("Skada", "AceHook-3.0");
+if not mod then return end;
+
+-- Global functions
+local wipe = wipe;
+local tinsert = tinsert;
+
+
+function mod:IsActivatable()
+	return IsAddOnLoaded("Skada");
+end
+
+function mod:GetPlayersFromSet(skadaSet)
+	local players = {}
+
+	-- Have to copy the data as skadaSet.players is a direct reference
+	-- to the skada set.
+	for i, player in ipairs(skadaSet.players) do
+		if self:ShouldIncludePlayer(player.id, player.name) then
+			local playerData = {
+				id = player.id,
+				name = player.name,
+				damage = player.damage,
+				healing = player.healing
+			};
+			tinsert(players, playerData);
+		end
+	end
+	return players
+end
+
+function mod:ProcessParseRequest(encounter, callback)
+	-- Find the skada set matching the encounter. Looking only
+	-- at the lastest set should make sense, as that set
+	-- should be the boss segment we just ended.
+	local _, skadaSet = next(Skada:GetSets());
+
+	if not skadaSet or not skadaSet.gotboss or skadaSet.mobname ~= encounter.name then
+		self:Debug("No Skada set found for boss");
+		return callback(false);
+	end
+
+	local duration = skadaSet.time;
+	local startTime = skadaSet.starttime;
+	local playerParses = self:GetPlayersFromSet(skadaSet);
+
+	return callback(true, startTime, duration, playerParses);
+end
+
+
+function mod:GetParsesForEncounter(encounter, callback)
+	-- If Skada hasn't finshed the segment, wait for it
+	if Skada.current then
+		tinsert(self.pendingParseRequests, {encounter = encounter, callback = callback});
+	else
+		self:ProcessParseRequest(encounter, callback);
+	end
+end
+
+function mod:EndSegment()
+	self:Debug("Skada: EndSegment")
+
+	-- If we have requests waiting for EndSegment, process them now.
+	if #self.pendingParseRequests then
+		for _, pendingRequest in ipairs(self.pendingParseRequests) do
+			local encounter = pendingRequest.encounter;
+			local callback = pendingRequest.callback;
+			self:ProcessParseRequest(encounter, callback);
+		end
+		wipe(self.pendingParseRequests);
+	end
+end
+
+function mod:OnEnable()
+	self.pendingParseRequests = {};
+
+	self:SecureHook(Skada, "EndSegment");
+end
+
+function mod:OnDisable()
+	wipe(self.pendingParseRequests);
+
+	self:UnHook(Skada, "EndSegment");
+end
diff --git a/src/src_include.xml b/src/src_include.xml
index be9fd62..1ad2cc5 100644
--- a/src/src_include.xml
+++ b/src/src_include.xml
@@ -3,4 +3,5 @@
 	<Script file="inspect.lua"/>
 	<Script file="highscore.lua"/>
 	<Script file="gui.lua"/>
+	<Include file="parse_modules\parse_modules_include.xml"/>
 </Ui>
\ No newline at end of file