diff ObjectiveCore.lua @ 19:605e8f0e46db

ObjectiveCore / Style / Events / Frame - polishing the execution path for better performance - make use of the Blizzard_ObjectiveTracker bitfield values to ensure compatibility in possible secure hooks - avoid full updates when possible (using said bitfield values to indicate targeted sections) - extreme streamlining of event handling layout: specific reason updates are invoked from API hooks; broader updates are invoked by when the event listener catches something vague like 'QUEST_LOG_UPDATE'
author Nenue
date Wed, 06 Apr 2016 07:38:35 -0400
parents 880828018bf4
children 6bd2102d340b
line wrap: on
line diff
--- a/ObjectiveCore.lua	Tue Apr 05 02:38:01 2016 -0400
+++ b/ObjectiveCore.lua	Wed Apr 06 07:38:35 2016 -0400
@@ -3,29 +3,16 @@
 -- @project-revision@ @project-hash@
 -- @file-revision@ @file-hash@
 -- Created: 3/26/2016 1:51 AM
-local B = select(2,...).frame
-local pairs, setmetatable, type, tostring = pairs, setmetatable, type, tostring
-local format = string.format
+local B, _G = select(2,...).frame, _G
+local pairs, setmetatable, type, tostring = _G.pairs, _G.setmetatable, _G.type, _G.tostring
+local format = _G.format
 local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
 local print = B.print('Objectives')
-local ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame = ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame
-
---[[
-  Full quest info is available if:
-    - It's in the player quest log, or is available from the Gossip interface
-    - It's being shared from another player and is acceptible
-    - It's an auto-quest that is available in the current location
-  Partial quest info is availabe if:
-    - It's already completed (i.e. it appears in CompletedQuestInfo()).
-    - It's an scheduled interval quest (daily, weekly, etc.)
-    - It's contained in a quest link received from chat
-  Under any other circumstances, only minimal info can be pulled:
-    - Its availability to the player
-    - Its relation with the currently engaged NPC
-    - Its binary completion status
-
-]]
-
+local ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame = _G.ObjectiveTrackerFrame, _G.VeneerObjectiveScroll, _G.CreateFrame
+local Wrapper = _G.VeneerObjectiveWrapper
+local ipairs, tinsert, hooksecurefunc = _G.ipairs, _G.tinsert, _G.hooksecurefunc
+local Scroller = VeneerObjectiveWrapper.scrollArea
+local Scroll = _G.VeneerObjectiveScroll
 
 --- Performance values
 --[[
@@ -45,155 +32,324 @@
 	self:RegisterEvent("QUEST_TURNED_IN");
 	self:RegisterEvent("PLAYER_MONEY");
  ]]
-mod.Reason ={
-  UPDATE_MASK_AUTOQUEST            = 0x00000001,
-  UPDATE_MASK_QUEST                = 0x00000002,
-  UPDATE_MASK_BONUS                = 0x00000004,
-  UPDATE_MASK_CHEEVS               = 0x00000008,
-  UPDATE_MASK_ALL                  = 0x00000017,
 
-  QUEST_LOG_UPDATE                 = 0x00000003,
-  TRACKED_ACHIEVEMENT_LIST_CHANGED = 0x00000008,
-  QUEST_WATCH_LIST_CHANGED         = 0x00000003,
-  QUEST_AUTOCOMPLETE               = 0x00000003,
-  QUEST_ACCEPTED                   = 0x00000003,
-  SUPER_TRACKED_QUEST_CHANGED      = 0x00000002,
-  SCENARIO_UPDATE                  = 0x00000004,
-  SCENARIO_CRITERIA_UPDATE         = 0x00000004,
-  TRACKED_ACHIEVEMENT_UPDATE       = 0x00000008,
-  ZONE_CHANGED_NEW_AREA            = 0x00000017,
-  ZONE_CHANGED                     = 0x00000017,
-  QUEST_POI_UPDATE                 = 0x00000003,
-  QUEST_TURNED_IN                  = 0x00000003,
-}
+
+--- These are the bitfields used by Blizzard_ObjectiveTracker to determine which segments get parsed.
+--- They are replicated here so that plugins can make use of any securehook args involving this info.
+local OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST                       -- 0x0100
+local OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP = OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP	-- 0x0200
+local OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE		-- 0x0400
+local OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO	    		        -- 0x0800
+local OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT	= OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT           -- 0x1000
+
+
+local OBJECTIVE_TRACKER_UPDATE_STATIC = OBJECTIVE_TRACKER_UPDATE_STATIC					                         	-- 0x0000
+local OBJECTIVE_TRACKER_UPDATE_ALL = OBJECTIVE_TRACKER_UPDATE_ALL			                    	          		-- 0xFFFF
+local OBJECTIVE_TRACKER_UPDATE_ID = OBJECTIVE_TRACKER_UPDATE_ID                                           -- 0
+
+local OBJECTIVE_TRACKER_UPDATE_QUEST = OBJECTIVE_TRACKER_UPDATE_QUEST				        	                  	-- 0x0001
+local OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED = OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED		               		    -- 0x0002
+local OBJECTIVE_TRACKER_UPDATE_TASK_ADDED = OBJECTIVE_TRACKER_UPDATE_TASK_ADDED		    	              		-- 0x0004
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_SCENARIO			      	                 	-- 0x0008
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE = OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE		       	-- 0x0010
+local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT			                   	-- 0x0020
+local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED		  	      -- 0x0040
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED = OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED		-- 0x0080
+
+local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_ALL -- default
+--- Used to determine which trackers are listening for money events
 mod.MoneyReasons = 0
 
---- Baseline defaults
+--- Baseline defaults table; values defined in the files that they pertain to
 mod.defaults = {}
 
---- list used to make things happen
+--- Tracker display order
 mod.orderedNames = {'Bonus', 'AutoQuest', 'Quest', 'Cheevs'}
 
---- ipairs() list of handlers for wrapper update
-mod.orderedHandlers = {}
-mod.orderedTrackers = {}
-mod.indexedTrackers = {}
---- pairs() list of handler frames for tracker updates
-mod.namedTrackers = {}
+--- ipairs() argument tables
+mod.orderedHandlers = setmetatable({}, {__mode = "k"})
+mod.orderedTrackers = setmetatable({}, {__mode = "k"})
+mod.indexedTrackers = setmetatable({}, {__mode = "k"})
 
---- Handler stubs
+--- pairs() argument tables
+mod.namedTrackers = setmetatable({}, {__mode = "k"})
+
+local WRAPPER_ANCHOR_POINT = 'TOPRIGHT'
+local WRAPPER_OFFSET_X = 0
+local WRAPPER_OFFSET_Y = -200
+local WRAPPER_MAX_HEIGHT = 670
+local WRAPPER_WIDTH = 280
+local WRAPPER_HEADER_HEIGHT = 24
+
+mod.defaults.Wrapper = {
+  AnchorPoint = WRAPPER_ANCHOR_POINT,
+  OffsetX = WRAPPER_OFFSET_X,
+  OffsetY = WRAPPER_OFFSET_Y,
+  Height = WRAPPER_MAX_HEIGHT,
+  Width = WRAPPER_WIDTH,
+  HeaderHeight = WRAPPER_HEADER_HEIGHT
+}
+--- Tracker module definitions begin here; innards dealing with data retreival and output are defined further in
+mod.DefaultTracker = {
+  previousHeight = 0,
+
+  name = "temp",
+  displayName = "temp",
+  updateReasonModule = 0xFF00,
+  updateReasonEvent  = 0x00FF,
+
+  numWatched = 0,   --- number of entries being handled
+  numBlocks = 0,    --- number of blocks created
+  actualBlocks = 0, --- number of blocks in use
+
+  freeBlocks = {},  --- block heap
+  usedBlocks = {},
+
+  Info = {},        -- find data by ID
+  BlockInfo = {},   -- find data by block ID
+  Watched = {},     -- find watchIndex by data ID
+  WatchInfo = {},   -- find data by watch index
+  WatchBlock = {},  -- find block by watch index
+}
+
 mod.AutoQuest = {
   name = "AutoQuest",
   displayName = "Notice",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST +
+      OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
 }
 mod.Quest = {
   name = "Quest",
   displayName = "Quests",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST +
+      OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
 }
 mod.Cheevs = {
   name = "Cheevs",
   displayName = "Achievements",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT +
+      OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED,
 }
 mod.Bonus = {
   name = "Bonus",
   displayName = "Bonus Objectives",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST +
+        OBJECTIVE_TRACKER_UPDATE_TASK_ADDED +
+        OBJECTIVE_TRACKER_UPDATE_SCENARIO +
+        OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE +
+        OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED
 }
 
---- Handler template
-local CreateHandler = function (self, name, index)
-  print(self, name)
+local Tracker_string = function (self)
+  return self.name
+end
+local Tracker_call = function (self, reason)
+  self:Update(reason)
+end
+
+local Tracker_Initialize = function (self, name, index)
+  print('Initializing |cFF00FFFF'..name..'|r module...')
 
   local handler = setmetatable(mod[name] or {}, {
-    __tostring = function() return name end,
-    __call = function (self) mod.UpdateTracker(self) end
+    __tostring = Tracker_string,
+    __call = Tracker_call
   })
   if type(mod.orderedHandlers[index]) == 'table' then
     return mod.orderedHandlers[index]
   end
 
-  print('take up locals first')
+  print('|cFFFFFF00Acquiring locals')
   local preset = {}
-  for k,v in pairs(mod[name]) do
+  for k, _ in pairs(handler) do
     preset[k] = true
-    if type(v) == 'table' then
-      handler[k] = {}
-    else
-      handler[k] = v
-    end
   end
 
 
-  print('resulting handler contents')
+  print('|cFFFF8800Inheriting')
   for k, v in pairs(self) do
     if not handler[k] then
       if type(v) == 'table' then
         -- assume all tables to be local data; don't inherit or ref
         handler[k] = {}
       else
-        handler[k] = mod.Tracker[k]
+        handler[k] = self[k]
       end
-    else
-      print(name, 'has its own', k)
+      print('copying', k)
     end
   end
   print('|cFFFF4400'..tostring(name)..'|r:')
   for k, v in pairs(handler) do
-    print(format("%24s %8s %s", (preset[k] and '|cFFFFFFFF' or '|cFFFFFF00') .. k .. '|r', type(v), tostring(v)))
+    print(format("%32s %8s %s", (preset[k] and '|cFFFFFFFF' or '|cFFFFFF00') .. k .. '|r', type(v), tostring(v)))
   end
 
   mod[name] = handler
 
+  local trackerName = 'Veneer'..name..'Tracker'
+  local handler = mod[name]
+  local frame = CreateFrame('Frame', trackerName, _G.VeneerObjectiveScroll, 'VeneerTrackerTemplate')
+  frame.title:SetText(handler.displayName)
+  mod.SetBlockStyle(frame, 'Tracker', 'Normal')
+  handler.frame = frame
+  handler.trackerName = trackerName
+  mod.orderedTrackers[index] = frame
+  mod.namedTrackers[name] = frame
+  mod.indexedTrackers[handler] = frame
+  print('|cFFFF0088' .. trackerName .. '|r created for |cFF00FFFF' .. handler.displayName .. '|r module')
+
   mod.orderedHandlers[index] = handler
   return true
 end
 
-mod.Tracker = setmetatable({}, {
-  __call = CreateHandler,
-  __tostring = function() return 'DEFAULT_TRACKING_HANDLER' end
-})
-local Tracker = mod.Tracker
-Tracker.numWatched = 0   --- number of entries being handled
-Tracker.numBlocks = 0    --- number of blocks created
-Tracker.actualBlocks = 0 --- number of blocks in use
-
-Tracker.freeBlocks = {}  --- block heap
-Tracker.usedBlocks = {}
-
-Tracker.Watched = {}     -- find by watchIndex
-Tracker.Info = {}        -- find by data ID
-Tracker.BlockInfo = {}   -- find by block ID
-Tracker.WatchInfo = {}   -- find data  by watch index
-Tracker.WatchBlock = {}  -- find block by watch index
-
-
-
-Tracker.GetBlock = function(handler, blockIndex)
-  local block = handler.usedBlocks[blockIndex]
-  if not handler.usedBlocks[blockIndex] then
-    if #handler.freeBlocks >= 1 then
-      block = handler.freeBlocks[#handler.freeBlocks]
-      handler.freeBlocks[#handler.freeBlocks] = nil
+function mod:OnEvent (event, ...)
+  local isHandled
+  print('|cFF00FF00'.. event ..'|r', ...)
+  if ( event == "QUEST_LOG_UPDATE" ) then
+    mod:Update(OBJECTIVE_TRACKER_UPDATE_QUEST);
+  elseif ( event == "TRACKED_ACHIEVEMENT_UPDATE" ) then
+    --AchievementObjectiveTracker_OnAchievementUpdate(...);
+    mod.Cheevs:Update(OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT)
+  elseif ( event == "QUEST_ACCEPTED" ) then
+    local questLogIndex, questID = ...;
+    if ( IsQuestTask(questID) ) then
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_TASK_ADDED, questID);
     else
-      block = CreateFrame('Frame', 'Veneer'..tostring(handler)..'Block'..blockIndex, VeneerObjectiveScroll, 'VeneerTrackerBlock')
-      block.SetStyle = mod.SetBlockStyle
-      block.Select = handler.Select
-      block.Open = handler.Open
-      block.Remove = handler.Remove
-      block.Link = handler.Link
-      block:SetScript('OnMouseUp', handler.OnMouseUp)
-      block:SetScript('OnMouseDown', handler.OnMouseDown)
-      block:ClearAllPoints()
+      if ( AUTO_QUEST_WATCH == "1" and GetNumQuestWatches() < MAX_WATCHABLE_QUESTS ) then
+        AddQuestWatch(questLogIndex);
+        SetSuperTrackedQuestID(questID);
+      end
+    end
+  elseif ( event == "TRACKED_ACHIEVEMENT_LIST_CHANGED" ) then
+    local achievementID, added = ...;
+    if ( added ) then
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, achievementID);
+    else
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT);
+    end
+  elseif ( event == "QUEST_WATCH_LIST_CHANGED" ) then
+    local questID, added = ...;
+    if ( added ) then
+      if ( not IsQuestTask(questID) ) then
+        mod:Update(OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, questID);
+      end
+    else
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_QUEST);
+    end
+  elseif ( event == "QUEST_POI_UPDATE" ) then
+    QuestPOIUpdateIcons();
+    if ( GetCVar("trackQuestSorting") == "proximity" ) then
+      SortQuestWatches();
     end
 
-    handler.usedBlocks[blockIndex] = block
+    mod:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST);
+
+  elseif ( event == "SCENARIO_CRITERIA_UPDATE" ) then
+    mod:Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO);
+  elseif ( event == "SUPER_TRACKED_QUEST_CHANGED" ) then
+    mod:Update(OBJECTIVE_TRACKER_UPDATE_QUEST)
+  elseif ( event == "ZONE_CHANGED" ) then
+    local inMicroDungeon = IsPlayerInMicroDungeon();
+    if ( inMicroDungeon ~= self.inMicroDungeon ) then
+      if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
+        SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
+      end
+      --SortQuestWatches();
+      self.inMicroDungeon = inMicroDungeon;
+    end
+  elseif ( event == "QUEST_AUTOCOMPLETE" ) then
+    local questId = ...;
+    AddAutoQuestPopUp(questId, "COMPLETE");
+  elseif ( event == "SCENARIO_UPDATE" ) then
+    local newStage = ...;
+    if ( newStage ) then
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE);
+    else
+      mod:Update(OBJECTIVE_TRACKER_UPDATE_SCENARIO);
+    end
+  elseif ( event == "ZONE_CHANGED_NEW_AREA" ) then
+    if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
+      SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
+    end
+    SortQuestWatches();
+  elseif ( event == "QUEST_TURNED_IN" ) then
+    local questID, xp, money = ...;
+    if ( IsQuestTask(questID) ) then
+      mod.Bonus:Update()
+    end
+  elseif ( event == "PLAYER_MONEY" and self.watchMoneyReasons > 0 ) then
+    mod:Update(self.watchMoneyReasons);
   end
-  return handler.usedBlocks[blockIndex]
 end
 
+--- Done once per ui load
+local BlizzHooks = {
+  ['AddQuestWatch'] = 'AddQuestWatch',
+  ['RemoveQuestWatch'] = 'RemoveQuestWatch',
+  ['AbandonQuest'] = 'AbandonQuest',
+  ['AcknowledgeAutoAcceptQuest'] = 'AcknowledgeAutoAcceptQuest',
+  ['AddAutoQuestPopUp'] = 'AddAutoQuestPopUp',
+  ['RemoveAutoQuestPopUp'] = 'RemoveAutoQuestPopUp',
+  ['AddTrackedAchievement'] = 'AddTrackedAchievement',
+  ['RemoveTrackedAchievement'] = 'RemoveTrackedAchievement',
+  ['SetSuperTrackedQuestID'] = 'SetSuperTrackedQuestID'
+}
+local VeneerData
 function mod:OnInitialize()
-  self.InitializeWrapper()
-  self.InitializeXPTracker()
-  mod.SetEvents()
+  local c = mod.Conf.Wrapper
+  VeneerData = _G.VeneerData
+  VeneerData.CallLog = VeneerData.CallLog or {}
+  if not mod.isHooked then
+    mod.isHooked = true
+    for blizzFunc, veneerFunc in pairs(BlizzHooks) do
+      if mod[veneerFunc] then
+        hooksecurefunc(blizzFunc, mod[veneerFunc])
+      else
+        hooksecurefunc(blizzFunc, function(...)
+          print('|cFFFF0088securehook('..tostring(blizzFunc)..')|r args:', ...)
+          tinsert(VeneerData.CallLog, {blizzFunc, ...})
+        end)
+      end
+    end
+  end
+  Scroller:SetScrollChild(Scroll)
+  Scroller:SetWidth(c.Width)
+  Scroll:SetPoint('TOPLEFT', Scroller, 'TOPLEFT')
+  Scroll:SetWidth(c.Width)
   ObjectiveTrackerFrame:UnregisterAllEvents()
   ObjectiveTrackerFrame:Hide()
 end
+
+--- Done any time the the minimize button is toggled up
+
+function mod:OnEnable()
+  for id, name in ipairs(mod.orderedNames) do
+    if not mod.orderedHandlers[id] then
+      Tracker_Initialize(mod.DefaultTracker, name, id)
+    end
+  end
+
+  for event, func in pairs(mod) do
+    if type(func) == 'function' and event:match('^[A-Z_]+$') then
+      print('|cFFFF44FFlistening to', event)
+      Wrapper:RegisterEvent(event)
+    end
+  end
+
+  local c = mod.Conf.Wrapper
+  Wrapper:SetPoint(c.AnchorPoint, UIParent, c.AnchorPoint, c.OffsetX, c.OffsetY)
+  B.Conf.FramePosition[Wrapper:GetName()] = {c.AnchorPoint, c.AnchorPoint, c.OffsetX, c.OffsetY}
+  Wrapper:SetWidth(c.Width)
+
+
+  mod.InitializeWidgets()
+  mod:Update()
+end
+
+function mod:OnDisable()
+
+end
+
+