diff --git a/.pkgmeta b/.pkgmeta index 3df0ef9..087de5a 100644 --- a/.pkgmeta +++ b/.pkgmeta @@ -17,3 +17,6 @@ externals: vendor/LibItemUpgradeInfo-1.0: url: git://git.wowace.com/wow/libitemupgradeinfo-1-0/mainline.git tag: Release-70000-24 + vendor/LibGroupInSpecT-1.1: + url: svn://svn.wowace.com/wow/libgroupinspect/mainline/trunk + # TODO(2016-07-30): Specify release tag once available diff --git a/vendor/LibGroupInSpecT-1.1/Changelog-LibGroupInSpecT-1.1-r84.txt b/vendor/LibGroupInSpecT-1.1/Changelog-LibGroupInSpecT-1.1-r84.txt new file mode 100644 index 0000000..5cf3ee6 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Changelog-LibGroupInSpecT-1.1-r84.txt @@ -0,0 +1,44 @@ +------------------------------------------------------------------------ +r84 | greltok | 2016-07-30 07:04:53 +0000 (Sat, 30 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +Added Demon Hunter. +------------------------------------------------------------------------ +r83 | greltok | 2016-07-29 07:40:55 +0000 (Fri, 29 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + M /trunk/LibGroupInSpecT-1.1.toc + +Removed glyphs. +------------------------------------------------------------------------ +r82 | greltok | 2016-07-22 06:29:55 +0000 (Fri, 22 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +Survival hunters are melee. +------------------------------------------------------------------------ +r81 | nebula169 | 2016-07-21 02:34:51 +0000 (Thu, 21 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +Remove Gladiator Stance checks. +------------------------------------------------------------------------ +r80 | tercioo | 2016-07-21 02:30:42 +0000 (Thu, 21 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +- Tercio's quick fix: the spellid '156291' for gladiator stance doesn't exist on Legion. +------------------------------------------------------------------------ +r79 | tercioo | 2016-07-18 19:47:24 +0000 (Mon, 18 Jul 2016) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +Quick fix for Legion Pre-Patch: NUM_GLYPH_SLOTS doesn't exists any more. +------------------------------------------------------------------------ +r78 | greltok | 2015-02-21 05:13:59 +0000 (Sat, 21 Feb 2015) | 1 line +Changed paths: + M /trunk/LibGroupInSpecT-1.1.lua + +Added event "GroupInSpecT_InspectReady". :Rescan() adds units to staleq rather than mainq. +------------------------------------------------------------------------ diff --git a/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua b/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua new file mode 100644 index 0000000..e2edae9 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.lua @@ -0,0 +1,898 @@ +-- vim: ts=2 sw=2 ai et fenc=utf8 + +--[[ +-- These events can be registered for using the regular CallbackHandler ways. +-- +-- "GroupInSpecT_Update", guid, unit, info +-- "GroupInSpecT_Remove, guid +-- "GroupInSpecT_InspectReady", guid, unit +-- +-- Where <info> is a table containing some or all of the following: +-- .guid +-- .name +-- .realm +-- .race +-- .race_localized +-- .class +-- .class_localized +-- .class_id +-- .gender -- 2 = male, 3 = female +-- .global_spec_id +-- .spec_index +-- .spec_name_localized +-- .spec_description +-- .spec_icon +-- .spec_background +-- .spec_role +-- .spec_role_detailed +-- .spec_group -- active spec group (1/2/nil) +-- .talents = { +-- [<talent_id>] = { +-- .tier +-- .column +-- .name_localized +-- .icon +-- .talent_id +-- .spell_id +-- } +-- ... +-- } +-- .lku -- last known unit id +-- .not_visible +-- +-- Functions for external use: +-- +-- lib:Rescan (guid or nil) +-- Force a rescan of the given group member GUID, or of all current group members if nil. +-- +-- lib:QueuedInspections () +-- Returns an array of GUIDs of outstanding inspects. +-- +-- lib:StaleInspections () +-- Returns an array of GUIDs for which the data has become stale and is +-- awaiting an update (no action required, the refresh happens internally). +-- Due to Blizzard exposing no events on (re/un)talent, there will be +-- frequent marking of inspect data as being stale. +-- +-- lib:GetCachedInfo (guid) +-- Returns the cached info for the given GUID, if available, nil otherwise. +-- Information is cached for current group members only. +-- +-- lib:GroupUnits () +-- Returns an array with the set of unit ids for the current group. +--]] + +local MAJOR, MINOR = "LibGroupInSpecT-1.1", tonumber (("$Revision: 84 $"):match ("(%d+)") or 0) + +if not LibStub then error(MAJOR.." requires LibStub") end +local lib = LibStub:NewLibrary (MAJOR, MINOR) +if not lib then return end + +lib.events = lib.events or LibStub ("CallbackHandler-1.0"):New (lib) +if not lib.events then error(MAJOR.." requires CallbackHandler") end + + +local UPDATE_EVENT = "GroupInSpecT_Update" +local REMOVE_EVENT = "GroupInSpecT_Remove" +local INSPECT_READY_EVENT = "GroupInSpecT_InspectReady" + +local COMMS_PREFIX = "LGIST11" +local COMMS_FMT = "1" +local COMMS_DELIM = "\a" + +local INSPECT_DELAY = 1.5 +local INSPECT_TIMEOUT = 10 -- If we get no notification within 10s, give up on unit + +local MAX_ATTEMPTS = 2 + +--[===[@debug@ +lib.debug = true +local function debug (...) + if lib.debug then -- allow programmatic override of debug output by client addons + print (...) + end +end +--@end-debug@]===] + +function lib.events:OnUsed(target, eventname) + if eventname == INSPECT_READY_EVENT then + target.inspect_ready_used = true + end +end + +function lib.events:OnUnused(target, eventname) + if eventname == INSPECT_READY_EVENT then + target.inspect_ready_used = nil + end +end + +-- Frame for events +local frame = _G[MAJOR .. "_Frame"] or CreateFrame ("Frame", MAJOR .. "_Frame") +lib.frame = frame +frame:Hide() +frame:UnregisterAllEvents () +frame:RegisterEvent ("PLAYER_LOGIN") +frame:RegisterEvent ("PLAYER_LOGOUT") +if not frame.OnEvent then + frame.OnEvent = function(this, event, ...) + local eventhandler = lib[event] + return eventhandler and eventhandler (lib, ...) + end + frame:SetScript ("OnEvent", frame.OnEvent) +end + + +-- Hide our run-state in an easy-to-dump object +lib.state = { + mainq = {}, staleq = {}, -- inspect queues + t = 0, + last_inspect = 0, + current_guid = nil, + throttle = 0, + tt = 0, + debounce_send_update = 0, +} +lib.cache = {} +lib.static_cache = {} + + +-- Note: if we cache NotifyInspect, we have to hook before we cache it! +if not lib.hooked then + hooksecurefunc("NotifyInspect", function (...) return lib:NotifyInspect (...) end) + lib.hooked = true +end +function lib:NotifyInspect(unit) + self.state.last_inspect = GetTime() +end + + +-- Get local handles on the key API functions +local CanInspect = _G.CanInspect +local ClearInspectPlayer = _G.ClearInspectPlayer +local GetClassInfo = _G.GetClassInfo +local GetNumSubgroupMembers = _G.GetNumSubgroupMembers +local GetNumSpecializationsForClassID = _G.GetNumSpecializationsForClassID +local GetPlayerInfoByGUID = _G.GetPlayerInfoByGUID +local GetInspectSpecialization = _G.GetInspectSpecialization +local GetSpecialization = _G.GetSpecialization +local GetSpecializationInfo = _G.GetSpecializationInfo +local GetSpecializationInfoForClassID = _G.GetSpecializationInfoForClassID +local GetSpecializationRoleByID = _G.GetSpecializationRoleByID +local GetSpellInfo = _G.GetSpellInfo +local GetTalentInfo = _G.GetTalentInfo +local IsInRaid = _G.IsInRaid +--local NotifyInspect = _G.NotifyInspect -- Don't cache, as to avoid missing future hooks +local GetNumClasses = _G.GetNumClasses +local UnitExists = _G.UnitExists +local UnitGUID = _G.UnitGUID +local UnitInParty = _G.UnitInParty +local UnitInRaid = _G.UnitInRaid +local UnitIsConnected = _G.UnitIsConnected +local UnitIsPlayer = _G.UnitIsPlayer +local UnitIsUnit = _G.UnitIsUnit +local UnitName = _G.UnitName + + +local global_spec_id_roles_detailed = { + -- Death Knight + [250] = "tank", -- Blood + [251] = "melee", -- Frost + [252] = "melee", -- Unholy + -- Demon Hunter + [577] = "melee", -- Havoc + [581] = "tank", -- Vengeance + -- Druid + [102] = "ranged", -- Balance + [103] = "melee", -- Feral + [104] = "tank", -- Guardian + [105] = "healer", -- Restoration + -- Hunter + [253] = "ranged", -- Beast Mastery + [254] = "ranged", -- Marksmanship + [255] = "melee", -- Survival + -- Mage + [62] = "ranged", -- Arcane + [63] = "ranged", -- Fire + [64] = "ranged", -- Frost + -- Monk + [268] = "tank", -- Brewmaster + [269] = "melee", -- Windwalker + [270] = "healer", -- Mistweaver + -- Paladin + [65] = "healer", -- Holy + [66] = "tank", -- Protection + [70] = "melee", -- Retribution + -- Priest + [256] = "healer", -- Discipline + [257] = "healer", -- Holy + [258] = "ranged", -- Shadow + -- Rogue + [259] = "melee", -- Assassination + [260] = "melee", -- Combat + [261] = "melee", -- Subtlety + -- Shaman + [262] = "ranged", -- Elemental + [263] = "melee", -- Enhancement + [264] = "healer", -- Restoration + -- Warlock + [265] = "ranged", -- Affliction + [266] = "ranged", -- Demonology + [267] = "ranged", -- Destruction + -- Warrior + [71] = "melee", -- Arms + [72] = "melee", -- Fury + [73] = "tank", -- Protection +} + +local class_fixed_roles = { + HUNTER = "DAMAGER", + MAGE = "DAMAGER", + ROGUE = "DAMAGER", + WARLOCK = "DAMAGER", +} + +local class_fixed_roles_detailed = { + MAGE = "ranged", + ROGUE = "melee", + WARLOCK = "ranged", +} + +-- Inspects only work after being fully logged in, so track that +function lib:PLAYER_LOGIN () + self.state.logged_in = true + + self:CacheGameData () + + frame:RegisterEvent ("INSPECT_READY") + frame:RegisterEvent ("GROUP_ROSTER_UPDATE") + frame:RegisterEvent ("PLAYER_ENTERING_WORLD") + frame:RegisterEvent ("UNIT_LEVEL") + frame:RegisterEvent ("PLAYER_TALENT_UPDATE") + frame:RegisterEvent ("PLAYER_SPECIALIZATION_CHANGED") + frame:RegisterEvent ("UNIT_SPELLCAST_SUCCEEDED") + frame:RegisterEvent ("UNIT_NAME_UPDATE") + frame:RegisterEvent ("UNIT_AURA") + frame:RegisterEvent ("CHAT_MSG_ADDON") + RegisterAddonMessagePrefix (COMMS_PREFIX) + + local guid = UnitGUID ("player") + local info = self:BuildInfo ("player") + self.events:Fire (UPDATE_EVENT, guid, "player", info) +end + +function lib:PLAYER_LOGOUT () + self.state.logged_in = false +end + + +-- Simple timer +do + lib.state.t = 0 + if not frame.OnUpdate then -- ticket #4 if the OnUpdate code every changes we should stop borrowing the existing handler + frame.OnUpdate = function(this, elapsed) + lib.state.t = lib.state.t + elapsed + lib.state.tt = lib.state.tt + elapsed + if lib.state.t > INSPECT_DELAY then + lib:ProcessQueues () + lib.state.t = 0 + end + -- Unthrottle, essentially allowing 1 msg every 3 seconds, but with substantial burst capacity + if lib.state.tt > 3 and lib.state.throttle > 0 then + lib.state.throttle = lib.state.throttle - 1 + lib.state.tt = 0 + end + if lib.state.debounce_send_update > 0 then + local debounce = lib.state.debounce_send_update - elapsed + lib.state.debounce_send_update = debounce + if debounce <= 0 then lib:SendLatestSpecData () end + end + end + frame:SetScript("OnUpdate", frame.OnUpdate) -- this is good regardless of the handler check above because otherwise a new anonymous function is created every time the OnUpdate code runs + end +end + + +-- Internal library functions + +-- Caches to deal with API shortcomings as well as performance +lib.static_cache.global_specs = {} -- [gspec] -> { .idx, .name_localized, .description, .icon, .background, .role } +lib.static_cache.class_to_class_id = {} -- [CLASS] -> class_id + +-- The talents cache can no longer be pre-fetched on login, but is now constructed class-by-class as we inspect people. +-- This probably means we want to only ever access it through the GetCachedTalentInfo() helper function below. +lib.static_cache.talents = {} -- [talent_id] -> { .spell_id, .talent_id, .name_localized, .icon, .tier, .column } + +-- Dridzt: I'd love another way but none of the GetTalent* functions return spellID, GetTalentLink() and parsing the link gives talentID that's not related to spellID as well +-- A quick tooltip scan is cheap though so elegance aside this is a good workaround considering this only runs once +local tip = CreateFrame ("GameTooltip", MAJOR.."ScanTip", nil, "GameTooltipTemplate") +tip:SetOwner (UIParent, "ANCHOR_NONE") + +function lib:GetCachedTalentInfo (class_id, tier, col, group, is_inspect, unit) + local talents = self.static_cache.talents + local talent_id, name, icon, sel, avail = GetTalentInfo (tier, col, group, is_inspect, unit) + if not talent_id or not class_id then + --[===[@debug@ + debug ("GetCachedTalentInfo("..tostring(class_id)..","..tier..","..col..","..group..","..tostring(is_inspect)..","..tostring(unit)..") returned nil") --@end-debug@]===] + return {} + end + talents[class_id] = talents[class_id] or {} + local class_talents = talents[class_id] + if not class_talents[talent_id] then + tip:ClearLines () + tip:SetTalent (talent_id, is_inspect, group) + local _, _,spell_id = tip:GetSpell () + class_talents[talent_id] = { + spell_id = spell_id, + talent_id = talent_id, + name_localized = name, + icon = icon, + tier = tier, + column = col, + } + end + return class_talents[talent_id], sel +end + + +function lib:CacheGameData () + local gspecs = self.static_cache.global_specs + gspecs[0] = {} -- Handle no-specialization case + for class_id = 1, GetNumClasses () do + for idx = 1, GetNumSpecializationsForClassID (class_id) do + local gspec_id, name, description, icon, background = GetSpecializationInfoForClassID (class_id, idx) + gspecs[gspec_id] = {} + local gspec = gspecs[gspec_id] + gspec.idx = idx + gspec.name_localized = name + gspec.description = description + gspec.icon = icon + gspec.background = background + gspec.role = GetSpecializationRoleByID (gspec_id) + end + + local _, class = GetClassInfo (class_id) + self.static_cache.class_to_class_id[class] = class_id + end +end + + +function lib:GuidToUnit (guid) + local info = self.cache[guid] + if info and info.lku and UnitGUID (info.lku) == guid then return info.lku end + + for i,unit in ipairs (self:GroupUnits ()) do + if UnitExists (unit) and UnitGUID (unit) == guid then + if info then info.lku = unit end + return unit + end + end +end + + +function lib:Query (unit) + if not UnitIsPlayer (unit) then return end -- NPC + + if UnitIsUnit (unit, "player") then + self.events:Fire (UPDATE_EVENT, UnitGUID("player"), "player", self:BuildInfo ("player")) + return + end + + local mainq, staleq = self.state.mainq, self.state.staleq + + local guid = UnitGUID (unit) + if not mainq[guid] then + mainq[guid] = 1 + staleq[guid] = nil + self.frame:Show () -- Start timer if not already running + end +end + + +function lib:Refresh (unit) + local guid = UnitGUID (unit) + if not guid then return end + --[===[@debug@ + debug ("Refreshing "..unit) --@end-debug@]===] + if not self.state.mainq[guid] then + self.state.staleq[guid] = 1 + self.frame:Show () + end +end + + +function lib:ProcessQueues () + if not self.state.logged_in then return end + if InCombatLockdown () then return end -- Never inspect while in combat + if UnitIsDead ("player") then return end -- You can't inspect while dead, so don't even try + if InspectFrame and InspectFrame:IsShown () then return end -- Don't mess with the UI's inspections + + local mainq = self.state.mainq + local staleq = self.state.staleq + + if not next (mainq) and next(staleq) then + --[===[@debug@ + debug ("Main queue empty, swapping main and stale queues") --@end-debug@]===] + self.state.mainq, self.state.staleq = self.state.staleq, self.state.mainq + mainq, staleq = staleq, mainq + end + + if (self.state.last_inspect + INSPECT_TIMEOUT) < GetTime () then + -- If there was an inspect going, it's timed out, so either retry or move it to stale queue + local guid = self.state.current_guid + if guid then + --[===[@debug@ + debug ("Inspect timed out for "..guid) --@end-debug@]===] + + local count = mainq and mainq[guid] or (MAX_ATTEMPTS + 1) + if not self:GuidToUnit (guid) then + --[===[@debug@ + debug ("No longer applicable, removing from queues") --@end-debug@]===] + mainq[guid], staleq[guid] = nil, nil + elseif count > MAX_ATTEMPTS then + --[===[@debug@ + debug ("Excessive retries, moving to stale queue") --@end-debug@]===] + mainq[guid], staleq[guid] = nil, 1 + else + mainq[guid] = count + 1 + end + self.state.current_guid = nil + end + end + + if self.state.current_guid then return end -- Still waiting on our inspect data + + for guid,count in pairs (mainq) do + local unit = self:GuidToUnit (guid) + if not unit then + --[===[@debug@ + debug ("No longer applicable, removing from queues") --@end-debug@]===] + mainq[guid], staleq[guid] = nil, nil + elseif not CanInspect (unit) or not UnitIsConnected (unit) then + --[===[@debug@ + debug ("Cannot inspect "..unit..", aka "..(UnitName(unit) or "nil")..", moving to stale queue") --@end-debug@]===] + mainq[guid], staleq[guid] = nil, 1 + else + --[===[@debug@ + debug ("Inspecting "..unit..", aka "..(UnitName(unit) or "nil")) --@end-debug@]===] + mainq[guid] = count + 1 + self.state.current_guid = guid + NotifyInspect (unit) + break + end + end + + if not next (mainq) and not next (staleq) and self.state.throttle == 0 and self.state.debounce_send_update <= 0 then + frame:Hide() -- Cancel timer, nothing queued and no unthrottling to be done + end +end + + +function lib:UpdatePlayerInfo (guid, unit, info) + info.class_localized, info.class, info.race_localized, info.race, info.gender, info.name, info.realm = GetPlayerInfoByGUID (guid) + local class = info.class + if info.realm and info.realm == "" then info.realm = nil end + info.class_id = class and self.static_cache.class_to_class_id[class] + if not info.spec_role then info.spec_role = class and class_fixed_roles[class] end + if not info.spec_role_detailed then info.spec_role_detailed = class and class_fixed_roles_detailed[class] end + info.lku = unit +end + + +function lib:BuildInfo (unit) + local guid = UnitGUID (unit) + if not guid then return end + + local cache = self.cache + local info = cache[guid] or {} + cache[guid] = info + info.guid = guid + + self:UpdatePlayerInfo (guid, unit, info) + -- On a cold login, GetPlayerInfoByGUID() doesn't seem to be usable, so mark as stale + local class = info.class + if not class and not self.state.mainq[guid] then + self.state.staleq[guid] = 1 + self.frame:Show () + end + + local is_inspect = not UnitIsUnit (unit, "player") + local spec = GetSpecialization () + info.global_spec_id = is_inspect and GetInspectSpecialization (unit) or spec and GetSpecializationInfo (spec) + + local gspecs = self.static_cache.global_specs + if not info.global_spec_id or not gspecs[info.global_spec_id] then -- not a valid spec_id + info.global_spec_id = nil + else + local gspec_id = info.global_spec_id + local spec_info = gspecs[gspec_id] + info.spec_index = spec_info.idx + info.spec_name_localized = spec_info.name_localized + info.spec_description = spec_info.description + info.spec_icon = spec_info.icon + info.spec_background = spec_info.background + info.spec_role = spec_info.role + info.spec_role_detailed = global_spec_id_roles_detailed[gspec_id] + end + + if not info.spec_role then info.spec_role = class and class_fixed_roles[class] end + if not info.spec_role_detailed then info.spec_role_detailed = class and class_fixed_roles_detailed[class] end + + info.talents = info.talents or {} + + -- If GetPlayerInfoByGUID didn't return the class, we can't do talents yet + if info.class_id then + info.spec_group = GetActiveSpecGroup (is_inspect) + wipe (info.talents) -- Due to spec-specific talents we might leave things in on a spec-change otherwise + for tier = 1, MAX_TALENT_TIERS do + for col = 1, NUM_TALENT_COLUMNS do + local talent, sel = self:GetCachedTalentInfo (info.class_id, tier, col, info.spec_group, is_inspect, unit) + if talent and talent.talent_id and sel then + info.talents[talent.talent_id] = talent + end + end + end + end + + info.glyphs = wipe (info.glyphs or {}) -- kept for addons that still refer to this + + if is_inspect and not UnitIsVisible (unit) and UnitIsConnected (unit) then info.not_visible = true end + + return info +end + + +function lib:INSPECT_READY (guid) + local unit = self:GuidToUnit (guid) + local finalize = false + if unit then + if guid == self.state.current_guid then + self.state.current_guid = nil -- Got what we asked for + finalize = true + --[===[@debug@ + debug ("Got inspection data for requested guid "..guid) --@end-debug@]===] + end + + local mainq, staleq = self.state.mainq, self.state.staleq + mainq[guid], staleq[guid] = nil, nil + + local gspec_id = GetInspectSpecialization (unit) + if not self.static_cache.global_specs[gspec_id] then -- Bah, got garbage, flag as stale and try again + staleq[guid] = 1 + return + end + + self.events:Fire (UPDATE_EVENT, guid, unit, self:BuildInfo (unit)) + self.events:Fire (INSPECT_READY_EVENT, guid, unit) + end + if finalize then + ClearInspectPlayer () + end +end + + +function lib:PLAYER_ENTERING_WORLD () + if self.commScope == "INSTANCE_CHAT" then + -- Handle moving directly from one LFG to another + self.commScope = nil + self:UpdateCommScope () + end +end + + +-- Group handling parts + +local members = {} +function lib:GROUP_ROSTER_UPDATE () + local group = self.cache + local units = self:GroupUnits () + -- Find new members + for i,unit in ipairs (self:GroupUnits ()) do + local guid = UnitGUID (unit) + if guid then + members[guid] = true + if not group[guid] then + self:Query (unit) + -- Update with what we have so far (guid, unit, name/class/race?) + self.events:Fire (UPDATE_EVENT, guid, unit, self:BuildInfo (unit)) + end + end + end + -- Find removed members + for guid in pairs (group) do + if not members[guid] then + group[guid] = nil + self.events:Fire (REMOVE_EVENT, guid, nil) + end + end + wipe (members) + self:UpdateCommScope () +end + + +function lib:DoPlayerUpdate () + self:Query ("player") + self.state.debounce_send_update = 2.5 -- Hold off 2.5sec before sending update + self.frame:Show () +end + + +function lib:SendLatestSpecData () + local scope = self.commScope + if not scope then return end + + local guid = UnitGUID ("player") + local info = self.cache[guid] + if not info then return end + + -- fmt, guid, global_spec_id, talent1 -> MAX_TALENT_TIERS + -- sequentially, allow no gaps for missing talents we decode by index on the receiving end. + local datastr = COMMS_FMT..COMMS_DELIM..guid..COMMS_DELIM..(info.global_spec_id or 0) + local talentCount = 1 + for k in pairs(info.talents) do + datastr = datastr..COMMS_DELIM..k + talentCount = talentCount + 1 + end + for i=talentCount,MAX_TALENT_TIERS do + datastr = datastr..COMMS_DELIM..0 + end + + --[===[@debug@ + debug ("Sending LGIST update to "..scope) --@end-debug@]===] + SendAddonMessage(COMMS_PREFIX, datastr, scope) +end + + +function lib:UpdateCommScope () + local scope = (IsInGroup (LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT") or (IsInRaid () and "RAID") or (IsInGroup (LE_PARTY_CATEGORY_HOME) and "PARTY") + if self.commScope ~= scope then + self.commScope = scope + self:DoPlayerUpdate () + end +end + + +-- Indicies for various parts of the split data msg +local msg_idx = {} +msg_idx.fmt = 1 +msg_idx.guid = msg_idx.fmt + 1 +msg_idx.global_spec_id = msg_idx.guid + 1 +msg_idx.talents = msg_idx.global_spec_id + 1 +msg_idx.end_talents = msg_idx.talents + MAX_TALENT_TIERS - 1 + +function lib:CHAT_MSG_ADDON (prefix, datastr, scope, sender) + if prefix ~= COMMS_PREFIX or scope ~= self.commScope then return end + --[===[@debug@ + debug ("Incoming LGIST update from "..(scope or "nil").."/"..(sender or "nil")..": "..(datastr:gsub(COMMS_DELIM,";") or "nil")) --@end-debug@]===] + + local data = { strsplit (COMMS_DELIM,datastr) } + local fmt = data[msg_idx.fmt] + if fmt ~= COMMS_FMT then return end -- Unknown format, ignore + + local guid = data[msg_idx.guid] + + local senderguid = UnitGUID(sender) + if senderguid and senderguid ~= guid then return end + + local info = guid and self.cache[guid] + if not info then return end -- Never allow random message to create new group member entries! + + local unit = self:GuidToUnit (guid) + if not unit then return end + if UnitIsUnit (unit, "player") then return end -- we're already up-to-date, comment out for solo debugging + + self.state.throttle = self.state.throttle + 1 + self.frame:Show () -- Ensure we're unthrottling + if self.state.throttle > 40 then return end -- If we ever hit this, someone's being "funny" + + info.class_localized, info.class, info.race_localized, info.race, info.gender, info.name, info.realm = GetPlayerInfoByGUID (guid) + if info.realm and info.realm == "" then info.realm = nil end + info.class_id = self.static_cache.class_to_class_id[info.class] + + local gspecs = self.static_cache.global_specs + + local gspec_id = data[msg_idx.global_spec_id] and tonumber (data[msg_idx.global_spec_id]) + if not gspec_id or not gspecs[gspec_id] then return end -- Malformed message, avoid throwing errors by using this nil + + info.global_spec_id = gspec_id + info.spec_index = gspecs[gspec_id].idx + info.spec_name_localized = gspecs[gspec_id].name_localized + info.spec_description = gspecs[gspec_id].description + info.spec_icon = gspecs[gspec_id].icon + info.spec_background = gspecs[gspec_id].background + info.spec_role = gspecs[gspec_id].role + info.spec_role_detailed = global_spec_id_roles_detailed[gspec_id] + + local need_inspect = nil + info.talents = wipe (info.talents or {}) + local talents = self.static_cache.talents[info.class_id] + if talents then -- The group entry is created before we have inspect-data, so may not have cached talents yet + for i = msg_idx.talents, msg_idx.end_talents do + local talent_id = tonumber (data[i]) + if talent_id and talent_id > 0 then + if talents[talent_id] then + info.talents[talent_id] = talents[talent_id] + else + -- While we had some talents for this class, we apparently didn't have all for this particular spec, so mark for inspect + need_inspect = 1 + end + end + end + else + -- Talents weren't pre-cached, so mark for inspect + need_inspect = 1 + end + + info.glyphs = wipe (info.glyphs or {}) -- kept for addons that still refer to this + + local mainq, staleq = self.state.mainq, self.state.staleq + local want_inspect = not need_inspect and self.inspect_ready_used and (mainq[guid] or staleq[guid]) and 1 or nil + mainq[guid], staleq[guid] = need_inspect, want_inspect + if need_inspect or want_inspect then self.frame:Show () end + + --[===[@debug@ + debug ("Firing LGIST update event for unit "..unit..", GUID "..guid) --@end-debug@]===] + self.events:Fire (UPDATE_EVENT, guid, unit, info) +end + + +function lib:UNIT_LEVEL (unit) + if UnitInRaid (unit) or UnitInParty (unit) then + self:Refresh (unit) + end + if UnitIsUnit (unit, "player") then + self:DoPlayerUpdate () + end +end + + +function lib:PLAYER_TALENT_UPDATE () + self:DoPlayerUpdate () +end + + +function lib:PLAYER_SPECIALIZATION_CHANGED (unit) +-- This event seems to fire a lot, and for no particular reason *sigh* +-- if UnitInRaid (unit) or UnitInParty (unit) then +-- self:Refresh (unit) +-- end + if unit and UnitIsUnit (unit, "player") then + self:DoPlayerUpdate () + end +end + + +function lib:UNIT_NAME_UPDATE (unit) + local group = self.cache + local guid = UnitGUID (unit) + local info = guid and group[guid] + if info then + self:UpdatePlayerInfo (guid, unit, info) + if info.name ~= UNKNOWN then + self.events:Fire (UPDATE_EVENT, guid, unit, info) + end + end +end + + +-- Always get a UNIT_AURA when a unit's UnitIsVisible() changes +function lib:UNIT_AURA (unit) + local group = self.cache + local guid = UnitGUID (unit) + local info = guid and group[guid] + if info then + if not UnitIsUnit (unit, "player") then + if UnitIsVisible (unit) then + if info.not_visible then + info.not_visible = nil + --[===[@debug@ + debug (unit..", aka "..(UnitName(unit) or "nil")..", is now visible") --@end-debug@]===] + if not self.state.mainq[guid] then + self.state.staleq[guid] = 1 + self.frame:Show () + end + end + elseif UnitIsConnected (unit) then + --[===[@debug@ + if not info.not_visible then + debug (unit..", aka "..(UnitName(unit) or "nil")..", is no longer visible") + end + --@end-debug@]===] + info.not_visible = true + end + end + end +end + + +local dual_spec_spells = {} +for i, spellid in ipairs (TALENT_ACTIVATION_SPELLS) do dual_spec_spells[spellid] = true end +function lib:UNIT_SPELLCAST_SUCCEEDED (unit, spellname, rank, lineid, spellid) + if dual_spec_spells[spellid] then + self:Query (unit) -- Definitely changed, so high prio refresh + end +end + + +-- External library functions + +function lib:QueuedInspections () + local q = {} + for guid in pairs (self.state.mainq) do + table.insert (q, guid) + end + return q +end + + +function lib:StaleInspections () + local q = {} + for guid in pairs (self.state.staleq) do + table.insert (q, guid) + end + return q +end + + +function lib:GetCachedInfo (guid) + local group = self.cache + return guid and group[guid] +end + + +function lib:Rescan (guid) + local mainq, staleq = self.state.mainq, self.state.staleq + if guid then + local unit = self:GuidToUnit (guid) + if unit then + if UnitIsUnit (unit, "player") then + self.events:Fire (UPDATE_EVENT, guid, "player", self:BuildInfo ("player")) + elseif not mainq[guid] then + staleq[guid] = 1 + end + end + else + for i,unit in ipairs (self:GroupUnits ()) do + if UnitExists (unit) then + if UnitIsUnit (unit, "player") then + self.events:Fire (UPDATE_EVENT, UnitGUID("player"), "player", self:BuildInfo ("player")) + else + local guid = UnitGUID (unit) + if guid and not mainq[guid] then + staleq[guid] = 1 + end + end + end + end + end + self.frame:Show () -- Start timer if not already running + + -- Evict any stale entries + self:GROUP_ROSTER_UPDATE () +end + + +local unitstrings = { + raid = { "player" }, -- This seems to be needed under certain circumstances. Odd. + party = { "player" }, -- Player not part of partyN + player = { "player" } +} +for i = 1,40 do table.insert (unitstrings.raid, "raid"..i) end +for i = 1,4 do table.insert (unitstrings.party, "party"..i) end + + +-- Returns an array with the set of unit ids for the current group +function lib:GroupUnits () + local units + if IsInRaid () then + units = unitstrings.raid + elseif GetNumSubgroupMembers () > 0 then + units = unitstrings.party + else + units = unitstrings.player + end + return units +end + + +-- If demand-loaded, we need to synthesize a login event +if IsLoggedIn () then lib:PLAYER_LOGIN () end diff --git a/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc b/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc new file mode 100644 index 0000000..cc84c10 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/LibGroupInSpecT-1.1.toc @@ -0,0 +1,15 @@ +## Interface: 70000 +## Title: Lib: GroupInSpecT-1.1 +## Notes: Keeps track of group members and keeps an up-to-date cache of their specialization and talents. +## Version: r84 +## Author: Anyia of HordeYakka (Jubei'Thos) +## X-Category: Library +## X-Curse-Packaged-Version: r84 +## X-Curse-Project-Name: LibGroupInSpecT +## X-Curse-Project-ID: libgroupinspect +## X-Curse-Repository-ID: wow/libgroupinspect/mainline + +Libs\LibStub\LibStub.lua +Libs\CallbackHandler-1.0\CallbackHandler-1.0.lua + +lib.xml diff --git a/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua b/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua new file mode 100644 index 0000000..2a64013 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua @@ -0,0 +1,238 @@ +--[[ $Id: CallbackHandler-1.0.lua 18 2014-10-16 02:52:20Z mikk $ ]] +local MAJOR, MINOR = "CallbackHandler-1.0", 6 +local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR) + +if not CallbackHandler then return end -- No upgrade needed + +local meta = {__index = function(tbl, key) tbl[key] = {} return tbl[key] end} + +-- Lua APIs +local tconcat = table.concat +local assert, error, loadstring = assert, error, loadstring +local setmetatable, rawset, rawget = setmetatable, rawset, rawget +local next, select, pairs, type, tostring = next, select, pairs, type, tostring + +-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded +-- List them here for Mikk's FindGlobals script +-- GLOBALS: geterrorhandler + +local xpcall = xpcall + +local function errorhandler(err) + return geterrorhandler()(err) +end + +local function CreateDispatcher(argCount) + local code = [[ + local next, xpcall, eh = ... + + local method, ARGS + local function call() method(ARGS) end + + local function dispatch(handlers, ...) + local index + index, method = next(handlers) + if not method then return end + local OLD_ARGS = ARGS + ARGS = ... + repeat + xpcall(call, eh) + index, method = next(handlers, index) + until not method + ARGS = OLD_ARGS + end + + return dispatch + ]] + + local ARGS, OLD_ARGS = {}, {} + for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end + code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", ")) + return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler) +end + +local Dispatchers = setmetatable({}, {__index=function(self, argCount) + local dispatcher = CreateDispatcher(argCount) + rawset(self, argCount, dispatcher) + return dispatcher +end}) + +-------------------------------------------------------------------------- +-- CallbackHandler:New +-- +-- target - target object to embed public APIs in +-- RegisterName - name of the callback registration API, default "RegisterCallback" +-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback" +-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API. + +function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName) + + RegisterName = RegisterName or "RegisterCallback" + UnregisterName = UnregisterName or "UnregisterCallback" + if UnregisterAllName==nil then -- false is used to indicate "don't want this method" + UnregisterAllName = "UnregisterAllCallbacks" + end + + -- we declare all objects and exported APIs inside this closure to quickly gain access + -- to e.g. function names, the "target" parameter, etc + + + -- Create the registry object + local events = setmetatable({}, meta) + local registry = { recurse=0, events=events } + + -- registry:Fire() - fires the given event/message into the registry + function registry:Fire(eventname, ...) + if not rawget(events, eventname) or not next(events[eventname]) then return end + local oldrecurse = registry.recurse + registry.recurse = oldrecurse + 1 + + Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...) + + registry.recurse = oldrecurse + + if registry.insertQueue and oldrecurse==0 then + -- Something in one of our callbacks wanted to register more callbacks; they got queued + for eventname,callbacks in pairs(registry.insertQueue) do + local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. + for self,func in pairs(callbacks) do + events[eventname][self] = func + -- fire OnUsed callback? + if first and registry.OnUsed then + registry.OnUsed(registry, target, eventname) + first = nil + end + end + end + registry.insertQueue = nil + end + end + + -- Registration of a callback, handles: + -- self["method"], leads to self["method"](self, ...) + -- self with function ref, leads to functionref(...) + -- "addonId" (instead of self) with function ref, leads to functionref(...) + -- all with an optional arg, which, if present, gets passed as first argument (after self if present) + target[RegisterName] = function(self, eventname, method, ... --[[actually just a single arg]]) + if type(eventname) ~= "string" then + error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2) + end + + method = method or eventname + + local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten. + + if type(method) ~= "string" and type(method) ~= "function" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - string or function expected.", 2) + end + + local regfunc + + if type(method) == "string" then + -- self["method"] calling style + if type(self) ~= "table" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): self was not a table?", 2) + elseif self==target then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): do not use Library:"..RegisterName.."(), use your own 'self'", 2) + elseif type(self[method]) ~= "function" then + error("Usage: "..RegisterName.."(\"eventname\", \"methodname\"): 'methodname' - method '"..tostring(method).."' not found on self.", 2) + end + + if select("#",...)>=1 then -- this is not the same as testing for arg==nil! + local arg=select(1,...) + regfunc = function(...) self[method](self,arg,...) end + else + regfunc = function(...) self[method](self,...) end + end + else + -- function ref with self=object or self="addonId" or self=thread + if type(self)~="table" and type(self)~="string" and type(self)~="thread" then + error("Usage: "..RegisterName.."(self or \"addonId\", eventname, method): 'self or addonId': table or string or thread expected.", 2) + end + + if select("#",...)>=1 then -- this is not the same as testing for arg==nil! + local arg=select(1,...) + regfunc = function(...) method(arg,...) end + else + regfunc = method + end + end + + + if events[eventname][self] or registry.recurse<1 then + -- if registry.recurse<1 then + -- we're overwriting an existing entry, or not currently recursing. just set it. + events[eventname][self] = regfunc + -- fire OnUsed callback? + if registry.OnUsed and first then + registry.OnUsed(registry, target, eventname) + end + else + -- we're currently processing a callback in this registry, so delay the registration of this new entry! + -- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency + registry.insertQueue = registry.insertQueue or setmetatable({},meta) + registry.insertQueue[eventname][self] = regfunc + end + end + + -- Unregister a callback + target[UnregisterName] = function(self, eventname) + if not self or self==target then + error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2) + end + if type(eventname) ~= "string" then + error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2) + end + if rawget(events, eventname) and events[eventname][self] then + events[eventname][self] = nil + -- Fire OnUnused callback? + if registry.OnUnused and not next(events[eventname]) then + registry.OnUnused(registry, target, eventname) + end + end + if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then + registry.insertQueue[eventname][self] = nil + end + end + + -- OPTIONAL: Unregister all callbacks for given selfs/addonIds + if UnregisterAllName then + target[UnregisterAllName] = function(...) + if select("#",...)<1 then + error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or \"addonId\" to unregister events for.", 2) + end + if select("#",...)==1 and ...==target then + error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or \"addonId\"", 2) + end + + + for i=1,select("#",...) do + local self = select(i,...) + if registry.insertQueue then + for eventname, callbacks in pairs(registry.insertQueue) do + if callbacks[self] then + callbacks[self] = nil + end + end + end + for eventname, callbacks in pairs(events) do + if callbacks[self] then + callbacks[self] = nil + -- Fire OnUnused callback? + if registry.OnUnused and not next(callbacks) then + registry.OnUnused(registry, target, eventname) + end + end + end + end + end + end + + return registry +end + + +-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it +-- try to upgrade old implicit embeds since the system is selfcontained and +-- relies on closures to work. + diff --git a/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml b/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml new file mode 100644 index 0000000..876df83 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml @@ -0,0 +1,4 @@ +<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ +..\FrameXML\UI.xsd"> + <Script file="CallbackHandler-1.0.lua"/> +</Ui> \ No newline at end of file diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.lua b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.lua new file mode 100644 index 0000000..7e9b5cd --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.lua @@ -0,0 +1,51 @@ +-- $Id: LibStub.lua 103 2014-10-16 03:02:50Z mikk $ +-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/addons/libstub/ for more info +-- LibStub is hereby placed in the Public Domain +-- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke +local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! +local LibStub = _G[LIBSTUB_MAJOR] + +-- Check to see is this version of the stub is obsolete +if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + -- LibStub:NewLibrary(major, minor) + -- major (string) - the major version of the library + -- minor (string or number ) - the minor version of the library + -- + -- returns nil if a newer or same version of the lib is already present + -- returns empty library object or old library object if upgrade is needed + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + -- LibStub:GetLibrary(major, [silent]) + -- major (string) - the major version of the library + -- silent (boolean) - if true, library is optional, silently return nil if its not found + -- + -- throws an error if the library can not be found (except silent is set) + -- returns the library object if found + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + -- LibStub:IterateLibraries() + -- + -- Returns an iterator for the currently registered libraries + function LibStub:IterateLibraries() + return pairs(self.libs) + end + + setmetatable(LibStub, { __call = LibStub.GetLibrary }) +end diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.toc b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.toc new file mode 100644 index 0000000..b12ce87 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/LibStub.toc @@ -0,0 +1,13 @@ +## Interface: 60000 +## Title: Lib: LibStub +## Notes: Universal Library Stub +## Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel +## X-Website: http://www.wowace.com/addons/libstub/ +## X-Category: Library +## X-License: Public Domain +## X-Curse-Packaged-Version: r103 +## X-Curse-Project-Name: LibStub +## X-Curse-Project-ID: libstub +## X-Curse-Repository-ID: wow/libstub/mainline + +LibStub.lua diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test.lua b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test.lua new file mode 100644 index 0000000..276ddab --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test.lua @@ -0,0 +1,41 @@ +debugstack = debug.traceback +strmatch = string.match + +loadfile("../LibStub.lua")() + +local lib, oldMinor = LibStub:NewLibrary("Pants", 1) -- make a new thingy +assert(lib) -- should return the library table +assert(not oldMinor) -- should not return the old minor, since it didn't exist + +-- the following is to create data and then be able to check if the same data exists after the fact +function lib:MyMethod() +end +local MyMethod = lib.MyMethod +lib.MyTable = {} +local MyTable = lib.MyTable + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", 1) -- try to register a library with the same version, should silently fail +assert(not newLib) -- should not return since out of date + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", 0) -- try to register a library with a previous, should silently fail +assert(not newLib) -- should not return since out of date + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", 2) -- register a new version +assert(newLib) -- library table +assert(rawequal(newLib, lib)) -- should be the same reference as the previous +assert(newOldMinor == 1) -- should return the minor version of the previous version + +assert(rawequal(lib.MyMethod, MyMethod)) -- verify that values were saved +assert(rawequal(lib.MyTable, MyTable)) -- verify that values were saved + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 3 Blah") -- register a new version with a string minor version (instead of a number) +assert(newLib) -- library table +assert(newOldMinor == 2) -- previous version was 2 + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", "Blah 4 and please ignore 15 Blah") -- register a new version with a string minor version (instead of a number) +assert(newLib) +assert(newOldMinor == 3) -- previous version was 3 (even though it gave a string) + +local newLib, newOldMinor = LibStub:NewLibrary("Pants", 5) -- register a new library, using a normal number instead of a string +assert(newLib) +assert(newOldMinor == 4) -- previous version was 4 (even though it gave a string) \ No newline at end of file diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test2.lua b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test2.lua new file mode 100644 index 0000000..eae7172 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test2.lua @@ -0,0 +1,27 @@ +debugstack = debug.traceback +strmatch = string.match + +loadfile("../LibStub.lua")() + +for major, library in LibStub:IterateLibraries() do + -- check that MyLib doesn't exist yet, by iterating through all the libraries + assert(major ~= "MyLib") +end + +assert(not LibStub:GetLibrary("MyLib", true)) -- check that MyLib doesn't exist yet by direct checking +assert(not pcall(LibStub.GetLibrary, LibStub, "MyLib")) -- don't silently fail, thus it should raise an error. +local lib = LibStub:NewLibrary("MyLib", 1) -- create the lib +assert(lib) -- check it exists +assert(rawequal(LibStub:GetLibrary("MyLib"), lib)) -- verify that :GetLibrary("MyLib") properly equals the lib reference + +assert(LibStub:NewLibrary("MyLib", 2)) -- create a new version + +local count=0 +for major, library in LibStub:IterateLibraries() do + -- check that MyLib exists somewhere in the libraries, by iterating through all the libraries + if major == "MyLib" then -- we found it! + count = count +1 + assert(rawequal(library, lib)) -- verify that the references are equal + end +end +assert(count == 1) -- verify that we actually found it, and only once diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test3.lua b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test3.lua new file mode 100644 index 0000000..30f7b94 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test3.lua @@ -0,0 +1,14 @@ +debugstack = debug.traceback +strmatch = string.match + +loadfile("../LibStub.lua")() + +local proxy = newproxy() -- non-string + +assert(not pcall(LibStub.NewLibrary, LibStub, proxy, 1)) -- should error, proxy is not a string, it's userdata +local success, ret = pcall(LibStub.GetLibrary, proxy, true) +assert(not success or not ret) -- either error because proxy is not a string or because it's not actually registered. + +assert(not pcall(LibStub.NewLibrary, LibStub, "Something", "No number in here")) -- should error, minor has no string in it. + +assert(not LibStub:GetLibrary("Something", true)) -- shouldn't've created it from the above statement \ No newline at end of file diff --git a/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test4.lua b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test4.lua new file mode 100644 index 0000000..43eb338 --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/Libs/LibStub/tests/test4.lua @@ -0,0 +1,41 @@ +debugstack = debug.traceback +strmatch = string.match + +loadfile("../LibStub.lua")() + + +-- Pretend like loaded libstub is old and doesn't have :IterateLibraries +assert(LibStub.minor) +LibStub.minor = LibStub.minor - 0.0001 +LibStub.IterateLibraries = nil + +loadfile("../LibStub.lua")() + +assert(type(LibStub.IterateLibraries)=="function") + + +-- Now pretend that we're the same version -- :IterateLibraries should NOT be re-created +LibStub.IterateLibraries = 123 + +loadfile("../LibStub.lua")() + +assert(LibStub.IterateLibraries == 123) + + +-- Now pretend that a newer version is loaded -- :IterateLibraries should NOT be re-created +LibStub.minor = LibStub.minor + 0.0001 + +loadfile("../LibStub.lua")() + +assert(LibStub.IterateLibraries == 123) + + +-- Again with a huge number +LibStub.minor = LibStub.minor + 1234567890 + +loadfile("../LibStub.lua")() + +assert(LibStub.IterateLibraries == 123) + + +print("OK") \ No newline at end of file diff --git a/vendor/LibGroupInSpecT-1.1/lib.xml b/vendor/LibGroupInSpecT-1.1/lib.xml new file mode 100644 index 0000000..b292e7e --- /dev/null +++ b/vendor/LibGroupInSpecT-1.1/lib.xml @@ -0,0 +1,3 @@ +<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ ..\..\FrameXML\UI.xsd"> +<Script file="LibGroupInSpecT-1.1.lua"/> +</Ui> diff --git a/vendor/vendor_include.xml b/vendor/vendor_include.xml index c1fa7ae..0ecd64c 100644 --- a/vendor/vendor_include.xml +++ b/vendor/vendor_include.xml @@ -17,8 +17,16 @@ <Include file="Ace3\AceTimer-3.0\AceTimer-3.0.xml"/> <Include file="Ace3\AceConfig-3.0\AceConfig-3.0.xml"/> - <!-- http://www.wowace.com/addons/libitemupgradeinfo-1-0/ --> + <!-- + http://www.wowace.com/addons/libitemupgradeinfo-1-0/ + Provides information about item upgrades applied to items. + --> <Include file="LibItemUpgradeInfo-1.0\LibItemUpgradeInfo-1.0.xml" /> + <!-- + http://www.wowace.com/addons/libgroupinspect/ + Keeps track of group members specialization and talents. + --> + <Include file="LibGroupInSpecT-1.1\lib.xml" /> <!--@end-no-lib-strip@--> </Ui> \ No newline at end of file