diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index 9d63b14bb..0329417bb 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -152,8 +152,8 @@
..\packages\Colore.5.1.0\lib\net35\Corale.Colore.dllTrue
-
- ..\packages\CSCore.1.2.1.1\lib\net35-client\CSCore.dll
+
+ ..\packages\CSCore.1.2.1.2\lib\net35-client\CSCore.dll..\packages\CUE.NET.1.1.3.1\lib\net45\CUE.NET.dll
@@ -170,8 +170,8 @@
..\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dllTrue
-
- ..\packages\DynamicExpresso.Core.1.3.3.6\lib\net40\DynamicExpresso.Core.dll
+
+ ..\packages\DynamicExpresso.Core.1.3.4.7\lib\net40\DynamicExpresso.Core.dll..\packages\MouseKeyHook.5.4.0\lib\net40\Gma.System.MouseKeyHook.dll
@@ -267,7 +267,7 @@
True
- ..\packages\SpotifyAPI-NET.2.16.0\lib\SpotifyAPI.dll
+ ..\packages\SpotifyAPI-NET.2.16.1\lib\SpotifyAPI.dll..\packages\squirrel.windows.1.4.4\lib\Net45\Squirrel.dll
@@ -799,16 +799,17 @@
Code
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Designer
@@ -1087,12 +1088,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchView.xaml b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchView.xaml
index 7fa2840a1..60369248f 100644
--- a/Artemis/Artemis/Modules/Games/Overwatch/OverwatchView.xaml
+++ b/Artemis/Artemis/Modules/Games/Overwatch/OverwatchView.xaml
@@ -41,7 +41,7 @@
-
+
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Artemis.toc b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Artemis.toc
similarity index 94%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Artemis.toc
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Artemis.toc
index 145946e76..824da3279 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Artemis.toc
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Artemis.toc
@@ -1,9 +1,9 @@
-## Interface: 70300
-## Title: Artemis
-## Notes: Transmits ingame data to Artemis
-## Author: SpoinkyNL
-## Version: 1.0.0
-
-embeds.xml
-
-Core.lua
+## Interface: 70300
+## Title: Artemis
+## Notes: Transmits ingame data to Artemis
+## Author: SpoinkyNL
+## Version: 1.0.0
+
+embeds.xml
+
+Core.lua
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Core.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Core.lua
similarity index 96%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Core.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Core.lua
index 4a04339d8..52a6287e9 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Core.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Core.lua
@@ -1,398 +1,398 @@
-Artemis = LibStub("AceAddon-3.0"):NewAddon("Artemis", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0")
-json = LibStub("json")
-
--- Hook onto logout because it seems PLAYER_LOGOUT fires on reload as well
-local _Logout = Logout
-
-local debugging = false
-local lastLine = {}
-local channeling = {}
-local unitUpdates = {}
-local lastTransmitMessage
-local lastTransmitTime
-local lastBuffs = "";
-local lastDebuffs = "";
-local prefixCounts = {}
-
-channeling["player"] = false
-channeling["target"] = false
-
-function Artemis:OnEnable()
- -- Register all the various events that Artemis will want to know about
- Artemis:RegisterEvent("PLAYER_ENTERING_WORLD")
- Artemis:RegisterEvent("PLAYER_LEVEL_UP")
- Artemis:RegisterEvent("PLAYER_FLAGS_CHANGED")
- Artemis:RegisterEvent("ACHIEVEMENT_EARNED")
- Artemis:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
- Artemis:RegisterEvent("UNIT_TARGET")
- Artemis:RegisterEvent("UNIT_HEALTH")
- Artemis:RegisterEvent("UNIT_POWER")
- Artemis:RegisterEvent("UNIT_AURA")
- Artemis:RegisterEvent("UNIT_SPELLCAST_START")
- Artemis:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
- Artemis:RegisterEvent("UNIT_SPELLCAST_FAILED")
- Artemis:RegisterEvent("UNIT_SPELLCAST_DELAYED")
- Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
- Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
- Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
- Artemis:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
- Artemis:RegisterEvent("ZONE_CHANGED")
- Artemis:RegisterEvent("ZONE_CHANGED_NEW_AREA")
-
- -- Register the chat command /artemis
- Artemis:RegisterChatCommand("artemis", "HandleChatCommand")
-
- -- Create a loop that'll periodically send character data in case of an Artemis restart/late start
- Artemis:ScheduleRepeatingTimer("PeriodicUpdate", 30)
-end
-
-function Artemis:HandleChatCommand(input)
- if input == "debug" then
- debugging = not (debugging)
- if debugging then
- Artemis:Print("Debugging enabled.")
- else
- Artemis:Print("Debugging disabled.")
- end
- end
- if input == "rc" then
- prefixCounts = {}
- Artemis:Print("Reset the send counters.")
- end
- if input == nill or input == "" or input == "help" then
- Artemis:Print("Available chat commands:")
- Artemis:Printf("|cffb7b7b7/artemis debug|r: Toggle debugging")
- Artemis:Printf("|cffb7b7b7/artemis rc|r: Reset the debug counters")
- end
-end
-
-function Artemis:Transmit(prefix, data, prio)
- local msg = "artemis(".. prefix .. "|" .. json.encode(data) ..")"
- -- If the message is the same as the previous, make sure it wasn't sent less than 250ms ago
- if msg == lastTransmitMessage then
- if not (lastTransmitTime == nil) then
- local diff = GetTime() - lastTransmitTime;
- if (diff < 0.25) then
- return
- end
- end
- end
-
- lastTransmitTime = GetTime()
-
- if debugging == true then
- if prefixCounts[prefix] == nill then
- prefixCounts[prefix] = 0
- end
- prefixCounts[prefix] = prefixCounts[prefix] + 1
- end
-
- if debugging == true then
- Artemis:Printf("Transmitting with prefix |cfffdff71" .. prefix .. "|r (" .. prefixCounts[prefix] .. ").")
- Artemis:Print(msg)
- end
- Artemis:SendCommMessage("(artemis)", msg, "WHISPER", UnitName("player"), prio)
-end
-
-function Artemis:TransmitUnitState(unit, ignoreThrottle)
- if not ignoreThrottle then
- if not (unitUpdates[unit] == nil) then
- local diff = GetTime() - unitUpdates[unit]
- if (diff < 0.5) then
- return
- end
- end
- end
-
- local table = {
- h = UnitHealth(unit),
- mh = UnitHealthMax(unit),
- p = UnitPower(unit),
- mp = UnitPowerMax(unit),
- t = UnitPowerType(unit)
- };
-
- unitUpdates[unit] = GetTime()
- Artemis:Transmit(unit .. "State", table)
-end
-
-function Artemis:GetUnitDetails(unit)
- return {
- n = UnitName(unit),
- c = UnitClass(unit),
- l = UnitLevel(unit),
- r = UnitRace(unit),
- g = UnitSex(unit),
- f = UnitFactionGroup(unit)
- };
-end
-
-function Artemis:GetPlayerDetails()
- local details = Artemis:GetUnitDetails("player")
- local id, name, _, _, role = GetSpecializationInfo(GetSpecialization())
-
- details.realm = GetRealmName()
- details.achievementPoints = GetTotalAchievementPoints(false)
- details.s = {id = id, n = name, r = role}
-
- return details
-end
-
-function Artemis:GetUnitAuras(unit, filter)
- local auras = {};
- for index = 1, 40 do
- local name, _, _, count, _, duration, expires, caster, _, _, spellID = UnitAura(unit, index, filter);
- if not (name == nil) then
- local buffTable = {n = name, id = spellID}
- -- Leave these values out if they are 0 to save some space
- if count > 0 then
- buffTable["c"] = count
- end
- if duration > 0 then
- buffTable["d"] = duration
- end
- if expires > 0 then
- buffTable["e"] = expires
- end
- table.insert(auras, buffTable)
- end
- end
- return auras
-end
-
-function Artemis:PeriodicUpdate()
- -- Don't do this in combat, enough data going out at that time already
- if InCombatLockdown() then
- return
- end
- Artemis:Transmit("player", Artemis:GetPlayerDetails())
- Artemis:TransmitUnitState("player", true);
-end
-
-function Artemis:PLAYER_ENTERING_WORLD(...)
- Artemis:Transmit("player", Artemis:GetPlayerDetails())
- Artemis:TransmitUnitState("player", true);
-end
-
-function Logout()
- Artemis:Transmit("gameState", "loggedOut")
- return _Logout()
-end
-
-function Artemis:PLAYER_LEVEL_UP(...)
- Artemis:Transmit("player", Artemis:GetPlayerDetails())
-end
-
-function Artemis:PLAYER_FLAGS_CHANGED(...)
- local _, unitID = ...
- if unitID == "player" then
- -- AFK overwrites DND
- if UnitIsAFK("player") then
- Artemis:Transmit("gameState", "afk")
- return
- end
- if UnitIsDND("player") then
- Artemis:Transmit("gameState", "dnd")
- return
- end
- Artemis:Transmit("gameState", "ingame")
- end
-end
-
-function Artemis:ACHIEVEMENT_EARNED(...)
- Artemis:Transmit("player", Artemis:GetPlayerDetails())
-end
-
-function Artemis:ACTIVE_TALENT_GROUP_CHANGED(...)
- Artemis:Transmit("player", Artemis:GetPlayerDetails())
-end
-
-function Artemis:UNIT_TARGET(...)
- local _, source = ...
- if not (source == "player") then
- return
- end
-
- local details = Artemis:GetUnitDetails("target")
- channeling["target"] = false
-
- Artemis:Transmit("target", details)
- Artemis:TransmitUnitState("target", true);
-end
-
-function Artemis:UNIT_HEALTH(...)
- local _, source = ...
- if not (source == "player") and not (source == "target") then
- return
- end
-
- Artemis:TransmitUnitState(source, false);
-end
-
-function Artemis:UNIT_POWER(...)
- local _, source = ...
- if not (source == "player") and not (source == "target") then
- return
- end
-
- Artemis:TransmitUnitState(source, false);
-end
-
-function Artemis:UNIT_AURA(...)
- local _, source = ...
- if not (source == "player") then
- return
- end
-
- local buffs = Artemis:GetUnitAuras(source, "PLAYER|HELPFUL")
- local debuffs = Artemis:GetUnitAuras(source, "PLAYER|HARMFUL")
-
- local newBuffs = json.encode(buffs)
- local newDebuffs = json.encode(debuffs)
-
- if not (lastBuffs == newBuffs) then
- Artemis:Transmit("buffs", buffs)
- end
- if not (lastDebuffs == newDebuffs) then
- Artemis:Transmit("debuffs", debuffs)
- end
-
- lastBuffs = newBuffs
- lastDebuffs = newDebuffs
-end
-
--- Detect non-instant spell casts
-function Artemis:UNIT_SPELLCAST_START(...)
- local _, unitID, spell, rank, lineID, spellID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
-
- local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
- local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
- lastLine[unitID] = lineID
-
- Artemis:Transmit("spellCast", table, "ALERT")
-end
-
--- Detect instant spell casts
-function Artemis:UNIT_SPELLCAST_SUCCEEDED (...)
- local _, unitID, spell, rank, lineID, spellID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- if channeling[unitID] == true then
- return
- end
- -- Many spells are irrelevant system spells, don't transmit these
- if unitID == "player" and not (IsPlayerSpell(spellID)) then
- return
- end
-
- local name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(unitID)
- -- Don't trigger on the success of a non instant cast
- if not (lastLine[unitID] == nil) and lastLine[unitID] == lineID then
- return
- end
-
- -- Set back the last line to what is currently being cast (Fireblast during Fireball per example)
- if not (name == nil) then
- lastLine[unitID] = castID
- else
- lastLine[unitID] = nil
- end
-
- local table = {uid = unitID, n = spell, sid = spellID}
-
- Artemis:Transmit("instantSpellCast", table, "ALERT")
-end
-
--- Detect falure of non instant casts
-function Artemis:UNIT_SPELLCAST_FAILED (...)
- local source, unitID, _, _, lineID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
- return
- end
-
- lastLine[unitID] = nil
-
- Artemis:Transmit("spellCastFailed", unitID, "ALERT")
-end
-
--- Detect falure of non instant casts
-function Artemis:UNIT_SPELLCAST_DELAYED (...)
- local _, unitID, spell, rank, lineID, spellID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
- local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
-
- Artemis:Transmit("spellCast", table, "ALERT")
-end
-
--- Detect cancellation of non instant casts
-function Artemis:UNIT_SPELLCAST_INTERRUPTED (...)
- local source, unitID, _, _, lineID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
- return
- end
-
- lastLine[unitID] = nil
-
- Artemis:Transmit("spellCastInterrupted", unitID, "ALERT")
-end
-
--- Detect spell channels
-function Artemis:UNIT_SPELLCAST_CHANNEL_START(...)
- local _, unitID, spell, rank, lineID, spellID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- channeling[unitID] = true
-
- local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
- local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
-
- Artemis:Transmit("spellChannel", table, "ALERT")
-end
-
-function Artemis:UNIT_SPELLCAST_CHANNEL_UPDATE (...)
- local _, unitID, spell, rank, lineID, spellID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
- local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
- local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
-
- Artemis:Transmit("spellChannel", table, "ALERT")
-end
-
--- Detect cancellation of channels
-function Artemis:UNIT_SPELLCAST_CHANNEL_STOP (...)
- local source, unitID, _, _, lineID = ...
- if not (unitID == "player") and not (unitID == "target") then
- return
- end
-
- channeling[unitID] = false
-
- Artemis:Transmit("spellChannelInterrupted", unitID, "ALERT")
-end
-
-function Artemis:ZONE_CHANGED_NEW_AREA (...)
- local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
-
- Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
-end
-function Artemis:ZONE_CHANGED (...)
- local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
-
- Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
+Artemis = LibStub("AceAddon-3.0"):NewAddon("Artemis", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0")
+json = LibStub("json")
+
+-- Hook onto logout because it seems PLAYER_LOGOUT fires on reload as well
+local _Logout = Logout
+
+local debugging = false
+local lastLine = {}
+local channeling = {}
+local unitUpdates = {}
+local lastTransmitMessage
+local lastTransmitTime
+local lastBuffs = "";
+local lastDebuffs = "";
+local prefixCounts = {}
+
+channeling["player"] = false
+channeling["target"] = false
+
+function Artemis:OnEnable()
+ -- Register all the various events that Artemis will want to know about
+ Artemis:RegisterEvent("PLAYER_ENTERING_WORLD")
+ Artemis:RegisterEvent("PLAYER_LEVEL_UP")
+ Artemis:RegisterEvent("PLAYER_FLAGS_CHANGED")
+ Artemis:RegisterEvent("ACHIEVEMENT_EARNED")
+ Artemis:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
+ Artemis:RegisterEvent("UNIT_TARGET")
+ Artemis:RegisterEvent("UNIT_HEALTH")
+ Artemis:RegisterEvent("UNIT_POWER")
+ Artemis:RegisterEvent("UNIT_AURA")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_START")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_FAILED")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_DELAYED")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
+ Artemis:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
+ Artemis:RegisterEvent("ZONE_CHANGED")
+ Artemis:RegisterEvent("ZONE_CHANGED_NEW_AREA")
+
+ -- Register the chat command /artemis
+ Artemis:RegisterChatCommand("artemis", "HandleChatCommand")
+
+ -- Create a loop that'll periodically send character data in case of an Artemis restart/late start
+ Artemis:ScheduleRepeatingTimer("PeriodicUpdate", 30)
+end
+
+function Artemis:HandleChatCommand(input)
+ if input == "debug" then
+ debugging = not (debugging)
+ if debugging then
+ Artemis:Print("Debugging enabled.")
+ else
+ Artemis:Print("Debugging disabled.")
+ end
+ end
+ if input == "rc" then
+ prefixCounts = {}
+ Artemis:Print("Reset the send counters.")
+ end
+ if input == nill or input == "" or input == "help" then
+ Artemis:Print("Available chat commands:")
+ Artemis:Printf("|cffb7b7b7/artemis debug|r: Toggle debugging")
+ Artemis:Printf("|cffb7b7b7/artemis rc|r: Reset the debug counters")
+ end
+end
+
+function Artemis:Transmit(prefix, data, prio)
+ local msg = "artemis(".. prefix .. "|" .. json.encode(data) ..")"
+ -- If the message is the same as the previous, make sure it wasn't sent less than 250ms ago
+ if msg == lastTransmitMessage then
+ if not (lastTransmitTime == nil) then
+ local diff = GetTime() - lastTransmitTime;
+ if (diff < 0.25) then
+ return
+ end
+ end
+ end
+
+ lastTransmitTime = GetTime()
+
+ if debugging == true then
+ if prefixCounts[prefix] == nill then
+ prefixCounts[prefix] = 0
+ end
+ prefixCounts[prefix] = prefixCounts[prefix] + 1
+ end
+
+ if debugging == true then
+ Artemis:Printf("Transmitting with prefix |cfffdff71" .. prefix .. "|r (" .. prefixCounts[prefix] .. ").")
+ Artemis:Print(msg)
+ end
+ Artemis:SendCommMessage("(artemis)", msg, "WHISPER", UnitName("player"), prio)
+end
+
+function Artemis:TransmitUnitState(unit, ignoreThrottle)
+ if not ignoreThrottle then
+ if not (unitUpdates[unit] == nil) then
+ local diff = GetTime() - unitUpdates[unit]
+ if (diff < 0.5) then
+ return
+ end
+ end
+ end
+
+ local table = {
+ h = UnitHealth(unit),
+ mh = UnitHealthMax(unit),
+ p = UnitPower(unit),
+ mp = UnitPowerMax(unit),
+ t = UnitPowerType(unit)
+ };
+
+ unitUpdates[unit] = GetTime()
+ Artemis:Transmit(unit .. "State", table)
+end
+
+function Artemis:GetUnitDetails(unit)
+ return {
+ n = UnitName(unit),
+ c = UnitClass(unit),
+ l = UnitLevel(unit),
+ r = UnitRace(unit),
+ g = UnitSex(unit),
+ f = UnitFactionGroup(unit)
+ };
+end
+
+function Artemis:GetPlayerDetails()
+ local details = Artemis:GetUnitDetails("player")
+ local id, name, _, _, role = GetSpecializationInfo(GetSpecialization())
+
+ details.realm = GetRealmName()
+ details.achievementPoints = GetTotalAchievementPoints(false)
+ details.s = {id = id, n = name, r = role}
+
+ return details
+end
+
+function Artemis:GetUnitAuras(unit, filter)
+ local auras = {};
+ for index = 1, 40 do
+ local name, _, _, count, _, duration, expires, caster, _, _, spellID = UnitAura(unit, index, filter);
+ if not (name == nil) then
+ local buffTable = {n = name, id = spellID}
+ -- Leave these values out if they are 0 to save some space
+ if count > 0 then
+ buffTable["c"] = count
+ end
+ if duration > 0 then
+ buffTable["d"] = duration
+ end
+ if expires > 0 then
+ buffTable["e"] = expires
+ end
+ table.insert(auras, buffTable)
+ end
+ end
+ return auras
+end
+
+function Artemis:PeriodicUpdate()
+ -- Don't do this in combat, enough data going out at that time already
+ if InCombatLockdown() then
+ return
+ end
+ Artemis:Transmit("player", Artemis:GetPlayerDetails())
+ Artemis:TransmitUnitState("player", true);
+end
+
+function Artemis:PLAYER_ENTERING_WORLD(...)
+ Artemis:Transmit("player", Artemis:GetPlayerDetails())
+ Artemis:TransmitUnitState("player", true);
+end
+
+function Logout()
+ Artemis:Transmit("gameState", "loggedOut")
+ return _Logout()
+end
+
+function Artemis:PLAYER_LEVEL_UP(...)
+ Artemis:Transmit("player", Artemis:GetPlayerDetails())
+end
+
+function Artemis:PLAYER_FLAGS_CHANGED(...)
+ local _, unitID = ...
+ if unitID == "player" then
+ -- AFK overwrites DND
+ if UnitIsAFK("player") then
+ Artemis:Transmit("gameState", "afk")
+ return
+ end
+ if UnitIsDND("player") then
+ Artemis:Transmit("gameState", "dnd")
+ return
+ end
+ Artemis:Transmit("gameState", "ingame")
+ end
+end
+
+function Artemis:ACHIEVEMENT_EARNED(...)
+ Artemis:Transmit("player", Artemis:GetPlayerDetails())
+end
+
+function Artemis:ACTIVE_TALENT_GROUP_CHANGED(...)
+ Artemis:Transmit("player", Artemis:GetPlayerDetails())
+end
+
+function Artemis:UNIT_TARGET(...)
+ local _, source = ...
+ if not (source == "player") then
+ return
+ end
+
+ local details = Artemis:GetUnitDetails("target")
+ channeling["target"] = false
+
+ Artemis:Transmit("target", details)
+ Artemis:TransmitUnitState("target", true);
+end
+
+function Artemis:UNIT_HEALTH(...)
+ local _, source = ...
+ if not (source == "player") and not (source == "target") then
+ return
+ end
+
+ Artemis:TransmitUnitState(source, false);
+end
+
+function Artemis:UNIT_POWER(...)
+ local _, source = ...
+ if not (source == "player") and not (source == "target") then
+ return
+ end
+
+ Artemis:TransmitUnitState(source, false);
+end
+
+function Artemis:UNIT_AURA(...)
+ local _, source = ...
+ if not (source == "player") then
+ return
+ end
+
+ local buffs = Artemis:GetUnitAuras(source, "PLAYER|HELPFUL")
+ local debuffs = Artemis:GetUnitAuras(source, "PLAYER|HARMFUL")
+
+ local newBuffs = json.encode(buffs)
+ local newDebuffs = json.encode(debuffs)
+
+ if not (lastBuffs == newBuffs) then
+ Artemis:Transmit("buffs", buffs)
+ end
+ if not (lastDebuffs == newDebuffs) then
+ Artemis:Transmit("debuffs", debuffs)
+ end
+
+ lastBuffs = newBuffs
+ lastDebuffs = newDebuffs
+end
+
+-- Detect non-instant spell casts
+function Artemis:UNIT_SPELLCAST_START(...)
+ local _, unitID, spell, rank, lineID, spellID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+
+ local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
+ local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
+ lastLine[unitID] = lineID
+
+ Artemis:Transmit("spellCast", table, "ALERT")
+end
+
+-- Detect instant spell casts
+function Artemis:UNIT_SPELLCAST_SUCCEEDED (...)
+ local _, unitID, spell, rank, lineID, spellID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ if channeling[unitID] == true then
+ return
+ end
+ -- Many spells are irrelevant system spells, don't transmit these
+ if unitID == "player" and not (IsPlayerSpell(spellID)) then
+ return
+ end
+
+ local name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(unitID)
+ -- Don't trigger on the success of a non instant cast
+ if not (lastLine[unitID] == nil) and lastLine[unitID] == lineID then
+ return
+ end
+
+ -- Set back the last line to what is currently being cast (Fireblast during Fireball per example)
+ if not (name == nil) then
+ lastLine[unitID] = castID
+ else
+ lastLine[unitID] = nil
+ end
+
+ local table = {uid = unitID, n = spell, sid = spellID}
+
+ Artemis:Transmit("instantSpellCast", table, "ALERT")
+end
+
+-- Detect falure of non instant casts
+function Artemis:UNIT_SPELLCAST_FAILED (...)
+ local source, unitID, _, _, lineID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
+ return
+ end
+
+ lastLine[unitID] = nil
+
+ Artemis:Transmit("spellCastFailed", unitID, "ALERT")
+end
+
+-- Detect falure of non instant casts
+function Artemis:UNIT_SPELLCAST_DELAYED (...)
+ local _, unitID, spell, rank, lineID, spellID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ local name, _, _, _, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unitID)
+ local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
+
+ Artemis:Transmit("spellCast", table, "ALERT")
+end
+
+-- Detect cancellation of non instant casts
+function Artemis:UNIT_SPELLCAST_INTERRUPTED (...)
+ local source, unitID, _, _, lineID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ if lastLine[unitID] == nil or not (lastLine[unitID] == lineID) then
+ return
+ end
+
+ lastLine[unitID] = nil
+
+ Artemis:Transmit("spellCastInterrupted", unitID, "ALERT")
+end
+
+-- Detect spell channels
+function Artemis:UNIT_SPELLCAST_CHANNEL_START(...)
+ local _, unitID, spell, rank, lineID, spellID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ channeling[unitID] = true
+
+ local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
+ local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
+
+ Artemis:Transmit("spellChannel", table, "ALERT")
+end
+
+function Artemis:UNIT_SPELLCAST_CHANNEL_UPDATE (...)
+ local _, unitID, spell, rank, lineID, spellID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+ local name, _, _, _, startTime, endTime, _, notInterruptible = UnitChannelInfo(unitID)
+ local table = {uid = unitID, n = name, sid = spellID, s = startTime, e = endTime, ni = notInterruptible}
+
+ Artemis:Transmit("spellChannel", table, "ALERT")
+end
+
+-- Detect cancellation of channels
+function Artemis:UNIT_SPELLCAST_CHANNEL_STOP (...)
+ local source, unitID, _, _, lineID = ...
+ if not (unitID == "player") and not (unitID == "target") then
+ return
+ end
+
+ channeling[unitID] = false
+
+ Artemis:Transmit("spellChannelInterrupted", unitID, "ALERT")
+end
+
+function Artemis:ZONE_CHANGED_NEW_AREA (...)
+ local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
+
+ Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
+end
+function Artemis:ZONE_CHANGED (...)
+ local pvpType, isSubZonePVP, factionName = GetZonePVPInfo()
+
+ Artemis:Transmit("zone", {z = GetRealZoneText(), s = GetSubZoneText(), t = pvpType, p = isSubZonePVP, f = factionName})
end
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.lua
index b338695fa..a7f7279cc 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.lua
@@ -1,674 +1,674 @@
---- **AceAddon-3.0** provides a template for creating addon objects.
--- It'll provide you with a set of callback functions that allow you to simplify the loading
--- process of your addon.\\
--- Callbacks provided are:\\
--- * **OnInitialize**, which is called directly after the addon is fully loaded.
--- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
--- * **OnDisable**, which is only called when your addon is manually being disabled.
--- @usage
--- -- A small (but complete) addon, that doesn't do anything,
--- -- but shows usage of the callbacks.
--- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
---
--- function MyAddon:OnInitialize()
--- -- do init tasks here, like loading the Saved Variables,
--- -- or setting up slash commands.
--- end
---
--- function MyAddon:OnEnable()
--- -- Do more initialization here, that really enables the use of your addon.
--- -- Register Events, Hook functions, Create Frames, Get information from
--- -- the game that wasn't available in OnInitialize
--- end
---
--- function MyAddon:OnDisable()
--- -- Unhook, Unregister Events, Hide frames that you created.
--- -- You would probably only use an OnDisable if you want to
--- -- build a "standby" mode, or be able to toggle modules on/off.
--- end
--- @class file
--- @name AceAddon-3.0.lua
--- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
-
-local MAJOR, MINOR = "AceAddon-3.0", 12
-local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not AceAddon then return end -- No Upgrade needed.
-
-AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
-AceAddon.addons = AceAddon.addons or {} -- addons in general
-AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
-AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
-AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
-AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
-
--- Lua APIs
-local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
-local fmt, tostring = string.format, tostring
-local select, pairs, next, type, unpack = select, pairs, next, type, unpack
-local loadstring, assert, error = loadstring, assert, error
-local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
-
--- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
--- List them here for Mikk's FindGlobals script
--- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
-
---[[
- xpcall safecall implementation
-]]
-local xpcall = xpcall
-
-local function errorhandler(err)
- return geterrorhandler()(err)
-end
-
-local function CreateDispatcher(argCount)
- local code = [[
- local xpcall, eh = ...
- local method, ARGS
- local function call() return method(ARGS) end
-
- local function dispatch(func, ...)
- method = func
- if not method then return end
- ARGS = ...
- return xpcall(call, eh)
- end
-
- return dispatch
- ]]
-
- local ARGS = {}
- for i = 1, argCount do ARGS[i] = "arg"..i end
- code = code:gsub("ARGS", tconcat(ARGS, ", "))
- return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
-end
-
-local Dispatchers = setmetatable({}, {__index=function(self, argCount)
- local dispatcher = CreateDispatcher(argCount)
- rawset(self, argCount, dispatcher)
- return dispatcher
-end})
-Dispatchers[0] = function(func)
- return xpcall(func, errorhandler)
-end
-
-local function safecall(func, ...)
- -- we check to see if the func is passed is actually a function here and don't error when it isn't
- -- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
- -- present execution should continue without hinderance
- if type(func) == "function" then
- return Dispatchers[select('#', ...)](func, ...)
- end
-end
-
--- local functions that will be implemented further down
-local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
-
--- used in the addon metatable
-local function addontostring( self ) return self.name end
-
--- Check if the addon is queued for initialization
-local function queuedForInitialization(addon)
- for i = 1, #AceAddon.initializequeue do
- if AceAddon.initializequeue[i] == addon then
- return true
- end
- end
- return false
-end
-
---- Create a new AceAddon-3.0 addon.
--- Any libraries you specified will be embeded, and the addon will be scheduled for
--- its OnInitialize and OnEnable callbacks.
--- The final addon object, with all libraries embeded, will be returned.
--- @paramsig [object ,]name[, lib, ...]
--- @param object Table to use as a base for the addon (optional)
--- @param name Name of the addon object to create
--- @param lib List of libraries to embed into the addon
--- @usage
--- -- Create a simple addon object
--- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
---
--- -- Create a Addon object based on the table of a frame
--- local MyFrame = CreateFrame("Frame")
--- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
-function AceAddon:NewAddon(objectorname, ...)
- local object,name
- local i=1
- if type(objectorname)=="table" then
- object=objectorname
- name=...
- i=2
- else
- name=objectorname
- end
- if type(name)~="string" then
- error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
- end
- if self.addons[name] then
- error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
- end
-
- object = object or {}
- object.name = name
-
- local addonmeta = {}
- local oldmeta = getmetatable(object)
- if oldmeta then
- for k, v in pairs(oldmeta) do addonmeta[k] = v end
- end
- addonmeta.__tostring = addontostring
-
- setmetatable( object, addonmeta )
- self.addons[name] = object
- object.modules = {}
- object.orderedModules = {}
- object.defaultModuleLibraries = {}
- Embed( object ) -- embed NewModule, GetModule methods
- self:EmbedLibraries(object, select(i,...))
-
- -- add to queue of addons to be initialized upon ADDON_LOADED
- tinsert(self.initializequeue, object)
- return object
-end
-
-
---- Get the addon object by its name from the internal AceAddon registry.
--- Throws an error if the addon object cannot be found (except if silent is set).
--- @param name unique name of the addon object
--- @param silent if true, the addon is optional, silently return nil if its not found
--- @usage
--- -- Get the Addon
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
-function AceAddon:GetAddon(name, silent)
- if not silent and not self.addons[name] then
- error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
- end
- return self.addons[name]
-end
-
--- - Embed a list of libraries into the specified addon.
--- This function will try to embed all of the listed libraries into the addon
--- and error if a single one fails.
---
--- **Note:** This function is for internal use by :NewAddon/:NewModule
--- @paramsig addon, [lib, ...]
--- @param addon addon object to embed the libs in
--- @param lib List of libraries to embed into the addon
-function AceAddon:EmbedLibraries(addon, ...)
- for i=1,select("#", ... ) do
- local libname = select(i, ...)
- self:EmbedLibrary(addon, libname, false, 4)
- end
-end
-
--- - Embed a library into the addon object.
--- This function will check if the specified library is registered with LibStub
--- and if it has a :Embed function to call. It'll error if any of those conditions
--- fails.
---
--- **Note:** This function is for internal use by :EmbedLibraries
--- @paramsig addon, libname[, silent[, offset]]
--- @param addon addon object to embed the library in
--- @param libname name of the library to embed
--- @param silent marks an embed to fail silently if the library doesn't exist (optional)
--- @param offset will push the error messages back to said offset, defaults to 2 (optional)
-function AceAddon:EmbedLibrary(addon, libname, silent, offset)
- local lib = LibStub:GetLibrary(libname, true)
- if not lib and not silent then
- error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
- elseif lib and type(lib.Embed) == "function" then
- lib:Embed(addon)
- tinsert(self.embeds[addon], libname)
- return true
- elseif lib then
- error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
- end
-end
-
---- Return the specified module from an addon object.
--- Throws an error if the addon object cannot be found (except if silent is set)
--- @name //addon//:GetModule
--- @paramsig name[, silent]
--- @param name unique name of the module
--- @param silent if true, the module is optional, silently return nil if its not found (optional)
--- @usage
--- -- Get the Addon
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- -- Get the Module
--- MyModule = MyAddon:GetModule("MyModule")
-function GetModule(self, name, silent)
- if not self.modules[name] and not silent then
- error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
- end
- return self.modules[name]
-end
-
-local function IsModuleTrue(self) return true end
-
---- Create a new module for the addon.
--- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
--- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
--- an addon object.
--- @name //addon//:NewModule
--- @paramsig name[, prototype|lib[, lib, ...]]
--- @param name unique name of the module
--- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
--- @param lib List of libraries to embed into the addon
--- @usage
--- -- Create a module with some embeded libraries
--- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
---
--- -- Create a module with a prototype
--- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
--- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
-function NewModule(self, name, prototype, ...)
- if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
- if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
-
- if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
-
- -- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
- -- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
- local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
-
- module.IsModule = IsModuleTrue
- module:SetEnabledState(self.defaultModuleState)
- module.moduleName = name
-
- if type(prototype) == "string" then
- AceAddon:EmbedLibraries(module, prototype, ...)
- else
- AceAddon:EmbedLibraries(module, ...)
- end
- AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
-
- if not prototype or type(prototype) == "string" then
- prototype = self.defaultModulePrototype or nil
- end
-
- if type(prototype) == "table" then
- local mt = getmetatable(module)
- mt.__index = prototype
- setmetatable(module, mt) -- More of a Base class type feel.
- end
-
- safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
- self.modules[name] = module
- tinsert(self.orderedModules, module)
-
- return module
-end
-
---- Returns the real name of the addon or module, without any prefix.
--- @name //addon//:GetName
--- @paramsig
--- @usage
--- print(MyAddon:GetName())
--- -- prints "MyAddon"
-function GetName(self)
- return self.moduleName or self.name
-end
-
---- Enables the Addon, if possible, return true or false depending on success.
--- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
--- and enabling all modules of the addon (unless explicitly disabled).\\
--- :Enable() also sets the internal `enableState` variable to true
--- @name //addon//:Enable
--- @paramsig
--- @usage
--- -- Enable MyModule
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyModule = MyAddon:GetModule("MyModule")
--- MyModule:Enable()
-function Enable(self)
- self:SetEnabledState(true)
-
- -- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still
- -- it'll be enabled after the init process
- if not queuedForInitialization(self) then
- return AceAddon:EnableAddon(self)
- end
-end
-
---- Disables the Addon, if possible, return true or false depending on success.
--- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
--- and disabling all modules of the addon.\\
--- :Disable() also sets the internal `enableState` variable to false
--- @name //addon//:Disable
--- @paramsig
--- @usage
--- -- Disable MyAddon
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyAddon:Disable()
-function Disable(self)
- self:SetEnabledState(false)
- return AceAddon:DisableAddon(self)
-end
-
---- Enables the Module, if possible, return true or false depending on success.
--- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
--- @name //addon//:EnableModule
--- @paramsig name
--- @usage
--- -- Enable MyModule using :GetModule
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyModule = MyAddon:GetModule("MyModule")
--- MyModule:Enable()
---
--- -- Enable MyModule using the short-hand
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyAddon:EnableModule("MyModule")
-function EnableModule(self, name)
- local module = self:GetModule( name )
- return module:Enable()
-end
-
---- Disables the Module, if possible, return true or false depending on success.
--- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
--- @name //addon//:DisableModule
--- @paramsig name
--- @usage
--- -- Disable MyModule using :GetModule
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyModule = MyAddon:GetModule("MyModule")
--- MyModule:Disable()
---
--- -- Disable MyModule using the short-hand
--- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
--- MyAddon:DisableModule("MyModule")
-function DisableModule(self, name)
- local module = self:GetModule( name )
- return module:Disable()
-end
-
---- Set the default libraries to be mixed into all modules created by this object.
--- Note that you can only change the default module libraries before any module is created.
--- @name //addon//:SetDefaultModuleLibraries
--- @paramsig lib[, lib, ...]
--- @param lib List of libraries to embed into the addon
--- @usage
--- -- Create the addon object
--- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
--- -- Configure default libraries for modules (all modules need AceEvent-3.0)
--- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
--- -- Create a module
--- MyModule = MyAddon:NewModule("MyModule")
-function SetDefaultModuleLibraries(self, ...)
- if next(self.modules) then
- error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
- end
- self.defaultModuleLibraries = {...}
-end
-
---- Set the default state in which new modules are being created.
--- Note that you can only change the default state before any module is created.
--- @name //addon//:SetDefaultModuleState
--- @paramsig state
--- @param state Default state for new modules, true for enabled, false for disabled
--- @usage
--- -- Create the addon object
--- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
--- -- Set the default state to "disabled"
--- MyAddon:SetDefaultModuleState(false)
--- -- Create a module and explicilty enable it
--- MyModule = MyAddon:NewModule("MyModule")
--- MyModule:Enable()
-function SetDefaultModuleState(self, state)
- if next(self.modules) then
- error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
- end
- self.defaultModuleState = state
-end
-
---- Set the default prototype to use for new modules on creation.
--- Note that you can only change the default prototype before any module is created.
--- @name //addon//:SetDefaultModulePrototype
--- @paramsig prototype
--- @param prototype Default prototype for the new modules (table)
--- @usage
--- -- Define a prototype
--- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
--- -- Set the default prototype
--- MyAddon:SetDefaultModulePrototype(prototype)
--- -- Create a module and explicitly Enable it
--- MyModule = MyAddon:NewModule("MyModule")
--- MyModule:Enable()
--- -- should print "OnEnable called!" now
--- @see NewModule
-function SetDefaultModulePrototype(self, prototype)
- if next(self.modules) then
- error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
- end
- if type(prototype) ~= "table" then
- error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
- end
- self.defaultModulePrototype = prototype
-end
-
---- Set the state of an addon or module
--- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
--- @name //addon//:SetEnabledState
--- @paramsig state
--- @param state the state of an addon or module (enabled=true, disabled=false)
-function SetEnabledState(self, state)
- self.enabledState = state
-end
-
-
---- Return an iterator of all modules associated to the addon.
--- @name //addon//:IterateModules
--- @paramsig
--- @usage
--- -- Enable all modules
--- for name, module in MyAddon:IterateModules() do
--- module:Enable()
--- end
-local function IterateModules(self) return pairs(self.modules) end
-
--- Returns an iterator of all embeds in the addon
--- @name //addon//:IterateEmbeds
--- @paramsig
-local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
-
---- Query the enabledState of an addon.
--- @name //addon//:IsEnabled
--- @paramsig
--- @usage
--- if MyAddon:IsEnabled() then
--- MyAddon:Disable()
--- end
-local function IsEnabled(self) return self.enabledState end
-local mixins = {
- NewModule = NewModule,
- GetModule = GetModule,
- Enable = Enable,
- Disable = Disable,
- EnableModule = EnableModule,
- DisableModule = DisableModule,
- IsEnabled = IsEnabled,
- SetDefaultModuleLibraries = SetDefaultModuleLibraries,
- SetDefaultModuleState = SetDefaultModuleState,
- SetDefaultModulePrototype = SetDefaultModulePrototype,
- SetEnabledState = SetEnabledState,
- IterateModules = IterateModules,
- IterateEmbeds = IterateEmbeds,
- GetName = GetName,
-}
-local function IsModule(self) return false end
-local pmixins = {
- defaultModuleState = true,
- enabledState = true,
- IsModule = IsModule,
-}
--- Embed( target )
--- target (object) - target object to embed aceaddon in
---
--- this is a local function specifically since it's meant to be only called internally
-function Embed(target, skipPMixins)
- for k, v in pairs(mixins) do
- target[k] = v
- end
- if not skipPMixins then
- for k, v in pairs(pmixins) do
- target[k] = target[k] or v
- end
- end
-end
-
-
--- - Initialize the addon after creation.
--- This function is only used internally during the ADDON_LOADED event
--- It will call the **OnInitialize** function on the addon object (if present),
--- and the **OnEmbedInitialize** function on all embeded libraries.
---
--- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
--- @param addon addon object to intialize
-function AceAddon:InitializeAddon(addon)
- safecall(addon.OnInitialize, addon)
-
- local embeds = self.embeds[addon]
- for i = 1, #embeds do
- local lib = LibStub:GetLibrary(embeds[i], true)
- if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
- end
-
- -- we don't call InitializeAddon on modules specifically, this is handled
- -- from the event handler and only done _once_
-end
-
--- - Enable the addon after creation.
--- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
--- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
--- It will call the **OnEnable** function on the addon object (if present),
--- and the **OnEmbedEnable** function on all embeded libraries.\\
--- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
---
--- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
--- Use :Enable on the addon itself instead.
--- @param addon addon object to enable
-function AceAddon:EnableAddon(addon)
- if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
- if self.statuses[addon.name] or not addon.enabledState then return false end
-
- -- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable.
- self.statuses[addon.name] = true
-
- safecall(addon.OnEnable, addon)
-
- -- make sure we're still enabled before continueing
- if self.statuses[addon.name] then
- local embeds = self.embeds[addon]
- for i = 1, #embeds do
- local lib = LibStub:GetLibrary(embeds[i], true)
- if lib then safecall(lib.OnEmbedEnable, lib, addon) end
- end
-
- -- enable possible modules.
- local modules = addon.orderedModules
- for i = 1, #modules do
- self:EnableAddon(modules[i])
- end
- end
- return self.statuses[addon.name] -- return true if we're disabled
-end
-
--- - Disable the addon
--- Note: This function is only used internally.
--- It will call the **OnDisable** function on the addon object (if present),
--- and the **OnEmbedDisable** function on all embeded libraries.\\
--- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
---
--- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
--- Use :Disable on the addon itself instead.
--- @param addon addon object to enable
-function AceAddon:DisableAddon(addon)
- if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
- if not self.statuses[addon.name] then return false end
-
- -- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable.
- self.statuses[addon.name] = false
-
- safecall( addon.OnDisable, addon )
-
- -- make sure we're still disabling...
- if not self.statuses[addon.name] then
- local embeds = self.embeds[addon]
- for i = 1, #embeds do
- local lib = LibStub:GetLibrary(embeds[i], true)
- if lib then safecall(lib.OnEmbedDisable, lib, addon) end
- end
- -- disable possible modules.
- local modules = addon.orderedModules
- for i = 1, #modules do
- self:DisableAddon(modules[i])
- end
- end
-
- return not self.statuses[addon.name] -- return true if we're disabled
-end
-
---- Get an iterator over all registered addons.
--- @usage
--- -- Print a list of all installed AceAddon's
--- for name, addon in AceAddon:IterateAddons() do
--- print("Addon: " .. name)
--- end
-function AceAddon:IterateAddons() return pairs(self.addons) end
-
---- Get an iterator over the internal status registry.
--- @usage
--- -- Print a list of all enabled addons
--- for name, status in AceAddon:IterateAddonStatus() do
--- if status then
--- print("EnabledAddon: " .. name)
--- end
--- end
-function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
-
--- Following Iterators are deprecated, and their addon specific versions should be used
--- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
-function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
-function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
-
--- Event Handling
-local function onEvent(this, event, arg1)
- -- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
- if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
- -- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
- while(#AceAddon.initializequeue > 0) do
- local addon = tremove(AceAddon.initializequeue, 1)
- -- this might be an issue with recursion - TODO: validate
- if event == "ADDON_LOADED" then addon.baseName = arg1 end
- AceAddon:InitializeAddon(addon)
- tinsert(AceAddon.enablequeue, addon)
- end
-
- if IsLoggedIn() then
- while(#AceAddon.enablequeue > 0) do
- local addon = tremove(AceAddon.enablequeue, 1)
- AceAddon:EnableAddon(addon)
- end
- end
- end
-end
-
-AceAddon.frame:RegisterEvent("ADDON_LOADED")
-AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
-AceAddon.frame:SetScript("OnEvent", onEvent)
-
--- upgrade embeded
-for name, addon in pairs(AceAddon.addons) do
- Embed(addon, true)
-end
-
--- 2010-10-27 nevcairiel - add new "orderedModules" table
-if oldminor and oldminor < 10 then
- for name, addon in pairs(AceAddon.addons) do
- addon.orderedModules = {}
- for module_name, module in pairs(addon.modules) do
- tinsert(addon.orderedModules, module)
- end
- end
-end
+--- **AceAddon-3.0** provides a template for creating addon objects.
+-- It'll provide you with a set of callback functions that allow you to simplify the loading
+-- process of your addon.\\
+-- Callbacks provided are:\\
+-- * **OnInitialize**, which is called directly after the addon is fully loaded.
+-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+-- * **OnDisable**, which is only called when your addon is manually being disabled.
+-- @usage
+-- -- A small (but complete) addon, that doesn't do anything,
+-- -- but shows usage of the callbacks.
+-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+--
+-- function MyAddon:OnInitialize()
+-- -- do init tasks here, like loading the Saved Variables,
+-- -- or setting up slash commands.
+-- end
+--
+-- function MyAddon:OnEnable()
+-- -- Do more initialization here, that really enables the use of your addon.
+-- -- Register Events, Hook functions, Create Frames, Get information from
+-- -- the game that wasn't available in OnInitialize
+-- end
+--
+-- function MyAddon:OnDisable()
+-- -- Unhook, Unregister Events, Hide frames that you created.
+-- -- You would probably only use an OnDisable if you want to
+-- -- build a "standby" mode, or be able to toggle modules on/off.
+-- end
+-- @class file
+-- @name AceAddon-3.0.lua
+-- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
+
+local MAJOR, MINOR = "AceAddon-3.0", 12
+local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceAddon then return end -- No Upgrade needed.
+
+AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame
+AceAddon.addons = AceAddon.addons or {} -- addons in general
+AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon.
+AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized
+AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled
+AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon
+
+-- Lua APIs
+local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
+local fmt, tostring = string.format, tostring
+local select, pairs, next, type, unpack = select, pairs, next, type, unpack
+local loadstring, assert, error = loadstring, assert, error
+local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler
+
+--[[
+ xpcall safecall implementation
+]]
+local xpcall = xpcall
+
+local function errorhandler(err)
+ return geterrorhandler()(err)
+end
+
+local function CreateDispatcher(argCount)
+ local code = [[
+ local xpcall, eh = ...
+ local method, ARGS
+ local function call() return method(ARGS) end
+
+ local function dispatch(func, ...)
+ method = func
+ if not method then return end
+ ARGS = ...
+ return xpcall(call, eh)
+ end
+
+ return dispatch
+ ]]
+
+ local ARGS = {}
+ for i = 1, argCount do ARGS[i] = "arg"..i end
+ code = code:gsub("ARGS", tconcat(ARGS, ", "))
+ return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
+end
+
+local Dispatchers = setmetatable({}, {__index=function(self, argCount)
+ local dispatcher = CreateDispatcher(argCount)
+ rawset(self, argCount, dispatcher)
+ return dispatcher
+end})
+Dispatchers[0] = function(func)
+ return xpcall(func, errorhandler)
+end
+
+local function safecall(func, ...)
+ -- we check to see if the func is passed is actually a function here and don't error when it isn't
+ -- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
+ -- present execution should continue without hinderance
+ if type(func) == "function" then
+ return Dispatchers[select('#', ...)](func, ...)
+ end
+end
+
+-- local functions that will be implemented further down
+local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype
+
+-- used in the addon metatable
+local function addontostring( self ) return self.name end
+
+-- Check if the addon is queued for initialization
+local function queuedForInitialization(addon)
+ for i = 1, #AceAddon.initializequeue do
+ if AceAddon.initializequeue[i] == addon then
+ return true
+ end
+ end
+ return false
+end
+
+--- Create a new AceAddon-3.0 addon.
+-- Any libraries you specified will be embeded, and the addon will be scheduled for
+-- its OnInitialize and OnEnable callbacks.
+-- The final addon object, with all libraries embeded, will be returned.
+-- @paramsig [object ,]name[, lib, ...]
+-- @param object Table to use as a base for the addon (optional)
+-- @param name Name of the addon object to create
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a simple addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0")
+--
+-- -- Create a Addon object based on the table of a frame
+-- local MyFrame = CreateFrame("Frame")
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0")
+function AceAddon:NewAddon(objectorname, ...)
+ local object,name
+ local i=1
+ if type(objectorname)=="table" then
+ object=objectorname
+ name=...
+ i=2
+ else
+ name=objectorname
+ end
+ if type(name)~="string" then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2)
+ end
+ if self.addons[name] then
+ error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2)
+ end
+
+ object = object or {}
+ object.name = name
+
+ local addonmeta = {}
+ local oldmeta = getmetatable(object)
+ if oldmeta then
+ for k, v in pairs(oldmeta) do addonmeta[k] = v end
+ end
+ addonmeta.__tostring = addontostring
+
+ setmetatable( object, addonmeta )
+ self.addons[name] = object
+ object.modules = {}
+ object.orderedModules = {}
+ object.defaultModuleLibraries = {}
+ Embed( object ) -- embed NewModule, GetModule methods
+ self:EmbedLibraries(object, select(i,...))
+
+ -- add to queue of addons to be initialized upon ADDON_LOADED
+ tinsert(self.initializequeue, object)
+ return object
+end
+
+
+--- Get the addon object by its name from the internal AceAddon registry.
+-- Throws an error if the addon object cannot be found (except if silent is set).
+-- @param name unique name of the addon object
+-- @param silent if true, the addon is optional, silently return nil if its not found
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+function AceAddon:GetAddon(name, silent)
+ if not silent and not self.addons[name] then
+ error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2)
+ end
+ return self.addons[name]
+end
+
+-- - Embed a list of libraries into the specified addon.
+-- This function will try to embed all of the listed libraries into the addon
+-- and error if a single one fails.
+--
+-- **Note:** This function is for internal use by :NewAddon/:NewModule
+-- @paramsig addon, [lib, ...]
+-- @param addon addon object to embed the libs in
+-- @param lib List of libraries to embed into the addon
+function AceAddon:EmbedLibraries(addon, ...)
+ for i=1,select("#", ... ) do
+ local libname = select(i, ...)
+ self:EmbedLibrary(addon, libname, false, 4)
+ end
+end
+
+-- - Embed a library into the addon object.
+-- This function will check if the specified library is registered with LibStub
+-- and if it has a :Embed function to call. It'll error if any of those conditions
+-- fails.
+--
+-- **Note:** This function is for internal use by :EmbedLibraries
+-- @paramsig addon, libname[, silent[, offset]]
+-- @param addon addon object to embed the library in
+-- @param libname name of the library to embed
+-- @param silent marks an embed to fail silently if the library doesn't exist (optional)
+-- @param offset will push the error messages back to said offset, defaults to 2 (optional)
+function AceAddon:EmbedLibrary(addon, libname, silent, offset)
+ local lib = LibStub:GetLibrary(libname, true)
+ if not lib and not silent then
+ error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2)
+ elseif lib and type(lib.Embed) == "function" then
+ lib:Embed(addon)
+ tinsert(self.embeds[addon], libname)
+ return true
+ elseif lib then
+ error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2)
+ end
+end
+
+--- Return the specified module from an addon object.
+-- Throws an error if the addon object cannot be found (except if silent is set)
+-- @name //addon//:GetModule
+-- @paramsig name[, silent]
+-- @param name unique name of the module
+-- @param silent if true, the module is optional, silently return nil if its not found (optional)
+-- @usage
+-- -- Get the Addon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- -- Get the Module
+-- MyModule = MyAddon:GetModule("MyModule")
+function GetModule(self, name, silent)
+ if not self.modules[name] and not silent then
+ error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2)
+ end
+ return self.modules[name]
+end
+
+local function IsModuleTrue(self) return true end
+
+--- Create a new module for the addon.
+-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\
+-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as
+-- an addon object.
+-- @name //addon//:NewModule
+-- @paramsig name[, prototype|lib[, lib, ...]]
+-- @param name unique name of the module
+-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional)
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create a module with some embeded libraries
+-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0")
+--
+-- -- Create a module with a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0")
+function NewModule(self, name, prototype, ...)
+ if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end
+ if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end
+
+ if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end
+
+ -- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well.
+ -- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is.
+ local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name))
+
+ module.IsModule = IsModuleTrue
+ module:SetEnabledState(self.defaultModuleState)
+ module.moduleName = name
+
+ if type(prototype) == "string" then
+ AceAddon:EmbedLibraries(module, prototype, ...)
+ else
+ AceAddon:EmbedLibraries(module, ...)
+ end
+ AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries))
+
+ if not prototype or type(prototype) == "string" then
+ prototype = self.defaultModulePrototype or nil
+ end
+
+ if type(prototype) == "table" then
+ local mt = getmetatable(module)
+ mt.__index = prototype
+ setmetatable(module, mt) -- More of a Base class type feel.
+ end
+
+ safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy.
+ self.modules[name] = module
+ tinsert(self.orderedModules, module)
+
+ return module
+end
+
+--- Returns the real name of the addon or module, without any prefix.
+-- @name //addon//:GetName
+-- @paramsig
+-- @usage
+-- print(MyAddon:GetName())
+-- -- prints "MyAddon"
+function GetName(self)
+ return self.moduleName or self.name
+end
+
+--- Enables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback
+-- and enabling all modules of the addon (unless explicitly disabled).\\
+-- :Enable() also sets the internal `enableState` variable to true
+-- @name //addon//:Enable
+-- @paramsig
+-- @usage
+-- -- Enable MyModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+function Enable(self)
+ self:SetEnabledState(true)
+
+ -- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still
+ -- it'll be enabled after the init process
+ if not queuedForInitialization(self) then
+ return AceAddon:EnableAddon(self)
+ end
+end
+
+--- Disables the Addon, if possible, return true or false depending on success.
+-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback
+-- and disabling all modules of the addon.\\
+-- :Disable() also sets the internal `enableState` variable to false
+-- @name //addon//:Disable
+-- @paramsig
+-- @usage
+-- -- Disable MyAddon
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:Disable()
+function Disable(self)
+ self:SetEnabledState(false)
+ return AceAddon:DisableAddon(self)
+end
+
+--- Enables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object.
+-- @name //addon//:EnableModule
+-- @paramsig name
+-- @usage
+-- -- Enable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Enable()
+--
+-- -- Enable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:EnableModule("MyModule")
+function EnableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Enable()
+end
+
+--- Disables the Module, if possible, return true or false depending on success.
+-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object.
+-- @name //addon//:DisableModule
+-- @paramsig name
+-- @usage
+-- -- Disable MyModule using :GetModule
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyModule = MyAddon:GetModule("MyModule")
+-- MyModule:Disable()
+--
+-- -- Disable MyModule using the short-hand
+-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon")
+-- MyAddon:DisableModule("MyModule")
+function DisableModule(self, name)
+ local module = self:GetModule( name )
+ return module:Disable()
+end
+
+--- Set the default libraries to be mixed into all modules created by this object.
+-- Note that you can only change the default module libraries before any module is created.
+-- @name //addon//:SetDefaultModuleLibraries
+-- @paramsig lib[, lib, ...]
+-- @param lib List of libraries to embed into the addon
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Configure default libraries for modules (all modules need AceEvent-3.0)
+-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0")
+-- -- Create a module
+-- MyModule = MyAddon:NewModule("MyModule")
+function SetDefaultModuleLibraries(self, ...)
+ if next(self.modules) then
+ error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2)
+ end
+ self.defaultModuleLibraries = {...}
+end
+
+--- Set the default state in which new modules are being created.
+-- Note that you can only change the default state before any module is created.
+-- @name //addon//:SetDefaultModuleState
+-- @paramsig state
+-- @param state Default state for new modules, true for enabled, false for disabled
+-- @usage
+-- -- Create the addon object
+-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon")
+-- -- Set the default state to "disabled"
+-- MyAddon:SetDefaultModuleState(false)
+-- -- Create a module and explicilty enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+function SetDefaultModuleState(self, state)
+ if next(self.modules) then
+ error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2)
+ end
+ self.defaultModuleState = state
+end
+
+--- Set the default prototype to use for new modules on creation.
+-- Note that you can only change the default prototype before any module is created.
+-- @name //addon//:SetDefaultModulePrototype
+-- @paramsig prototype
+-- @param prototype Default prototype for the new modules (table)
+-- @usage
+-- -- Define a prototype
+-- local prototype = { OnEnable = function(self) print("OnEnable called!") end }
+-- -- Set the default prototype
+-- MyAddon:SetDefaultModulePrototype(prototype)
+-- -- Create a module and explicitly Enable it
+-- MyModule = MyAddon:NewModule("MyModule")
+-- MyModule:Enable()
+-- -- should print "OnEnable called!" now
+-- @see NewModule
+function SetDefaultModulePrototype(self, prototype)
+ if next(self.modules) then
+ error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2)
+ end
+ if type(prototype) ~= "table" then
+ error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2)
+ end
+ self.defaultModulePrototype = prototype
+end
+
+--- Set the state of an addon or module
+-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize.
+-- @name //addon//:SetEnabledState
+-- @paramsig state
+-- @param state the state of an addon or module (enabled=true, disabled=false)
+function SetEnabledState(self, state)
+ self.enabledState = state
+end
+
+
+--- Return an iterator of all modules associated to the addon.
+-- @name //addon//:IterateModules
+-- @paramsig
+-- @usage
+-- -- Enable all modules
+-- for name, module in MyAddon:IterateModules() do
+-- module:Enable()
+-- end
+local function IterateModules(self) return pairs(self.modules) end
+
+-- Returns an iterator of all embeds in the addon
+-- @name //addon//:IterateEmbeds
+-- @paramsig
+local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end
+
+--- Query the enabledState of an addon.
+-- @name //addon//:IsEnabled
+-- @paramsig
+-- @usage
+-- if MyAddon:IsEnabled() then
+-- MyAddon:Disable()
+-- end
+local function IsEnabled(self) return self.enabledState end
+local mixins = {
+ NewModule = NewModule,
+ GetModule = GetModule,
+ Enable = Enable,
+ Disable = Disable,
+ EnableModule = EnableModule,
+ DisableModule = DisableModule,
+ IsEnabled = IsEnabled,
+ SetDefaultModuleLibraries = SetDefaultModuleLibraries,
+ SetDefaultModuleState = SetDefaultModuleState,
+ SetDefaultModulePrototype = SetDefaultModulePrototype,
+ SetEnabledState = SetEnabledState,
+ IterateModules = IterateModules,
+ IterateEmbeds = IterateEmbeds,
+ GetName = GetName,
+}
+local function IsModule(self) return false end
+local pmixins = {
+ defaultModuleState = true,
+ enabledState = true,
+ IsModule = IsModule,
+}
+-- Embed( target )
+-- target (object) - target object to embed aceaddon in
+--
+-- this is a local function specifically since it's meant to be only called internally
+function Embed(target, skipPMixins)
+ for k, v in pairs(mixins) do
+ target[k] = v
+ end
+ if not skipPMixins then
+ for k, v in pairs(pmixins) do
+ target[k] = target[k] or v
+ end
+ end
+end
+
+
+-- - Initialize the addon after creation.
+-- This function is only used internally during the ADDON_LOADED event
+-- It will call the **OnInitialize** function on the addon object (if present),
+-- and the **OnEmbedInitialize** function on all embeded libraries.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- @param addon addon object to intialize
+function AceAddon:InitializeAddon(addon)
+ safecall(addon.OnInitialize, addon)
+
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedInitialize, lib, addon) end
+ end
+
+ -- we don't call InitializeAddon on modules specifically, this is handled
+ -- from the event handler and only done _once_
+end
+
+-- - Enable the addon after creation.
+-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED,
+-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons.
+-- It will call the **OnEnable** function on the addon object (if present),
+-- and the **OnEmbedEnable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Enable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:EnableAddon(addon)
+ if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+ if self.statuses[addon.name] or not addon.enabledState then return false end
+
+ -- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable.
+ self.statuses[addon.name] = true
+
+ safecall(addon.OnEnable, addon)
+
+ -- make sure we're still enabled before continueing
+ if self.statuses[addon.name] then
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedEnable, lib, addon) end
+ end
+
+ -- enable possible modules.
+ local modules = addon.orderedModules
+ for i = 1, #modules do
+ self:EnableAddon(modules[i])
+ end
+ end
+ return self.statuses[addon.name] -- return true if we're disabled
+end
+
+-- - Disable the addon
+-- Note: This function is only used internally.
+-- It will call the **OnDisable** function on the addon object (if present),
+-- and the **OnEmbedDisable** function on all embeded libraries.\\
+-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled.
+--
+-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing.
+-- Use :Disable on the addon itself instead.
+-- @param addon addon object to enable
+function AceAddon:DisableAddon(addon)
+ if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end
+ if not self.statuses[addon.name] then return false end
+
+ -- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable.
+ self.statuses[addon.name] = false
+
+ safecall( addon.OnDisable, addon )
+
+ -- make sure we're still disabling...
+ if not self.statuses[addon.name] then
+ local embeds = self.embeds[addon]
+ for i = 1, #embeds do
+ local lib = LibStub:GetLibrary(embeds[i], true)
+ if lib then safecall(lib.OnEmbedDisable, lib, addon) end
+ end
+ -- disable possible modules.
+ local modules = addon.orderedModules
+ for i = 1, #modules do
+ self:DisableAddon(modules[i])
+ end
+ end
+
+ return not self.statuses[addon.name] -- return true if we're disabled
+end
+
+--- Get an iterator over all registered addons.
+-- @usage
+-- -- Print a list of all installed AceAddon's
+-- for name, addon in AceAddon:IterateAddons() do
+-- print("Addon: " .. name)
+-- end
+function AceAddon:IterateAddons() return pairs(self.addons) end
+
+--- Get an iterator over the internal status registry.
+-- @usage
+-- -- Print a list of all enabled addons
+-- for name, status in AceAddon:IterateAddonStatus() do
+-- if status then
+-- print("EnabledAddon: " .. name)
+-- end
+-- end
+function AceAddon:IterateAddonStatus() return pairs(self.statuses) end
+
+-- Following Iterators are deprecated, and their addon specific versions should be used
+-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon)
+function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end
+function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end
+
+-- Event Handling
+local function onEvent(this, event, arg1)
+ -- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up
+ if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then
+ -- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration
+ while(#AceAddon.initializequeue > 0) do
+ local addon = tremove(AceAddon.initializequeue, 1)
+ -- this might be an issue with recursion - TODO: validate
+ if event == "ADDON_LOADED" then addon.baseName = arg1 end
+ AceAddon:InitializeAddon(addon)
+ tinsert(AceAddon.enablequeue, addon)
+ end
+
+ if IsLoggedIn() then
+ while(#AceAddon.enablequeue > 0) do
+ local addon = tremove(AceAddon.enablequeue, 1)
+ AceAddon:EnableAddon(addon)
+ end
+ end
+ end
+end
+
+AceAddon.frame:RegisterEvent("ADDON_LOADED")
+AceAddon.frame:RegisterEvent("PLAYER_LOGIN")
+AceAddon.frame:SetScript("OnEvent", onEvent)
+
+-- upgrade embeded
+for name, addon in pairs(AceAddon.addons) do
+ Embed(addon, true)
+end
+
+-- 2010-10-27 nevcairiel - add new "orderedModules" table
+if oldminor and oldminor < 10 then
+ for name, addon in pairs(AceAddon.addons) do
+ addon.orderedModules = {}
+ for module_name, module in pairs(addon.modules) do
+ tinsert(addon.orderedModules, module)
+ end
+ end
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.xml
index 559e04ebc..e24640c36 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceAddon-3.0/AceAddon-3.0.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceAddon-3.0/AceAddon-3.0.xml
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.lua
index 54dadfa38..5815530cc 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.lua
@@ -1,301 +1,301 @@
---- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels.
--- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\
--- **ChatThrottleLib** is of course being used to avoid being disconnected by the server.
---
--- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by
--- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
--- and can be accessed directly, without having to explicitly call AceComm itself.\\
--- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you
--- make into AceComm.
--- @class file
--- @name AceComm-3.0
--- @release $Id: AceComm-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
-
---[[ AceComm-3.0
-
-TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited.
-
-]]
-
-local CallbackHandler = LibStub("CallbackHandler-1.0")
-local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
-
-local MAJOR, MINOR = "AceComm-3.0", 10
-local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not AceComm then return end
-
--- Lua APIs
-local type, next, pairs, tostring = type, next, pairs, tostring
-local strsub, strfind = string.sub, string.find
-local match = string.match
-local tinsert, tconcat = table.insert, table.concat
-local error, assert = error, assert
-
--- WoW APIs
-local Ambiguate = Ambiguate
-
--- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
--- List them here for Mikk's FindGlobals script
--- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix
-
-AceComm.embeds = AceComm.embeds or {}
-
--- for my sanity and yours, let's give the message type bytes some names
-local MSG_MULTI_FIRST = "\001"
-local MSG_MULTI_NEXT = "\002"
-local MSG_MULTI_LAST = "\003"
-local MSG_ESCAPE = "\004"
-
--- remove old structures (pre WoW 4.0)
-AceComm.multipart_origprefixes = nil
-AceComm.multipart_reassemblers = nil
-
--- the multipart message spool: indexed by a combination of sender+distribution+
-AceComm.multipart_spool = AceComm.multipart_spool or {}
-
---- Register for Addon Traffic on a specified prefix
--- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
--- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
-function AceComm:RegisterComm(prefix, method)
- if method == nil then
- method = "OnCommReceived"
- end
-
- if #prefix > 16 then -- TODO: 15?
- error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
- end
- RegisterAddonMessagePrefix(prefix)
-
- return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler
-end
-
-local warnedPrefix=false
-
---- Send a message over the Addon Channel
--- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
--- @param text Data to send, nils (\000) not allowed. Any length.
--- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API
--- @param target Destination for some distributions; see SendAddonMessage API
--- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL".
--- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send.
--- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified.
-function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg)
- prio = prio or "NORMAL" -- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery!
- if not( type(prefix)=="string" and
- type(text)=="string" and
- type(distribution)=="string" and
- (target==nil or type(target)=="string") and
- (prio=="BULK" or prio=="NORMAL" or prio=="ALERT")
- ) then
- error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
- end
-
- local textlen = #text
- local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
- local queueName = prefix..distribution..(target or "")
-
- local ctlCallback = nil
- if callbackFn then
- ctlCallback = function(sent)
- return callbackFn(callbackArg, sent, textlen)
- end
- end
-
- local forceMultipart
- if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
- -- we need to escape the first character with a \004
- if textlen+1 > maxtextlen then -- would we go over the size limit?
- forceMultipart = true -- just make it multipart, no escape problems then
- else
- text = "\004" .. text
- end
- end
-
- if not forceMultipart and textlen <= maxtextlen then
- -- fits all in one message
- CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
- else
- maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
-
- -- first part
- local chunk = strsub(text, 1, maxtextlen)
- CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
-
- -- continuation
- local pos = 1+maxtextlen
-
- while pos+maxtextlen <= textlen do
- chunk = strsub(text, pos, pos+maxtextlen-1)
- CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
- pos = pos + maxtextlen
- end
-
- -- final part
- chunk = strsub(text, pos)
- CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
- end
-end
-
-
-----------------------------------------
--- Message receiving
-----------------------------------------
-
-do
- local compost = setmetatable({}, {__mode = "k"})
- local function new()
- local t = next(compost)
- if t then
- compost[t]=nil
- for i=#t,3,-1 do -- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten
- t[i]=nil
- end
- return t
- end
-
- return {}
- end
-
- local function lostdatawarning(prefix,sender,where)
- DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")")
- end
-
- function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender)
- local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
- local spool = AceComm.multipart_spool
-
- --[[
- if spool[key] then
- lostdatawarning(prefix,sender,"First")
- -- continue and overwrite
- end
- --]]
-
- spool[key] = message -- plain string for now
- end
-
- function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender)
- local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
- local spool = AceComm.multipart_spool
- local olddata = spool[key]
-
- if not olddata then
- --lostdatawarning(prefix,sender,"Next")
- return
- end
-
- if type(olddata)~="table" then
- -- ... but what we have is not a table. So make it one. (Pull a composted one if available)
- local t = new()
- t[1] = olddata -- add old data as first string
- t[2] = message -- and new message as second string
- spool[key] = t -- and put the table in the spool instead of the old string
- else
- tinsert(olddata, message)
- end
- end
-
- function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender)
- local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
- local spool = AceComm.multipart_spool
- local olddata = spool[key]
-
- if not olddata then
- --lostdatawarning(prefix,sender,"End")
- return
- end
-
- spool[key] = nil
-
- if type(olddata) == "table" then
- -- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat
- tinsert(olddata, message)
- AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender)
- compost[olddata] = true
- else
- -- if we've only received a "first", the spooled data will still only be a string
- AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender)
- end
- end
-end
-
-
-
-
-
-
-----------------------------------------
--- Embed CallbackHandler
-----------------------------------------
-
-if not AceComm.callbacks then
- AceComm.callbacks = CallbackHandler:New(AceComm,
- "_RegisterComm",
- "UnregisterComm",
- "UnregisterAllComm")
-end
-
-AceComm.callbacks.OnUsed = nil
-AceComm.callbacks.OnUnused = nil
-
-local function OnEvent(self, event, prefix, message, distribution, sender)
- if event == "CHAT_MSG_ADDON" then
- sender = Ambiguate(sender, "none")
- local control, rest = match(message, "^([\001-\009])(.*)")
- if control then
- if control==MSG_MULTI_FIRST then
- AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
- elseif control==MSG_MULTI_NEXT then
- AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
- elseif control==MSG_MULTI_LAST then
- AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
- elseif control==MSG_ESCAPE then
- AceComm.callbacks:Fire(prefix, rest, distribution, sender)
- else
- -- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
- end
- else
- -- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
- AceComm.callbacks:Fire(prefix, message, distribution, sender)
- end
- else
- assert(false, "Received "..tostring(event).." event?!")
- end
-end
-
-AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame")
-AceComm.frame:SetScript("OnEvent", OnEvent)
-AceComm.frame:UnregisterAllEvents()
-AceComm.frame:RegisterEvent("CHAT_MSG_ADDON")
-
-
-----------------------------------------
--- Base library stuff
-----------------------------------------
-
-local mixins = {
- "RegisterComm",
- "UnregisterComm",
- "UnregisterAllComm",
- "SendCommMessage",
-}
-
--- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:..
--- @param target target object to embed AceComm-3.0 in
-function AceComm:Embed(target)
- for k, v in pairs(mixins) do
- target[v] = self[v]
- end
- self.embeds[target] = true
- return target
-end
-
-function AceComm:OnEmbedDisable(target)
- target:UnregisterAllComm()
-end
-
--- Update embeds
-for target, v in pairs(AceComm.embeds) do
- AceComm:Embed(target)
-end
+--- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels.
+-- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\
+-- **ChatThrottleLib** is of course being used to avoid being disconnected by the server.
+--
+-- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceComm itself.\\
+-- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceComm.
+-- @class file
+-- @name AceComm-3.0
+-- @release $Id: AceComm-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
+
+--[[ AceComm-3.0
+
+TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited.
+
+]]
+
+local CallbackHandler = LibStub("CallbackHandler-1.0")
+local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib")
+
+local MAJOR, MINOR = "AceComm-3.0", 10
+local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceComm then return end
+
+-- Lua APIs
+local type, next, pairs, tostring = type, next, pairs, tostring
+local strsub, strfind = string.sub, string.find
+local match = string.match
+local tinsert, tconcat = table.insert, table.concat
+local error, assert = error, assert
+
+-- WoW APIs
+local Ambiguate = Ambiguate
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix
+
+AceComm.embeds = AceComm.embeds or {}
+
+-- for my sanity and yours, let's give the message type bytes some names
+local MSG_MULTI_FIRST = "\001"
+local MSG_MULTI_NEXT = "\002"
+local MSG_MULTI_LAST = "\003"
+local MSG_ESCAPE = "\004"
+
+-- remove old structures (pre WoW 4.0)
+AceComm.multipart_origprefixes = nil
+AceComm.multipart_reassemblers = nil
+
+-- the multipart message spool: indexed by a combination of sender+distribution+
+AceComm.multipart_spool = AceComm.multipart_spool or {}
+
+--- Register for Addon Traffic on a specified prefix
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters
+-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived"
+function AceComm:RegisterComm(prefix, method)
+ if method == nil then
+ method = "OnCommReceived"
+ end
+
+ if #prefix > 16 then -- TODO: 15?
+ error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters")
+ end
+ RegisterAddonMessagePrefix(prefix)
+
+ return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler
+end
+
+local warnedPrefix=false
+
+--- Send a message over the Addon Channel
+-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent)
+-- @param text Data to send, nils (\000) not allowed. Any length.
+-- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API
+-- @param target Destination for some distributions; see SendAddonMessage API
+-- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL".
+-- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send.
+-- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified.
+function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg)
+ prio = prio or "NORMAL" -- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery!
+ if not( type(prefix)=="string" and
+ type(text)=="string" and
+ type(distribution)=="string" and
+ (target==nil or type(target)=="string") and
+ (prio=="BULK" or prio=="NORMAL" or prio=="ALERT")
+ ) then
+ error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2)
+ end
+
+ local textlen = #text
+ local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327
+ local queueName = prefix..distribution..(target or "")
+
+ local ctlCallback = nil
+ if callbackFn then
+ ctlCallback = function(sent)
+ return callbackFn(callbackArg, sent, textlen)
+ end
+ end
+
+ local forceMultipart
+ if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character
+ -- we need to escape the first character with a \004
+ if textlen+1 > maxtextlen then -- would we go over the size limit?
+ forceMultipart = true -- just make it multipart, no escape problems then
+ else
+ text = "\004" .. text
+ end
+ end
+
+ if not forceMultipart and textlen <= maxtextlen then
+ -- fits all in one message
+ CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen)
+ else
+ maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1)
+
+ -- first part
+ local chunk = strsub(text, 1, maxtextlen)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen)
+
+ -- continuation
+ local pos = 1+maxtextlen
+
+ while pos+maxtextlen <= textlen do
+ chunk = strsub(text, pos, pos+maxtextlen-1)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1)
+ pos = pos + maxtextlen
+ end
+
+ -- final part
+ chunk = strsub(text, pos)
+ CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen)
+ end
+end
+
+
+----------------------------------------
+-- Message receiving
+----------------------------------------
+
+do
+ local compost = setmetatable({}, {__mode = "k"})
+ local function new()
+ local t = next(compost)
+ if t then
+ compost[t]=nil
+ for i=#t,3,-1 do -- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten
+ t[i]=nil
+ end
+ return t
+ end
+
+ return {}
+ end
+
+ local function lostdatawarning(prefix,sender,where)
+ DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")")
+ end
+
+ function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+
+ --[[
+ if spool[key] then
+ lostdatawarning(prefix,sender,"First")
+ -- continue and overwrite
+ end
+ --]]
+
+ spool[key] = message -- plain string for now
+ end
+
+ function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+ local olddata = spool[key]
+
+ if not olddata then
+ --lostdatawarning(prefix,sender,"Next")
+ return
+ end
+
+ if type(olddata)~="table" then
+ -- ... but what we have is not a table. So make it one. (Pull a composted one if available)
+ local t = new()
+ t[1] = olddata -- add old data as first string
+ t[2] = message -- and new message as second string
+ spool[key] = t -- and put the table in the spool instead of the old string
+ else
+ tinsert(olddata, message)
+ end
+ end
+
+ function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender)
+ local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender
+ local spool = AceComm.multipart_spool
+ local olddata = spool[key]
+
+ if not olddata then
+ --lostdatawarning(prefix,sender,"End")
+ return
+ end
+
+ spool[key] = nil
+
+ if type(olddata) == "table" then
+ -- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat
+ tinsert(olddata, message)
+ AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender)
+ compost[olddata] = true
+ else
+ -- if we've only received a "first", the spooled data will still only be a string
+ AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender)
+ end
+ end
+end
+
+
+
+
+
+
+----------------------------------------
+-- Embed CallbackHandler
+----------------------------------------
+
+if not AceComm.callbacks then
+ AceComm.callbacks = CallbackHandler:New(AceComm,
+ "_RegisterComm",
+ "UnregisterComm",
+ "UnregisterAllComm")
+end
+
+AceComm.callbacks.OnUsed = nil
+AceComm.callbacks.OnUnused = nil
+
+local function OnEvent(self, event, prefix, message, distribution, sender)
+ if event == "CHAT_MSG_ADDON" then
+ sender = Ambiguate(sender, "none")
+ local control, rest = match(message, "^([\001-\009])(.*)")
+ if control then
+ if control==MSG_MULTI_FIRST then
+ AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender)
+ elseif control==MSG_MULTI_NEXT then
+ AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender)
+ elseif control==MSG_MULTI_LAST then
+ AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender)
+ elseif control==MSG_ESCAPE then
+ AceComm.callbacks:Fire(prefix, rest, distribution, sender)
+ else
+ -- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!)
+ end
+ else
+ -- single part: fire it off immediately and let CallbackHandler decide if it's registered or not
+ AceComm.callbacks:Fire(prefix, message, distribution, sender)
+ end
+ else
+ assert(false, "Received "..tostring(event).." event?!")
+ end
+end
+
+AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame")
+AceComm.frame:SetScript("OnEvent", OnEvent)
+AceComm.frame:UnregisterAllEvents()
+AceComm.frame:RegisterEvent("CHAT_MSG_ADDON")
+
+
+----------------------------------------
+-- Base library stuff
+----------------------------------------
+
+local mixins = {
+ "RegisterComm",
+ "UnregisterComm",
+ "UnregisterAllComm",
+ "SendCommMessage",
+}
+
+-- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceComm-3.0 in
+function AceComm:Embed(target)
+ for k, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+function AceComm:OnEmbedDisable(target)
+ target:UnregisterAllComm()
+end
+
+-- Update embeds
+for target, v in pairs(AceComm.embeds) do
+ AceComm:Embed(target)
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.xml
index e7b85f8e2..2829688e0 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/AceComm-3.0.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/AceComm-3.0.xml
@@ -1,5 +1,5 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/ChatThrottleLib.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/ChatThrottleLib.lua
similarity index 96%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/ChatThrottleLib.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/ChatThrottleLib.lua
index 3ddeef819..05a0363d9 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceComm-3.0/ChatThrottleLib.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceComm-3.0/ChatThrottleLib.lua
@@ -1,524 +1,524 @@
---
--- ChatThrottleLib by Mikk
---
--- Manages AddOn chat output to keep player from getting kicked off.
---
--- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
--- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
---
--- Priorities get an equal share of available bandwidth when fully loaded.
--- Communication channels are separated on extension+chattype+destination and
--- get round-robinned. (Destination only matters for whispers and channels,
--- obviously)
---
--- Will install hooks for SendChatMessage and SendAddonMessage to measure
--- bandwidth bypassing the library and use less bandwidth itself.
---
---
--- Fully embeddable library. Just copy this file into your addon directory,
--- add it to the .toc, and it's done.
---
--- Can run as a standalone addon also, but, really, just embed it! :-)
---
--- LICENSE: ChatThrottleLib is released into the Public Domain
---
-
-local CTL_VERSION = 23
-
-local _G = _G
-
-if _G.ChatThrottleLib then
- if _G.ChatThrottleLib.version >= CTL_VERSION then
- -- There's already a newer (or same) version loaded. Buh-bye.
- return
- elseif not _G.ChatThrottleLib.securelyHooked then
- print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, =v16) in it!")
- -- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
- -- ... and if someone has securehooked, they can kiss that goodbye too... >.<
- _G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
- if _G.ChatThrottleLib.ORIG_SendAddonMessage then
- _G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
- end
- end
- _G.ChatThrottleLib.ORIG_SendChatMessage = nil
- _G.ChatThrottleLib.ORIG_SendAddonMessage = nil
-end
-
-if not _G.ChatThrottleLib then
- _G.ChatThrottleLib = {}
-end
-
-ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
-local ChatThrottleLib = _G.ChatThrottleLib
-
-ChatThrottleLib.version = CTL_VERSION
-
-
-
------------------- TWEAKABLES -----------------
-
-ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
-ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
-
-ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
-
-ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
-
-
-local setmetatable = setmetatable
-local table_remove = table.remove
-local tostring = tostring
-local GetTime = GetTime
-local math_min = math.min
-local math_max = math.max
-local next = next
-local strlen = string.len
-local GetFramerate = GetFramerate
-local strlower = string.lower
-local unpack,type,pairs,wipe = unpack,type,pairs,wipe
-local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
-
-
------------------------------------------------------------------------
--- Double-linked ring implementation
-
-local Ring = {}
-local RingMeta = { __index = Ring }
-
-function Ring:New()
- local ret = {}
- setmetatable(ret, RingMeta)
- return ret
-end
-
-function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
- if self.pos then
- obj.prev = self.pos.prev
- obj.prev.next = obj
- obj.next = self.pos
- obj.next.prev = obj
- else
- obj.next = obj
- obj.prev = obj
- self.pos = obj
- end
-end
-
-function Ring:Remove(obj)
- obj.next.prev = obj.prev
- obj.prev.next = obj.next
- if self.pos == obj then
- self.pos = obj.next
- if self.pos == obj then
- self.pos = nil
- end
- end
-end
-
-
-
------------------------------------------------------------------------
--- Recycling bin for pipes
--- A pipe is a plain integer-indexed queue of messages
--- Pipes normally live in Rings of pipes (3 rings total, one per priority)
-
-ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
-local PipeBin = setmetatable({}, {__mode="k"})
-
-local function DelPipe(pipe)
- PipeBin[pipe] = true
-end
-
-local function NewPipe()
- local pipe = next(PipeBin)
- if pipe then
- wipe(pipe)
- PipeBin[pipe] = nil
- return pipe
- end
- return {}
-end
-
-
-
-
------------------------------------------------------------------------
--- Recycling bin for messages
-
-ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
-local MsgBin = setmetatable({}, {__mode="k"})
-
-local function DelMsg(msg)
- msg[1] = nil
- -- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
- MsgBin[msg] = true
-end
-
-local function NewMsg()
- local msg = next(MsgBin)
- if msg then
- MsgBin[msg] = nil
- return msg
- end
- return {}
-end
-
-
------------------------------------------------------------------------
--- ChatThrottleLib:Init
--- Initialize queues, set up frame for OnUpdate, etc
-
-
-function ChatThrottleLib:Init()
-
- -- Set up queues
- if not self.Prio then
- self.Prio = {}
- self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
- self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
- self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
- end
-
- -- v4: total send counters per priority
- for _, Prio in pairs(self.Prio) do
- Prio.nTotalSent = Prio.nTotalSent or 0
- end
-
- if not self.avail then
- self.avail = 0 -- v5
- end
- if not self.nTotalSent then
- self.nTotalSent = 0 -- v5
- end
-
-
- -- Set up a frame to get OnUpdate events
- if not self.Frame then
- self.Frame = CreateFrame("Frame")
- self.Frame:Hide()
- end
- self.Frame:SetScript("OnUpdate", self.OnUpdate)
- self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
- self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
- self.OnUpdateDelay = 0
- self.LastAvailUpdate = GetTime()
- self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
-
- -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
- if not self.securelyHooked then
- -- Use secure hooks as of v16. Old regular hook support yanked out in v21.
- self.securelyHooked = true
- --SendChatMessage
- hooksecurefunc("SendChatMessage", function(...)
- return ChatThrottleLib.Hook_SendChatMessage(...)
- end)
- --SendAddonMessage
- hooksecurefunc("SendAddonMessage", function(...)
- return ChatThrottleLib.Hook_SendAddonMessage(...)
- end)
- end
- self.nBypass = 0
-end
-
-
------------------------------------------------------------------------
--- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
-
-local bMyTraffic = false
-
-function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
- if bMyTraffic then
- return
- end
- local self = ChatThrottleLib
- local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
- self.avail = self.avail - size
- self.nBypass = self.nBypass + size -- just a statistic
-end
-function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
- if bMyTraffic then
- return
- end
- local self = ChatThrottleLib
- local size = tostring(text or ""):len() + tostring(prefix or ""):len();
- size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
- self.avail = self.avail - size
- self.nBypass = self.nBypass + size -- just a statistic
-end
-
-
-
------------------------------------------------------------------------
--- ChatThrottleLib:UpdateAvail
--- Update self.avail with how much bandwidth is currently available
-
-function ChatThrottleLib:UpdateAvail()
- local now = GetTime()
- local MAX_CPS = self.MAX_CPS;
- local newavail = MAX_CPS * (now - self.LastAvailUpdate)
- local avail = self.avail
-
- if now - self.HardThrottlingBeginTime < 5 then
- -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
- avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
- self.bChoking = true
- elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
- avail = math_min(MAX_CPS, avail + newavail*0.5)
- self.bChoking = true -- just a statistic
- else
- avail = math_min(self.BURST, avail + newavail)
- self.bChoking = false
- end
-
- avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
-
- self.avail = avail
- self.LastAvailUpdate = now
-
- return avail
-end
-
-
------------------------------------------------------------------------
--- Despooling logic
--- Reminder:
--- - We have 3 Priorities, each containing a "Ring" construct ...
--- - ... made up of N "Pipe"s (1 for each destination/pipename)
--- - and each pipe contains messages
-
-function ChatThrottleLib:Despool(Prio)
- local ring = Prio.Ring
- while ring.pos and Prio.avail > ring.pos[1].nSize do
- local msg = table_remove(ring.pos, 1)
- if not ring.pos[1] then -- did we remove last msg in this pipe?
- local pipe = Prio.Ring.pos
- Prio.Ring:Remove(pipe)
- Prio.ByName[pipe.name] = nil
- DelPipe(pipe)
- else
- Prio.Ring.pos = Prio.Ring.pos.next
- end
- local didSend=false
- local lowerDest = strlower(msg[3] or "")
- if lowerDest == "raid" and not UnitInRaid("player") then
- -- do nothing
- elseif lowerDest == "party" and not UnitInParty("player") then
- -- do nothing
- else
- Prio.avail = Prio.avail - msg.nSize
- bMyTraffic = true
- msg.f(unpack(msg, 1, msg.n))
- bMyTraffic = false
- Prio.nTotalSent = Prio.nTotalSent + msg.nSize
- DelMsg(msg)
- didSend = true
- end
- -- notify caller of delivery (even if we didn't send it)
- if msg.callbackFn then
- msg.callbackFn (msg.callbackArg, didSend)
- end
- -- USER CALLBACK MAY ERROR
- end
-end
-
-
-function ChatThrottleLib.OnEvent(this,event)
- -- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
- local self = ChatThrottleLib
- if event == "PLAYER_ENTERING_WORLD" then
- self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
- self.avail = 0
- end
-end
-
-
-function ChatThrottleLib.OnUpdate(this,delay)
- local self = ChatThrottleLib
-
- self.OnUpdateDelay = self.OnUpdateDelay + delay
- if self.OnUpdateDelay < 0.08 then
- return
- end
- self.OnUpdateDelay = 0
-
- self:UpdateAvail()
-
- if self.avail < 0 then
- return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
- end
-
- -- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
- local n = 0
- for prioname,Prio in pairs(self.Prio) do
- if Prio.Ring.pos or Prio.avail < 0 then
- n = n + 1
- end
- end
-
- -- Anything queued still?
- if n<1 then
- -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
- for prioname, Prio in pairs(self.Prio) do
- self.avail = self.avail + Prio.avail
- Prio.avail = 0
- end
- self.bQueueing = false
- self.Frame:Hide()
- return
- end
-
- -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
- local avail = self.avail/n
- self.avail = 0
-
- for prioname, Prio in pairs(self.Prio) do
- if Prio.Ring.pos or Prio.avail < 0 then
- Prio.avail = Prio.avail + avail
- if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
- self:Despool(Prio)
- -- Note: We might not get here if the user-supplied callback function errors out! Take care!
- end
- end
- end
-
-end
-
-
-
-
------------------------------------------------------------------------
--- Spooling logic
-
-function ChatThrottleLib:Enqueue(prioname, pipename, msg)
- local Prio = self.Prio[prioname]
- local pipe = Prio.ByName[pipename]
- if not pipe then
- self.Frame:Show()
- pipe = NewPipe()
- pipe.name = pipename
- Prio.ByName[pipename] = pipe
- Prio.Ring:Add(pipe)
- end
-
- pipe[#pipe + 1] = msg
-
- self.bQueueing = true
-end
-
-function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
- if not self or not prio or not prefix or not text or not self.Prio[prio] then
- error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
- end
- if callbackFn and type(callbackFn)~="function" then
- error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
- end
-
- local nSize = text:len()
-
- if nSize>255 then
- error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
- end
-
- nSize = nSize + self.MSG_OVERHEAD
-
- -- Check if there's room in the global available bandwidth gauge to send directly
- if not self.bQueueing and nSize < self:UpdateAvail() then
- self.avail = self.avail - nSize
- bMyTraffic = true
- _G.SendChatMessage(text, chattype, language, destination)
- bMyTraffic = false
- self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
- if callbackFn then
- callbackFn (callbackArg, true)
- end
- -- USER CALLBACK MAY ERROR
- return
- end
-
- -- Message needs to be queued
- local msg = NewMsg()
- msg.f = _G.SendChatMessage
- msg[1] = text
- msg[2] = chattype or "SAY"
- msg[3] = language
- msg[4] = destination
- msg.n = 4
- msg.nSize = nSize
- msg.callbackFn = callbackFn
- msg.callbackArg = callbackArg
-
- self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
-end
-
-
-function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
- if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
- error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
- end
- if callbackFn and type(callbackFn)~="function" then
- error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
- end
-
- local nSize = text:len();
-
- if RegisterAddonMessagePrefix then
- if nSize>255 then
- error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
- end
- else
- nSize = nSize + prefix:len() + 1
- if nSize>255 then
- error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
- end
- end
-
- nSize = nSize + self.MSG_OVERHEAD;
-
- -- Check if there's room in the global available bandwidth gauge to send directly
- if not self.bQueueing and nSize < self:UpdateAvail() then
- self.avail = self.avail - nSize
- bMyTraffic = true
- _G.SendAddonMessage(prefix, text, chattype, target)
- bMyTraffic = false
- self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
- if callbackFn then
- callbackFn (callbackArg, true)
- end
- -- USER CALLBACK MAY ERROR
- return
- end
-
- -- Message needs to be queued
- local msg = NewMsg()
- msg.f = _G.SendAddonMessage
- msg[1] = prefix
- msg[2] = text
- msg[3] = chattype
- msg[4] = target
- msg.n = (target~=nil) and 4 or 3;
- msg.nSize = nSize
- msg.callbackFn = callbackFn
- msg.callbackArg = callbackArg
-
- self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
-end
-
-
-
-
------------------------------------------------------------------------
--- Get the ball rolling!
-
-ChatThrottleLib:Init()
-
---[[ WoWBench debugging snippet
-if(WOWB_VER) then
- local function SayTimer()
- print("SAY: "..GetTime().." "..arg1)
- end
- ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
- ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
-end
-]]
-
-
+--
+-- ChatThrottleLib by Mikk
+--
+-- Manages AddOn chat output to keep player from getting kicked off.
+--
+-- ChatThrottleLib:SendChatMessage/:SendAddonMessage functions that accept
+-- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
+--
+-- Priorities get an equal share of available bandwidth when fully loaded.
+-- Communication channels are separated on extension+chattype+destination and
+-- get round-robinned. (Destination only matters for whispers and channels,
+-- obviously)
+--
+-- Will install hooks for SendChatMessage and SendAddonMessage to measure
+-- bandwidth bypassing the library and use less bandwidth itself.
+--
+--
+-- Fully embeddable library. Just copy this file into your addon directory,
+-- add it to the .toc, and it's done.
+--
+-- Can run as a standalone addon also, but, really, just embed it! :-)
+--
+-- LICENSE: ChatThrottleLib is released into the Public Domain
+--
+
+local CTL_VERSION = 23
+
+local _G = _G
+
+if _G.ChatThrottleLib then
+ if _G.ChatThrottleLib.version >= CTL_VERSION then
+ -- There's already a newer (or same) version loaded. Buh-bye.
+ return
+ elseif not _G.ChatThrottleLib.securelyHooked then
+ print("ChatThrottleLib: Warning: There's an ANCIENT ChatThrottleLib.lua (pre-wow 2.0, =v16) in it!")
+ -- ATTEMPT to unhook; this'll behave badly if someone else has hooked...
+ -- ... and if someone has securehooked, they can kiss that goodbye too... >.<
+ _G.SendChatMessage = _G.ChatThrottleLib.ORIG_SendChatMessage
+ if _G.ChatThrottleLib.ORIG_SendAddonMessage then
+ _G.SendAddonMessage = _G.ChatThrottleLib.ORIG_SendAddonMessage
+ end
+ end
+ _G.ChatThrottleLib.ORIG_SendChatMessage = nil
+ _G.ChatThrottleLib.ORIG_SendAddonMessage = nil
+end
+
+if not _G.ChatThrottleLib then
+ _G.ChatThrottleLib = {}
+end
+
+ChatThrottleLib = _G.ChatThrottleLib -- in case some addon does "local ChatThrottleLib" above us and we're copypasted (AceComm-2, sigh)
+local ChatThrottleLib = _G.ChatThrottleLib
+
+ChatThrottleLib.version = CTL_VERSION
+
+
+
+------------------ TWEAKABLES -----------------
+
+ChatThrottleLib.MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
+ChatThrottleLib.MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
+
+ChatThrottleLib.BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now.
+
+ChatThrottleLib.MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
+
+
+local setmetatable = setmetatable
+local table_remove = table.remove
+local tostring = tostring
+local GetTime = GetTime
+local math_min = math.min
+local math_max = math.max
+local next = next
+local strlen = string.len
+local GetFramerate = GetFramerate
+local strlower = string.lower
+local unpack,type,pairs,wipe = unpack,type,pairs,wipe
+local UnitInRaid,UnitInParty = UnitInRaid,UnitInParty
+
+
+-----------------------------------------------------------------------
+-- Double-linked ring implementation
+
+local Ring = {}
+local RingMeta = { __index = Ring }
+
+function Ring:New()
+ local ret = {}
+ setmetatable(ret, RingMeta)
+ return ret
+end
+
+function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
+ if self.pos then
+ obj.prev = self.pos.prev
+ obj.prev.next = obj
+ obj.next = self.pos
+ obj.next.prev = obj
+ else
+ obj.next = obj
+ obj.prev = obj
+ self.pos = obj
+ end
+end
+
+function Ring:Remove(obj)
+ obj.next.prev = obj.prev
+ obj.prev.next = obj.next
+ if self.pos == obj then
+ self.pos = obj.next
+ if self.pos == obj then
+ self.pos = nil
+ end
+ end
+end
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for pipes
+-- A pipe is a plain integer-indexed queue of messages
+-- Pipes normally live in Rings of pipes (3 rings total, one per priority)
+
+ChatThrottleLib.PipeBin = nil -- pre-v19, drastically different
+local PipeBin = setmetatable({}, {__mode="k"})
+
+local function DelPipe(pipe)
+ PipeBin[pipe] = true
+end
+
+local function NewPipe()
+ local pipe = next(PipeBin)
+ if pipe then
+ wipe(pipe)
+ PipeBin[pipe] = nil
+ return pipe
+ end
+ return {}
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Recycling bin for messages
+
+ChatThrottleLib.MsgBin = nil -- pre-v19, drastically different
+local MsgBin = setmetatable({}, {__mode="k"})
+
+local function DelMsg(msg)
+ msg[1] = nil
+ -- there's more parameters, but they're very repetetive so the string pool doesn't suffer really, and it's faster to just not delete them.
+ MsgBin[msg] = true
+end
+
+local function NewMsg()
+ local msg = next(MsgBin)
+ if msg then
+ MsgBin[msg] = nil
+ return msg
+ end
+ return {}
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:Init
+-- Initialize queues, set up frame for OnUpdate, etc
+
+
+function ChatThrottleLib:Init()
+
+ -- Set up queues
+ if not self.Prio then
+ self.Prio = {}
+ self.Prio["ALERT"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ self.Prio["NORMAL"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ self.Prio["BULK"] = { ByName = {}, Ring = Ring:New(), avail = 0 }
+ end
+
+ -- v4: total send counters per priority
+ for _, Prio in pairs(self.Prio) do
+ Prio.nTotalSent = Prio.nTotalSent or 0
+ end
+
+ if not self.avail then
+ self.avail = 0 -- v5
+ end
+ if not self.nTotalSent then
+ self.nTotalSent = 0 -- v5
+ end
+
+
+ -- Set up a frame to get OnUpdate events
+ if not self.Frame then
+ self.Frame = CreateFrame("Frame")
+ self.Frame:Hide()
+ end
+ self.Frame:SetScript("OnUpdate", self.OnUpdate)
+ self.Frame:SetScript("OnEvent", self.OnEvent) -- v11: Monitor P_E_W so we can throttle hard for a few seconds
+ self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
+ self.OnUpdateDelay = 0
+ self.LastAvailUpdate = GetTime()
+ self.HardThrottlingBeginTime = GetTime() -- v11: Throttle hard for a few seconds after startup
+
+ -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
+ if not self.securelyHooked then
+ -- Use secure hooks as of v16. Old regular hook support yanked out in v21.
+ self.securelyHooked = true
+ --SendChatMessage
+ hooksecurefunc("SendChatMessage", function(...)
+ return ChatThrottleLib.Hook_SendChatMessage(...)
+ end)
+ --SendAddonMessage
+ hooksecurefunc("SendAddonMessage", function(...)
+ return ChatThrottleLib.Hook_SendAddonMessage(...)
+ end)
+ end
+ self.nBypass = 0
+end
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
+
+local bMyTraffic = false
+
+function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination, ...)
+ if bMyTraffic then
+ return
+ end
+ local self = ChatThrottleLib
+ local size = strlen(tostring(text or "")) + strlen(tostring(destination or "")) + self.MSG_OVERHEAD
+ self.avail = self.avail - size
+ self.nBypass = self.nBypass + size -- just a statistic
+end
+function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype, destination, ...)
+ if bMyTraffic then
+ return
+ end
+ local self = ChatThrottleLib
+ local size = tostring(text or ""):len() + tostring(prefix or ""):len();
+ size = size + tostring(destination or ""):len() + self.MSG_OVERHEAD
+ self.avail = self.avail - size
+ self.nBypass = self.nBypass + size -- just a statistic
+end
+
+
+
+-----------------------------------------------------------------------
+-- ChatThrottleLib:UpdateAvail
+-- Update self.avail with how much bandwidth is currently available
+
+function ChatThrottleLib:UpdateAvail()
+ local now = GetTime()
+ local MAX_CPS = self.MAX_CPS;
+ local newavail = MAX_CPS * (now - self.LastAvailUpdate)
+ local avail = self.avail
+
+ if now - self.HardThrottlingBeginTime < 5 then
+ -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
+ avail = math_min(avail + (newavail*0.1), MAX_CPS*0.5)
+ self.bChoking = true
+ elseif GetFramerate() < self.MIN_FPS then -- GetFrameRate call takes ~0.002 secs
+ avail = math_min(MAX_CPS, avail + newavail*0.5)
+ self.bChoking = true -- just a statistic
+ else
+ avail = math_min(self.BURST, avail + newavail)
+ self.bChoking = false
+ end
+
+ avail = math_max(avail, 0-(MAX_CPS*2)) -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
+
+ self.avail = avail
+ self.LastAvailUpdate = now
+
+ return avail
+end
+
+
+-----------------------------------------------------------------------
+-- Despooling logic
+-- Reminder:
+-- - We have 3 Priorities, each containing a "Ring" construct ...
+-- - ... made up of N "Pipe"s (1 for each destination/pipename)
+-- - and each pipe contains messages
+
+function ChatThrottleLib:Despool(Prio)
+ local ring = Prio.Ring
+ while ring.pos and Prio.avail > ring.pos[1].nSize do
+ local msg = table_remove(ring.pos, 1)
+ if not ring.pos[1] then -- did we remove last msg in this pipe?
+ local pipe = Prio.Ring.pos
+ Prio.Ring:Remove(pipe)
+ Prio.ByName[pipe.name] = nil
+ DelPipe(pipe)
+ else
+ Prio.Ring.pos = Prio.Ring.pos.next
+ end
+ local didSend=false
+ local lowerDest = strlower(msg[3] or "")
+ if lowerDest == "raid" and not UnitInRaid("player") then
+ -- do nothing
+ elseif lowerDest == "party" and not UnitInParty("player") then
+ -- do nothing
+ else
+ Prio.avail = Prio.avail - msg.nSize
+ bMyTraffic = true
+ msg.f(unpack(msg, 1, msg.n))
+ bMyTraffic = false
+ Prio.nTotalSent = Prio.nTotalSent + msg.nSize
+ DelMsg(msg)
+ didSend = true
+ end
+ -- notify caller of delivery (even if we didn't send it)
+ if msg.callbackFn then
+ msg.callbackFn (msg.callbackArg, didSend)
+ end
+ -- USER CALLBACK MAY ERROR
+ end
+end
+
+
+function ChatThrottleLib.OnEvent(this,event)
+ -- v11: We know that the rate limiter is touchy after login. Assume that it's touchy after zoning, too.
+ local self = ChatThrottleLib
+ if event == "PLAYER_ENTERING_WORLD" then
+ self.HardThrottlingBeginTime = GetTime() -- Throttle hard for a few seconds after zoning
+ self.avail = 0
+ end
+end
+
+
+function ChatThrottleLib.OnUpdate(this,delay)
+ local self = ChatThrottleLib
+
+ self.OnUpdateDelay = self.OnUpdateDelay + delay
+ if self.OnUpdateDelay < 0.08 then
+ return
+ end
+ self.OnUpdateDelay = 0
+
+ self:UpdateAvail()
+
+ if self.avail < 0 then
+ return -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
+ end
+
+ -- See how many of our priorities have queued messages (we only have 3, don't worry about the loop)
+ local n = 0
+ for prioname,Prio in pairs(self.Prio) do
+ if Prio.Ring.pos or Prio.avail < 0 then
+ n = n + 1
+ end
+ end
+
+ -- Anything queued still?
+ if n<1 then
+ -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
+ for prioname, Prio in pairs(self.Prio) do
+ self.avail = self.avail + Prio.avail
+ Prio.avail = 0
+ end
+ self.bQueueing = false
+ self.Frame:Hide()
+ return
+ end
+
+ -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
+ local avail = self.avail/n
+ self.avail = 0
+
+ for prioname, Prio in pairs(self.Prio) do
+ if Prio.Ring.pos or Prio.avail < 0 then
+ Prio.avail = Prio.avail + avail
+ if Prio.Ring.pos and Prio.avail > Prio.Ring.pos[1].nSize then
+ self:Despool(Prio)
+ -- Note: We might not get here if the user-supplied callback function errors out! Take care!
+ end
+ end
+ end
+
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Spooling logic
+
+function ChatThrottleLib:Enqueue(prioname, pipename, msg)
+ local Prio = self.Prio[prioname]
+ local pipe = Prio.ByName[pipename]
+ if not pipe then
+ self.Frame:Show()
+ pipe = NewPipe()
+ pipe.name = pipename
+ Prio.ByName[pipename] = pipe
+ Prio.Ring:Add(pipe)
+ end
+
+ pipe[#pipe + 1] = msg
+
+ self.bQueueing = true
+end
+
+function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination, queueName, callbackFn, callbackArg)
+ if not self or not prio or not prefix or not text or not self.Prio[prio] then
+ error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix", "text"[, "chattype"[, "language"[, "destination"]]]', 2)
+ end
+ if callbackFn and type(callbackFn)~="function" then
+ error('ChatThrottleLib:ChatMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+ end
+
+ local nSize = text:len()
+
+ if nSize>255 then
+ error("ChatThrottleLib:SendChatMessage(): message length cannot exceed 255 bytes", 2)
+ end
+
+ nSize = nSize + self.MSG_OVERHEAD
+
+ -- Check if there's room in the global available bandwidth gauge to send directly
+ if not self.bQueueing and nSize < self:UpdateAvail() then
+ self.avail = self.avail - nSize
+ bMyTraffic = true
+ _G.SendChatMessage(text, chattype, language, destination)
+ bMyTraffic = false
+ self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+ if callbackFn then
+ callbackFn (callbackArg, true)
+ end
+ -- USER CALLBACK MAY ERROR
+ return
+ end
+
+ -- Message needs to be queued
+ local msg = NewMsg()
+ msg.f = _G.SendChatMessage
+ msg[1] = text
+ msg[2] = chattype or "SAY"
+ msg[3] = language
+ msg[4] = destination
+ msg.n = 4
+ msg.nSize = nSize
+ msg.callbackFn = callbackFn
+ msg.callbackArg = callbackArg
+
+ self:Enqueue(prio, queueName or (prefix..(chattype or "SAY")..(destination or "")), msg)
+end
+
+
+function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype, target, queueName, callbackFn, callbackArg)
+ if not self or not prio or not prefix or not text or not chattype or not self.Prio[prio] then
+ error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype"[, "target"])', 2)
+ end
+ if callbackFn and type(callbackFn)~="function" then
+ error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2)
+ end
+
+ local nSize = text:len();
+
+ if RegisterAddonMessagePrefix then
+ if nSize>255 then
+ error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2)
+ end
+ else
+ nSize = nSize + prefix:len() + 1
+ if nSize>255 then
+ error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2)
+ end
+ end
+
+ nSize = nSize + self.MSG_OVERHEAD;
+
+ -- Check if there's room in the global available bandwidth gauge to send directly
+ if not self.bQueueing and nSize < self:UpdateAvail() then
+ self.avail = self.avail - nSize
+ bMyTraffic = true
+ _G.SendAddonMessage(prefix, text, chattype, target)
+ bMyTraffic = false
+ self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize
+ if callbackFn then
+ callbackFn (callbackArg, true)
+ end
+ -- USER CALLBACK MAY ERROR
+ return
+ end
+
+ -- Message needs to be queued
+ local msg = NewMsg()
+ msg.f = _G.SendAddonMessage
+ msg[1] = prefix
+ msg[2] = text
+ msg[3] = chattype
+ msg[4] = target
+ msg.n = (target~=nil) and 4 or 3;
+ msg.nSize = nSize
+ msg.callbackFn = callbackFn
+ msg.callbackArg = callbackArg
+
+ self:Enqueue(prio, queueName or (prefix..chattype..(target or "")), msg)
+end
+
+
+
+
+-----------------------------------------------------------------------
+-- Get the ball rolling!
+
+ChatThrottleLib:Init()
+
+--[[ WoWBench debugging snippet
+if(WOWB_VER) then
+ local function SayTimer()
+ print("SAY: "..GetTime().." "..arg1)
+ end
+ ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer)
+ ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY")
+end
+]]
+
+
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.lua
index 4cd24457b..0567a6563 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.lua
@@ -1,250 +1,250 @@
---- **AceConsole-3.0** provides registration facilities for slash commands.
--- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
--- to your addons individual needs.
---
--- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
--- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
--- and can be accessed directly, without having to explicitly call AceConsole itself.\\
--- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
--- make into AceConsole.
--- @class file
--- @name AceConsole-3.0
--- @release $Id: AceConsole-3.0.lua 1143 2016-07-11 08:52:03Z nevcairiel $
-local MAJOR,MINOR = "AceConsole-3.0", 7
-
-local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not AceConsole then return end -- No upgrade needed
-
-AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
-AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
-AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
-
--- Lua APIs
-local tconcat, tostring, select = table.concat, tostring, select
-local type, pairs, error = type, pairs, error
-local format, strfind, strsub = string.format, string.find, string.sub
-local max = math.max
-
--- WoW APIs
-local _G = _G
-
--- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
--- List them here for Mikk's FindGlobals script
--- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
-
-local tmp={}
-local function Print(self,frame,...)
- local n=0
- if self ~= AceConsole then
- n=n+1
- tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
- end
- for i=1, select("#", ...) do
- n=n+1
- tmp[n] = tostring(select(i, ...))
- end
- frame:AddMessage( tconcat(tmp," ",1,n) )
-end
-
---- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
--- @paramsig [chatframe ,] ...
--- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
--- @param ... List of any values to be printed
-function AceConsole:Print(...)
- local frame = ...
- if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
- return Print(self, frame, select(2,...))
- else
- return Print(self, DEFAULT_CHAT_FRAME, ...)
- end
-end
-
-
---- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
--- @paramsig [chatframe ,] "format"[, ...]
--- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
--- @param format Format string - same syntax as standard Lua format()
--- @param ... Arguments to the format string
-function AceConsole:Printf(...)
- local frame = ...
- if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
- return Print(self, frame, format(select(2,...)))
- else
- return Print(self, DEFAULT_CHAT_FRAME, format(...))
- end
-end
-
-
-
-
---- Register a simple chat command
--- @param command Chat command to be registered WITHOUT leading "/"
--- @param func Function to call when the slash command is being used (funcref or methodname)
--- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
-function AceConsole:RegisterChatCommand( command, func, persist )
- if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
-
- if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
-
- local name = "ACECONSOLE_"..command:upper()
-
- if type( func ) == "string" then
- SlashCmdList[name] = function(input, editBox)
- self[func](self, input, editBox)
- end
- else
- SlashCmdList[name] = func
- end
- _G["SLASH_"..name.."1"] = "/"..command:lower()
- AceConsole.commands[command] = name
- -- non-persisting commands are registered for enabling disabling
- if not persist then
- if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
- AceConsole.weakcommands[self][command] = func
- end
- return true
-end
-
---- Unregister a chatcommand
--- @param command Chat command to be unregistered WITHOUT leading "/"
-function AceConsole:UnregisterChatCommand( command )
- local name = AceConsole.commands[command]
- if name then
- SlashCmdList[name] = nil
- _G["SLASH_" .. name .. "1"] = nil
- hash_SlashCmdList["/" .. command:upper()] = nil
- AceConsole.commands[command] = nil
- end
-end
-
---- Get an iterator over all Chat Commands registered with AceConsole
--- @return Iterator (pairs) over all commands
-function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
-
-
-local function nils(n, ...)
- if n>1 then
- return nil, nils(n-1, ...)
- elseif n==1 then
- return nil, ...
- else
- return ...
- end
-end
-
-
---- Retreive one or more space-separated arguments from a string.
--- Treats quoted strings and itemlinks as non-spaced.
--- @param str The raw argument string
--- @param numargs How many arguments to get (default 1)
--- @param startpos Where in the string to start scanning (default 1)
--- @return Returns arg1, arg2, ..., nextposition\\
--- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
-function AceConsole:GetArgs(str, numargs, startpos)
- numargs = numargs or 1
- startpos = max(startpos or 1, 1)
-
- local pos=startpos
-
- -- find start of new arg
- pos = strfind(str, "[^ ]", pos)
- if not pos then -- whoops, end of string
- return nils(numargs, 1e9)
- end
-
- if numargs<1 then
- return pos
- end
-
- -- quoted or space separated? find out which pattern to use
- local delim_or_pipe
- local ch = strsub(str, pos, pos)
- if ch=='"' then
- pos = pos + 1
- delim_or_pipe='([|"])'
- elseif ch=="'" then
- pos = pos + 1
- delim_or_pipe="([|'])"
- else
- delim_or_pipe="([| ])"
- end
-
- startpos = pos
-
- while true do
- -- find delimiter or hyperlink
- local ch,_
- pos,_,ch = strfind(str, delim_or_pipe, pos)
-
- if not pos then break end
-
- if ch=="|" then
- -- some kind of escape
-
- if strsub(str,pos,pos+1)=="|H" then
- -- It's a |H....|hhyper link!|h
- pos=strfind(str, "|h", pos+2) -- first |h
- if not pos then break end
-
- pos=strfind(str, "|h", pos+2) -- second |h
- if not pos then break end
- elseif strsub(str,pos, pos+1) == "|T" then
- -- It's a |T....|t texture
- pos=strfind(str, "|t", pos+2)
- if not pos then break end
- end
-
- pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
-
- else
- -- found delimiter, done with this arg
- return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
- end
-
- end
-
- -- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
- return strsub(str, startpos), nils(numargs-1, 1e9)
-end
-
-
---- embedding and embed handling
-
-local mixins = {
- "Print",
- "Printf",
- "RegisterChatCommand",
- "UnregisterChatCommand",
- "GetArgs",
-}
-
--- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
--- @param target target object to embed AceBucket in
-function AceConsole:Embed( target )
- for k, v in pairs( mixins ) do
- target[v] = self[v]
- end
- self.embeds[target] = true
- return target
-end
-
-function AceConsole:OnEmbedEnable( target )
- if AceConsole.weakcommands[target] then
- for command, func in pairs( AceConsole.weakcommands[target] ) do
- target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
- end
- end
-end
-
-function AceConsole:OnEmbedDisable( target )
- if AceConsole.weakcommands[target] then
- for command, func in pairs( AceConsole.weakcommands[target] ) do
- target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
- end
- end
-end
-
-for addon in pairs(AceConsole.embeds) do
- AceConsole:Embed(addon)
-end
+--- **AceConsole-3.0** provides registration facilities for slash commands.
+-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
+-- to your addons individual needs.
+--
+-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
+-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceConsole.
+-- @class file
+-- @name AceConsole-3.0
+-- @release $Id: AceConsole-3.0.lua 1143 2016-07-11 08:52:03Z nevcairiel $
+local MAJOR,MINOR = "AceConsole-3.0", 7
+
+local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceConsole then return end -- No upgrade needed
+
+AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
+AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
+AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
+
+-- Lua APIs
+local tconcat, tostring, select = table.concat, tostring, select
+local type, pairs, error = type, pairs, error
+local format, strfind, strsub = string.format, string.find, string.sub
+local max = math.max
+
+-- WoW APIs
+local _G = _G
+
+-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
+-- List them here for Mikk's FindGlobals script
+-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
+
+local tmp={}
+local function Print(self,frame,...)
+ local n=0
+ if self ~= AceConsole then
+ n=n+1
+ tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
+ end
+ for i=1, select("#", ...) do
+ n=n+1
+ tmp[n] = tostring(select(i, ...))
+ end
+ frame:AddMessage( tconcat(tmp," ",1,n) )
+end
+
+--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] ...
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param ... List of any values to be printed
+function AceConsole:Print(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, select(2,...))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, ...)
+ end
+end
+
+
+--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
+-- @paramsig [chatframe ,] "format"[, ...]
+-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
+-- @param format Format string - same syntax as standard Lua format()
+-- @param ... Arguments to the format string
+function AceConsole:Printf(...)
+ local frame = ...
+ if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
+ return Print(self, frame, format(select(2,...)))
+ else
+ return Print(self, DEFAULT_CHAT_FRAME, format(...))
+ end
+end
+
+
+
+
+--- Register a simple chat command
+-- @param command Chat command to be registered WITHOUT leading "/"
+-- @param func Function to call when the slash command is being used (funcref or methodname)
+-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
+function AceConsole:RegisterChatCommand( command, func, persist )
+ if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
+
+ if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
+
+ local name = "ACECONSOLE_"..command:upper()
+
+ if type( func ) == "string" then
+ SlashCmdList[name] = function(input, editBox)
+ self[func](self, input, editBox)
+ end
+ else
+ SlashCmdList[name] = func
+ end
+ _G["SLASH_"..name.."1"] = "/"..command:lower()
+ AceConsole.commands[command] = name
+ -- non-persisting commands are registered for enabling disabling
+ if not persist then
+ if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
+ AceConsole.weakcommands[self][command] = func
+ end
+ return true
+end
+
+--- Unregister a chatcommand
+-- @param command Chat command to be unregistered WITHOUT leading "/"
+function AceConsole:UnregisterChatCommand( command )
+ local name = AceConsole.commands[command]
+ if name then
+ SlashCmdList[name] = nil
+ _G["SLASH_" .. name .. "1"] = nil
+ hash_SlashCmdList["/" .. command:upper()] = nil
+ AceConsole.commands[command] = nil
+ end
+end
+
+--- Get an iterator over all Chat Commands registered with AceConsole
+-- @return Iterator (pairs) over all commands
+function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
+
+
+local function nils(n, ...)
+ if n>1 then
+ return nil, nils(n-1, ...)
+ elseif n==1 then
+ return nil, ...
+ else
+ return ...
+ end
+end
+
+
+--- Retreive one or more space-separated arguments from a string.
+-- Treats quoted strings and itemlinks as non-spaced.
+-- @param str The raw argument string
+-- @param numargs How many arguments to get (default 1)
+-- @param startpos Where in the string to start scanning (default 1)
+-- @return Returns arg1, arg2, ..., nextposition\\
+-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
+function AceConsole:GetArgs(str, numargs, startpos)
+ numargs = numargs or 1
+ startpos = max(startpos or 1, 1)
+
+ local pos=startpos
+
+ -- find start of new arg
+ pos = strfind(str, "[^ ]", pos)
+ if not pos then -- whoops, end of string
+ return nils(numargs, 1e9)
+ end
+
+ if numargs<1 then
+ return pos
+ end
+
+ -- quoted or space separated? find out which pattern to use
+ local delim_or_pipe
+ local ch = strsub(str, pos, pos)
+ if ch=='"' then
+ pos = pos + 1
+ delim_or_pipe='([|"])'
+ elseif ch=="'" then
+ pos = pos + 1
+ delim_or_pipe="([|'])"
+ else
+ delim_or_pipe="([| ])"
+ end
+
+ startpos = pos
+
+ while true do
+ -- find delimiter or hyperlink
+ local ch,_
+ pos,_,ch = strfind(str, delim_or_pipe, pos)
+
+ if not pos then break end
+
+ if ch=="|" then
+ -- some kind of escape
+
+ if strsub(str,pos,pos+1)=="|H" then
+ -- It's a |H....|hhyper link!|h
+ pos=strfind(str, "|h", pos+2) -- first |h
+ if not pos then break end
+
+ pos=strfind(str, "|h", pos+2) -- second |h
+ if not pos then break end
+ elseif strsub(str,pos, pos+1) == "|T" then
+ -- It's a |T....|t texture
+ pos=strfind(str, "|t", pos+2)
+ if not pos then break end
+ end
+
+ pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
+
+ else
+ -- found delimiter, done with this arg
+ return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
+ end
+
+ end
+
+ -- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
+ return strsub(str, startpos), nils(numargs-1, 1e9)
+end
+
+
+--- embedding and embed handling
+
+local mixins = {
+ "Print",
+ "Printf",
+ "RegisterChatCommand",
+ "UnregisterChatCommand",
+ "GetArgs",
+}
+
+-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceBucket in
+function AceConsole:Embed( target )
+ for k, v in pairs( mixins ) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+function AceConsole:OnEmbedEnable( target )
+ if AceConsole.weakcommands[target] then
+ for command, func in pairs( AceConsole.weakcommands[target] ) do
+ target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
+ end
+ end
+end
+
+function AceConsole:OnEmbedDisable( target )
+ if AceConsole.weakcommands[target] then
+ for command, func in pairs( AceConsole.weakcommands[target] ) do
+ target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
+ end
+ end
+end
+
+for addon in pairs(AceConsole.embeds) do
+ AceConsole:Embed(addon)
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.xml
index 35f6b5728..371278a62 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceConsole-3.0/AceConsole-3.0.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceConsole-3.0/AceConsole-3.0.xml
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.lua
index 360a39344..bbf55c299 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.lua
@@ -1,126 +1,126 @@
---- AceEvent-3.0 provides event registration and secure dispatching.
--- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
--- CallbackHandler, and dispatches all game events or addon message to the registrees.
---
--- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent:Embed(MyAddon) or by
--- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
--- and can be accessed directly, without having to explicitly call AceEvent itself.\\
--- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
--- make into AceEvent.
--- @class file
--- @name AceEvent-3.0
--- @release $Id: AceEvent-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
-local CallbackHandler = LibStub("CallbackHandler-1.0")
-
-local MAJOR, MINOR = "AceEvent-3.0", 4
-local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not AceEvent then return end
-
--- Lua APIs
-local pairs = pairs
-
-AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
-AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
-
--- APIs and registry for blizzard events, using CallbackHandler lib
-if not AceEvent.events then
- AceEvent.events = CallbackHandler:New(AceEvent,
- "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
-end
-
-function AceEvent.events:OnUsed(target, eventname)
- AceEvent.frame:RegisterEvent(eventname)
-end
-
-function AceEvent.events:OnUnused(target, eventname)
- AceEvent.frame:UnregisterEvent(eventname)
-end
-
-
--- APIs and registry for IPC messages, using CallbackHandler lib
-if not AceEvent.messages then
- AceEvent.messages = CallbackHandler:New(AceEvent,
- "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
- )
- AceEvent.SendMessage = AceEvent.messages.Fire
-end
-
---- embedding and embed handling
-local mixins = {
- "RegisterEvent", "UnregisterEvent",
- "RegisterMessage", "UnregisterMessage",
- "SendMessage",
- "UnregisterAllEvents", "UnregisterAllMessages",
-}
-
---- Register for a Blizzard Event.
--- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
--- Any arguments to the event will be passed on after that.
--- @name AceEvent:RegisterEvent
--- @class function
--- @paramsig event[, callback [, arg]]
--- @param event The event to register for
--- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
--- @param arg An optional argument to pass to the callback function
-
---- Unregister an event.
--- @name AceEvent:UnregisterEvent
--- @class function
--- @paramsig event
--- @param event The event to unregister
-
---- Register for a custom AceEvent-internal message.
--- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
--- Any arguments to the event will be passed on after that.
--- @name AceEvent:RegisterMessage
--- @class function
--- @paramsig message[, callback [, arg]]
--- @param message The message to register for
--- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
--- @param arg An optional argument to pass to the callback function
-
---- Unregister a message
--- @name AceEvent:UnregisterMessage
--- @class function
--- @paramsig message
--- @param message The message to unregister
-
---- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
--- @name AceEvent:SendMessage
--- @class function
--- @paramsig message, ...
--- @param message The message to send
--- @param ... Any arguments to the message
-
-
--- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
--- @param target target object to embed AceEvent in
-function AceEvent:Embed(target)
- for k, v in pairs(mixins) do
- target[v] = self[v]
- end
- self.embeds[target] = true
- return target
-end
-
--- AceEvent:OnEmbedDisable( target )
--- target (object) - target object that is being disabled
---
--- Unregister all events messages etc when the target disables.
--- this method should be called by the target manually or by an addon framework
-function AceEvent:OnEmbedDisable(target)
- target:UnregisterAllEvents()
- target:UnregisterAllMessages()
-end
-
--- Script to fire blizzard events into the event listeners
-local events = AceEvent.events
-AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
- events:Fire(event, ...)
-end)
-
---- Finally: upgrade our old embeds
-for target, v in pairs(AceEvent.embeds) do
- AceEvent:Embed(target)
-end
+--- AceEvent-3.0 provides event registration and secure dispatching.
+-- All dispatching is done using **CallbackHandler-1.0**. AceEvent is a simple wrapper around
+-- CallbackHandler, and dispatches all game events or addon message to the registrees.
+--
+-- **AceEvent-3.0** can be embeded into your addon, either explicitly by calling AceEvent:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceEvent itself.\\
+-- It is recommended to embed AceEvent, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceEvent.
+-- @class file
+-- @name AceEvent-3.0
+-- @release $Id: AceEvent-3.0.lua 1161 2017-08-12 14:30:16Z funkydude $
+local CallbackHandler = LibStub("CallbackHandler-1.0")
+
+local MAJOR, MINOR = "AceEvent-3.0", 4
+local AceEvent = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceEvent then return end
+
+-- Lua APIs
+local pairs = pairs
+
+AceEvent.frame = AceEvent.frame or CreateFrame("Frame", "AceEvent30Frame") -- our event frame
+AceEvent.embeds = AceEvent.embeds or {} -- what objects embed this lib
+
+-- APIs and registry for blizzard events, using CallbackHandler lib
+if not AceEvent.events then
+ AceEvent.events = CallbackHandler:New(AceEvent,
+ "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents")
+end
+
+function AceEvent.events:OnUsed(target, eventname)
+ AceEvent.frame:RegisterEvent(eventname)
+end
+
+function AceEvent.events:OnUnused(target, eventname)
+ AceEvent.frame:UnregisterEvent(eventname)
+end
+
+
+-- APIs and registry for IPC messages, using CallbackHandler lib
+if not AceEvent.messages then
+ AceEvent.messages = CallbackHandler:New(AceEvent,
+ "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages"
+ )
+ AceEvent.SendMessage = AceEvent.messages.Fire
+end
+
+--- embedding and embed handling
+local mixins = {
+ "RegisterEvent", "UnregisterEvent",
+ "RegisterMessage", "UnregisterMessage",
+ "SendMessage",
+ "UnregisterAllEvents", "UnregisterAllMessages",
+}
+
+--- Register for a Blizzard Event.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterEvent
+-- @class function
+-- @paramsig event[, callback [, arg]]
+-- @param event The event to register for
+-- @param callback The callback function to call when the event is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister an event.
+-- @name AceEvent:UnregisterEvent
+-- @class function
+-- @paramsig event
+-- @param event The event to unregister
+
+--- Register for a custom AceEvent-internal message.
+-- The callback will be called with the optional `arg` as the first argument (if supplied), and the event name as the second (or first, if no arg was supplied)
+-- Any arguments to the event will be passed on after that.
+-- @name AceEvent:RegisterMessage
+-- @class function
+-- @paramsig message[, callback [, arg]]
+-- @param message The message to register for
+-- @param callback The callback function to call when the message is triggered (funcref or method, defaults to a method with the event name)
+-- @param arg An optional argument to pass to the callback function
+
+--- Unregister a message
+-- @name AceEvent:UnregisterMessage
+-- @class function
+-- @paramsig message
+-- @param message The message to unregister
+
+--- Send a message over the AceEvent-3.0 internal message system to other addons registered for this message.
+-- @name AceEvent:SendMessage
+-- @class function
+-- @paramsig message, ...
+-- @param message The message to send
+-- @param ... Any arguments to the message
+
+
+-- Embeds AceEvent into the target object making the functions from the mixins list available on target:..
+-- @param target target object to embed AceEvent in
+function AceEvent:Embed(target)
+ for k, v in pairs(mixins) do
+ target[v] = self[v]
+ end
+ self.embeds[target] = true
+ return target
+end
+
+-- AceEvent:OnEmbedDisable( target )
+-- target (object) - target object that is being disabled
+--
+-- Unregister all events messages etc when the target disables.
+-- this method should be called by the target manually or by an addon framework
+function AceEvent:OnEmbedDisable(target)
+ target:UnregisterAllEvents()
+ target:UnregisterAllMessages()
+end
+
+-- Script to fire blizzard events into the event listeners
+local events = AceEvent.events
+AceEvent.frame:SetScript("OnEvent", function(this, event, ...)
+ events:Fire(event, ...)
+end)
+
+--- Finally: upgrade our old embeds
+for target, v in pairs(AceEvent.embeds) do
+ AceEvent:Embed(target)
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.xml
index c1c5214dc..dbe7d9933 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceEvent-3.0/AceEvent-3.0.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceEvent-3.0/AceEvent-3.0.xml
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.lua
index 072ff8eaa..8ba6b3c9d 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.lua
@@ -1,276 +1,276 @@
---- **AceTimer-3.0** provides a central facility for registering timers.
--- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
--- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
--- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
--- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
--- restricts us to.
---
--- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
--- need to cancel the timer you just registered.
---
--- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
--- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
--- and can be accessed directly, without having to explicitly call AceTimer itself.\\
--- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
--- make into AceTimer.
--- @class file
--- @name AceTimer-3.0
--- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
-
-local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
-local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
-
-if not AceTimer then return end -- No upgrade needed
-AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
-local activeTimers = AceTimer.activeTimers -- Upvalue our private data
-
--- Lua APIs
-local type, unpack, next, error, select = type, unpack, next, error, select
--- WoW APIs
-local GetTime, C_TimerAfter = GetTime, C_Timer.After
-
-local function new(self, loop, func, delay, ...)
- if delay < 0.01 then
- delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
- end
-
- local timer = {...}
- timer.object = self
- timer.func = func
- timer.looping = loop
- timer.argsCount = select("#", ...)
- timer.delay = delay
- timer.ends = GetTime() + delay
-
- activeTimers[timer] = timer
-
- -- Create new timer closure to wrap the "timer" object
- timer.callback = function()
- if not timer.cancelled then
- if type(timer.func) == "string" then
- -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
- -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
- timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
- else
- timer.func(unpack(timer, 1, timer.argsCount))
- end
-
- if timer.looping and not timer.cancelled then
- -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
- -- due to fps differences
- local time = GetTime()
- local delay = timer.delay - (time - timer.ends)
- -- Ensure the delay doesn't go below the threshold
- if delay < 0.01 then delay = 0.01 end
- C_TimerAfter(delay, timer.callback)
- timer.ends = time + delay
- else
- activeTimers[timer.handle or timer] = nil
- end
- end
- end
-
- C_TimerAfter(delay, timer.callback)
- return timer
-end
-
---- Schedule a new one-shot timer.
--- The timer will fire once in `delay` seconds, unless canceled before.
--- @param callback Callback function for the timer pulse (funcref or method name).
--- @param delay Delay for the timer, in seconds.
--- @param ... An optional, unlimited amount of arguments to pass to the callback function.
--- @usage
--- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
---
--- function MyAddOn:OnEnable()
--- self:ScheduleTimer("TimerFeedback", 5)
--- end
---
--- function MyAddOn:TimerFeedback()
--- print("5 seconds passed")
--- end
-function AceTimer:ScheduleTimer(func, delay, ...)
- if not func or not delay then
- error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
- end
- if type(func) == "string" then
- if type(self) ~= "table" then
- error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
- elseif not self[func] then
- error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
- end
- end
- return new(self, nil, func, delay, ...)
-end
-
---- Schedule a repeating timer.
--- The timer will fire every `delay` seconds, until canceled.
--- @param callback Callback function for the timer pulse (funcref or method name).
--- @param delay Delay for the timer, in seconds.
--- @param ... An optional, unlimited amount of arguments to pass to the callback function.
--- @usage
--- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
---
--- function MyAddOn:OnEnable()
--- self.timerCount = 0
--- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
--- end
---
--- function MyAddOn:TimerFeedback()
--- self.timerCount = self.timerCount + 1
--- print(("%d seconds passed"):format(5 * self.timerCount))
--- -- run 30 seconds in total
--- if self.timerCount == 6 then
--- self:CancelTimer(self.testTimer)
--- end
--- end
-function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
- if not func or not delay then
- error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
- end
- if type(func) == "string" then
- if type(self) ~= "table" then
- error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
- elseif not self[func] then
- error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
- end
- end
- return new(self, true, func, delay, ...)
-end
-
---- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
--- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
--- and the timer has not fired yet or was canceled before.
--- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
-function AceTimer:CancelTimer(id)
- local timer = activeTimers[id]
-
- if not timer then
- return false
- else
- timer.cancelled = true
- activeTimers[id] = nil
- return true
- end
-end
-
---- Cancels all timers registered to the current addon object ('self')
-function AceTimer:CancelAllTimers()
- for k,v in pairs(activeTimers) do
- if v.object == self then
- AceTimer.CancelTimer(self, k)
- end
- end
-end
-
---- Returns the time left for a timer with the given id, registered by the current addon object ('self').
--- This function will return 0 when the id is invalid.
--- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
--- @return The time left on the timer.
-function AceTimer:TimeLeft(id)
- local timer = activeTimers[id]
- if not timer then
- return 0
- else
- return timer.ends - GetTime()
- end
-end
-
-
--- ---------------------------------------------------------------------
--- Upgrading
-
--- Upgrade from old hash-bucket based timers to C_Timer.After timers.
-if oldminor and oldminor < 10 then
- -- disable old timer logic
- AceTimer.frame:SetScript("OnUpdate", nil)
- AceTimer.frame:SetScript("OnEvent", nil)
- AceTimer.frame:UnregisterAllEvents()
- -- convert timers
- for object,timers in pairs(AceTimer.selfs) do
- for handle,timer in pairs(timers) do
- if type(timer) == "table" and timer.callback then
- local newTimer
- if timer.delay then
- newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
- else
- newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
- end
- -- Use the old handle for old timers
- activeTimers[newTimer] = nil
- activeTimers[handle] = newTimer
- newTimer.handle = handle
- end
- end
- end
- AceTimer.selfs = nil
- AceTimer.hash = nil
- AceTimer.debug = nil
-elseif oldminor and oldminor < 17 then
- -- Upgrade from old animation based timers to C_Timer.After timers.
- AceTimer.inactiveTimers = nil
- AceTimer.frame = nil
- local oldTimers = AceTimer.activeTimers
- -- Clear old timer table and update upvalue
- AceTimer.activeTimers = {}
- activeTimers = AceTimer.activeTimers
- for handle, timer in pairs(oldTimers) do
- local newTimer
- -- Stop the old timer animation
- local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
- timer:GetParent():Stop()
- if timer.looping then
- newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
- else
- newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
- end
- -- Use the old handle for old timers
- activeTimers[newTimer] = nil
- activeTimers[handle] = newTimer
- newTimer.handle = handle
- end
-
- -- Migrate transitional handles
- if oldminor < 13 and AceTimer.hashCompatTable then
- for handle, id in pairs(AceTimer.hashCompatTable) do
- local t = activeTimers[id]
- if t then
- activeTimers[id] = nil
- activeTimers[handle] = t
- t.handle = handle
- end
- end
- AceTimer.hashCompatTable = nil
- end
-end
-
--- ---------------------------------------------------------------------
--- Embed handling
-
-AceTimer.embeds = AceTimer.embeds or {}
-
-local mixins = {
- "ScheduleTimer", "ScheduleRepeatingTimer",
- "CancelTimer", "CancelAllTimers",
- "TimeLeft"
-}
-
-function AceTimer:Embed(target)
- AceTimer.embeds[target] = true
- for _,v in pairs(mixins) do
- target[v] = AceTimer[v]
- end
- return target
-end
-
--- AceTimer:OnEmbedDisable(target)
--- target (object) - target object that AceTimer is embedded in.
---
--- cancel all timers registered for the object
-function AceTimer:OnEmbedDisable(target)
- target:CancelAllTimers()
-end
-
-for addon in pairs(AceTimer.embeds) do
- AceTimer:Embed(addon)
-end
+--- **AceTimer-3.0** provides a central facility for registering timers.
+-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
+-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
+-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
+-- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API
+-- restricts us to.
+--
+-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
+-- need to cancel the timer you just registered.
+--
+-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
+-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
+-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
+-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
+-- make into AceTimer.
+-- @class file
+-- @name AceTimer-3.0
+-- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $
+
+local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes
+local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not AceTimer then return end -- No upgrade needed
+AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
+local activeTimers = AceTimer.activeTimers -- Upvalue our private data
+
+-- Lua APIs
+local type, unpack, next, error, select = type, unpack, next, error, select
+-- WoW APIs
+local GetTime, C_TimerAfter = GetTime, C_Timer.After
+
+local function new(self, loop, func, delay, ...)
+ if delay < 0.01 then
+ delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us
+ end
+
+ local timer = {...}
+ timer.object = self
+ timer.func = func
+ timer.looping = loop
+ timer.argsCount = select("#", ...)
+ timer.delay = delay
+ timer.ends = GetTime() + delay
+
+ activeTimers[timer] = timer
+
+ -- Create new timer closure to wrap the "timer" object
+ timer.callback = function()
+ if not timer.cancelled then
+ if type(timer.func) == "string" then
+ -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
+ -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
+ timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount))
+ else
+ timer.func(unpack(timer, 1, timer.argsCount))
+ end
+
+ if timer.looping and not timer.cancelled then
+ -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
+ -- due to fps differences
+ local time = GetTime()
+ local delay = timer.delay - (time - timer.ends)
+ -- Ensure the delay doesn't go below the threshold
+ if delay < 0.01 then delay = 0.01 end
+ C_TimerAfter(delay, timer.callback)
+ timer.ends = time + delay
+ else
+ activeTimers[timer.handle or timer] = nil
+ end
+ end
+ end
+
+ C_TimerAfter(delay, timer.callback)
+ return timer
+end
+
+--- Schedule a new one-shot timer.
+-- The timer will fire once in `delay` seconds, unless canceled before.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self:ScheduleTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- print("5 seconds passed")
+-- end
+function AceTimer:ScheduleTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, nil, func, delay, ...)
+end
+
+--- Schedule a repeating timer.
+-- The timer will fire every `delay` seconds, until canceled.
+-- @param callback Callback function for the timer pulse (funcref or method name).
+-- @param delay Delay for the timer, in seconds.
+-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
+-- @usage
+-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
+--
+-- function MyAddOn:OnEnable()
+-- self.timerCount = 0
+-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
+-- end
+--
+-- function MyAddOn:TimerFeedback()
+-- self.timerCount = self.timerCount + 1
+-- print(("%d seconds passed"):format(5 * self.timerCount))
+-- -- run 30 seconds in total
+-- if self.timerCount == 6 then
+-- self:CancelTimer(self.testTimer)
+-- end
+-- end
+function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
+ if not func or not delay then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
+ end
+ if type(func) == "string" then
+ if type(self) ~= "table" then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
+ elseif not self[func] then
+ error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
+ end
+ end
+ return new(self, true, func, delay, ...)
+end
+
+--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
+-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
+-- and the timer has not fired yet or was canceled before.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+function AceTimer:CancelTimer(id)
+ local timer = activeTimers[id]
+
+ if not timer then
+ return false
+ else
+ timer.cancelled = true
+ activeTimers[id] = nil
+ return true
+ end
+end
+
+--- Cancels all timers registered to the current addon object ('self')
+function AceTimer:CancelAllTimers()
+ for k,v in pairs(activeTimers) do
+ if v.object == self then
+ AceTimer.CancelTimer(self, k)
+ end
+ end
+end
+
+--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
+-- This function will return 0 when the id is invalid.
+-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
+-- @return The time left on the timer.
+function AceTimer:TimeLeft(id)
+ local timer = activeTimers[id]
+ if not timer then
+ return 0
+ else
+ return timer.ends - GetTime()
+ end
+end
+
+
+-- ---------------------------------------------------------------------
+-- Upgrading
+
+-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
+if oldminor and oldminor < 10 then
+ -- disable old timer logic
+ AceTimer.frame:SetScript("OnUpdate", nil)
+ AceTimer.frame:SetScript("OnEvent", nil)
+ AceTimer.frame:UnregisterAllEvents()
+ -- convert timers
+ for object,timers in pairs(AceTimer.selfs) do
+ for handle,timer in pairs(timers) do
+ if type(timer) == "table" and timer.callback then
+ local newTimer
+ if timer.delay then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+ end
+ end
+ AceTimer.selfs = nil
+ AceTimer.hash = nil
+ AceTimer.debug = nil
+elseif oldminor and oldminor < 17 then
+ -- Upgrade from old animation based timers to C_Timer.After timers.
+ AceTimer.inactiveTimers = nil
+ AceTimer.frame = nil
+ local oldTimers = AceTimer.activeTimers
+ -- Clear old timer table and update upvalue
+ AceTimer.activeTimers = {}
+ activeTimers = AceTimer.activeTimers
+ for handle, timer in pairs(oldTimers) do
+ local newTimer
+ -- Stop the old timer animation
+ local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
+ timer:GetParent():Stop()
+ if timer.looping then
+ newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
+ else
+ newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
+ end
+ -- Use the old handle for old timers
+ activeTimers[newTimer] = nil
+ activeTimers[handle] = newTimer
+ newTimer.handle = handle
+ end
+
+ -- Migrate transitional handles
+ if oldminor < 13 and AceTimer.hashCompatTable then
+ for handle, id in pairs(AceTimer.hashCompatTable) do
+ local t = activeTimers[id]
+ if t then
+ activeTimers[id] = nil
+ activeTimers[handle] = t
+ t.handle = handle
+ end
+ end
+ AceTimer.hashCompatTable = nil
+ end
+end
+
+-- ---------------------------------------------------------------------
+-- Embed handling
+
+AceTimer.embeds = AceTimer.embeds or {}
+
+local mixins = {
+ "ScheduleTimer", "ScheduleRepeatingTimer",
+ "CancelTimer", "CancelAllTimers",
+ "TimeLeft"
+}
+
+function AceTimer:Embed(target)
+ AceTimer.embeds[target] = true
+ for _,v in pairs(mixins) do
+ target[v] = AceTimer[v]
+ end
+ return target
+end
+
+-- AceTimer:OnEmbedDisable(target)
+-- target (object) - target object that AceTimer is embedded in.
+--
+-- cancel all timers registered for the object
+function AceTimer:OnEmbedDisable(target)
+ target:CancelAllTimers()
+end
+
+for addon in pairs(AceTimer.embeds) do
+ AceTimer:Embed(addon)
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.xml
index 77fb196c7..be43d8ead 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/AceTimer-3.0/AceTimer-3.0.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/AceTimer-3.0/AceTimer-3.0.xml
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/LibStub/LibStub.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/LibStub/LibStub.lua
similarity index 97%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/LibStub/LibStub.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/LibStub/LibStub.lua
index cfc97de71..0a41ac04d 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/LibStub/LibStub.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/LibStub/LibStub.lua
@@ -1,30 +1,30 @@
--- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/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]
-
-if not LibStub or LibStub.minor < LIBSTUB_MINOR then
- LibStub = LibStub or {libs = {}, minors = {} }
- _G[LIBSTUB_MAJOR] = LibStub
- LibStub.minor = LIBSTUB_MINOR
-
- 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
-
- 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
-
- function LibStub:IterateLibraries() return pairs(self.libs) end
- setmetatable(LibStub, { __call = LibStub.GetLibrary })
-end
+-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/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]
+
+if not LibStub or LibStub.minor < LIBSTUB_MINOR then
+ LibStub = LibStub or {libs = {}, minors = {} }
+ _G[LIBSTUB_MAJOR] = LibStub
+ LibStub.minor = LIBSTUB_MINOR
+
+ 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
+
+ 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
+
+ function LibStub:IterateLibraries() return pairs(self.libs) end
+ setmetatable(LibStub, { __call = LibStub.GetLibrary })
+end
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/json.lua b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/json.lua
similarity index 95%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/json.lua
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/json.lua
index fd25af5d2..8bdc71440 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/Libs/json.lua
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/Libs/json.lua
@@ -1,382 +1,382 @@
-local json = LibStub:NewLibrary("json", 1)
-if not json then return end
-
---
--- json.lua
---
--- Copyright (c) 2015 rxi
---
--- This library is free software; you can redistribute it and/or modify it
--- under the terms of the MIT license. See LICENSE for details.
---
-
-
--------------------------------------------------------------------------------
--- Encode
--------------------------------------------------------------------------------
-
-local encode
-
-local escape_char_map = {
- [ "\\" ] = "\\\\",
- [ "\"" ] = "\\\"",
- [ "\b" ] = "\\b",
- [ "\f" ] = "\\f",
- [ "\n" ] = "\\n",
- [ "\r" ] = "\\r",
- [ "\t" ] = "\\t",
-}
-
-local escape_char_map_inv = { [ "\\/" ] = "/" }
-for k, v in pairs(escape_char_map) do
- escape_char_map_inv[v] = k
-end
-
-
-local function escape_char(c)
- return escape_char_map[c] or string.format("\\u%04x", c:byte())
-end
-
-
-local function encode_nil(val)
- return "null"
-end
-
-
-local function encode_table(val, stack)
- local res = {}
- stack = stack or {}
-
- -- Circular reference?
- if stack[val] then error("circular reference") end
-
- stack[val] = true
-
- if val[1] ~= nil or next(val) == nil then
- -- Treat as array -- check keys are valid and it is not sparse
- local n = 0
- for k in pairs(val) do
- if type(k) ~= "number" then
- error("invalid table: mixed or invalid key types")
- end
- n = n + 1
- end
- if n ~= #val then
- error("invalid table: sparse array")
- end
- -- Encode
- for i, v in ipairs(val) do
- table.insert(res, encode(v, stack))
- end
- stack[val] = nil
- return "[" .. table.concat(res, ",") .. "]"
-
- else
- -- Treat as an object
- for k, v in pairs(val) do
- if type(k) ~= "string" then
- error("invalid table: mixed or invalid key types")
- end
- table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
- end
- stack[val] = nil
- return "{" .. table.concat(res, ",") .. "}"
- end
-end
-
-
-local function encode_string(val)
- return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
-end
-
-
-local function encode_number(val)
- -- Check for NaN, -inf and inf
- if val ~= val or val <= -math.huge or val >= math.huge then
- error("unexpected number value '" .. tostring(val) .. "'")
- end
- return string.format("%.14g", val)
-end
-
-
-local type_func_map = {
- [ "nil" ] = encode_nil,
- [ "table" ] = encode_table,
- [ "string" ] = encode_string,
- [ "number" ] = encode_number,
- [ "boolean" ] = tostring,
-}
-
-
-encode = function(val, stack)
- local t = type(val)
- local f = type_func_map[t]
- if f then
- return f(val, stack)
- end
- error("unexpected type '" .. t .. "'")
-end
-
-
-function json.encode(val)
- return ( encode(val) )
-end
-
-
--------------------------------------------------------------------------------
--- Decode
--------------------------------------------------------------------------------
-
-local parse
-
-local function create_set(...)
- local res = {}
- for i = 1, select("#", ...) do
- res[ select(i, ...) ] = true
- end
- return res
-end
-
-local space_chars = create_set(" ", "\t", "\r", "\n")
-local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
-local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
-local literals = create_set("true", "false", "null")
-
-local literal_map = {
- [ "true" ] = true,
- [ "false" ] = false,
- [ "null" ] = nil,
-}
-
-
-local function next_char(str, idx, set, negate)
- for i = idx, #str do
- if set[str:sub(i, i)] ~= negate then
- return i
- end
- end
- return #str + 1
-end
-
-
-local function decode_error(str, idx, msg)
- local line_count = 1
- local col_count = 1
- for i = 1, idx - 1 do
- col_count = col_count + 1
- if str:sub(i, i) == "\n" then
- line_count = line_count + 1
- col_count = 1
- end
- end
- error( string.format("%s at line %d col %d", msg, line_count, col_count) )
-end
-
-
-local function codepoint_to_utf8(n)
- -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
- local f = math.floor
- if n <= 0x7f then
- return string.char(n)
- elseif n <= 0x7ff then
- return string.char(f(n / 64) + 192, n % 64 + 128)
- elseif n <= 0xffff then
- return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
- elseif n <= 0x10ffff then
- return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
- f(n % 4096 / 64) + 128, n % 64 + 128)
- end
- error( string.format("invalid unicode codepoint '%x'", n) )
-end
-
-
-local function parse_unicode_escape(s)
- local n1 = tonumber( s:sub(3, 6), 16 )
- local n2 = tonumber( s:sub(9, 12), 16 )
- -- Surrogate pair?
- if n2 then
- return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
- else
- return codepoint_to_utf8(n1)
- end
-end
-
-
-local function parse_string(str, i)
- local has_unicode_escape = false
- local has_surrogate_escape = false
- local has_escape = false
- local last
- for j = i + 1, #str do
- local x = str:byte(j)
-
- if x < 32 then
- decode_error(str, j, "control character in string")
- end
-
- if last == 92 then -- "\\" (escape char)
- if x == 117 then -- "u" (unicode escape sequence)
- local hex = str:sub(j + 1, j + 5)
- if not hex:find("%x%x%x%x") then
- decode_error(str, j, "invalid unicode escape in string")
- end
- if hex:find("^[dD][89aAbB]") then
- has_surrogate_escape = true
- else
- has_unicode_escape = true
- end
- else
- local c = string.char(x)
- if not escape_chars[c] then
- decode_error(str, j, "invalid escape char '" .. c .. "' in string")
- end
- has_escape = true
- end
- last = nil
-
- elseif x == 34 then -- '"' (end of string)
- local s = str:sub(i + 1, j - 1)
- if has_surrogate_escape then
- s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
- end
- if has_unicode_escape then
- s = s:gsub("\\u....", parse_unicode_escape)
- end
- if has_escape then
- s = s:gsub("\\.", escape_char_map_inv)
- end
- return s, j + 1
-
- else
- last = x
- end
- end
- decode_error(str, i, "expected closing quote for string")
-end
-
-
-local function parse_number(str, i)
- local x = next_char(str, i, delim_chars)
- local s = str:sub(i, x - 1)
- local n = tonumber(s)
- if not n then
- decode_error(str, i, "invalid number '" .. s .. "'")
- end
- return n, x
-end
-
-
-local function parse_literal(str, i)
- local x = next_char(str, i, delim_chars)
- local word = str:sub(i, x - 1)
- if not literals[word] then
- decode_error(str, i, "invalid literal '" .. word .. "'")
- end
- return literal_map[word], x
-end
-
-
-local function parse_array(str, i)
- local res = {}
- local n = 1
- i = i + 1
- while 1 do
- local x
- i = next_char(str, i, space_chars, true)
- -- Empty / end of array?
- if str:sub(i, i) == "]" then
- i = i + 1
- break
- end
- -- Read token
- x, i = parse(str, i)
- res[n] = x
- n = n + 1
- -- Next token
- i = next_char(str, i, space_chars, true)
- local chr = str:sub(i, i)
- i = i + 1
- if chr == "]" then break end
- if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
- end
- return res, i
-end
-
-
-local function parse_object(str, i)
- local res = {}
- i = i + 1
- while 1 do
- local key, val
- i = next_char(str, i, space_chars, true)
- -- Empty / end of object?
- if str:sub(i, i) == "}" then
- i = i + 1
- break
- end
- -- Read key
- if str:sub(i, i) ~= '"' then
- decode_error(str, i, "expected string for key")
- end
- key, i = parse(str, i)
- -- Read ':' delimiter
- i = next_char(str, i, space_chars, true)
- if str:sub(i, i) ~= ":" then
- decode_error(str, i, "expected ':' after key")
- end
- i = next_char(str, i + 1, space_chars, true)
- -- Read value
- val, i = parse(str, i)
- -- Set
- res[key] = val
- -- Next token
- i = next_char(str, i, space_chars, true)
- local chr = str:sub(i, i)
- i = i + 1
- if chr == "}" then break end
- if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
- end
- return res, i
-end
-
-
-local char_func_map = {
- [ '"' ] = parse_string,
- [ "0" ] = parse_number,
- [ "1" ] = parse_number,
- [ "2" ] = parse_number,
- [ "3" ] = parse_number,
- [ "4" ] = parse_number,
- [ "5" ] = parse_number,
- [ "6" ] = parse_number,
- [ "7" ] = parse_number,
- [ "8" ] = parse_number,
- [ "9" ] = parse_number,
- [ "-" ] = parse_number,
- [ "t" ] = parse_literal,
- [ "f" ] = parse_literal,
- [ "n" ] = parse_literal,
- [ "[" ] = parse_array,
- [ "{" ] = parse_object,
-}
-
-
-parse = function(str, idx)
- local chr = str:sub(idx, idx)
- local f = char_func_map[chr]
- if f then
- return f(str, idx)
- end
- decode_error(str, idx, "unexpected character '" .. chr .. "'")
-end
-
-
-function json.decode(str)
- if type(str) ~= "string" then
- error("expected argument of type string, got " .. type(str))
- end
- return ( parse(str, next_char(str, 1, space_chars, true)) )
-end
-
-
+local json = LibStub:NewLibrary("json", 1)
+if not json then return end
+
+--
+-- json.lua
+--
+-- Copyright (c) 2015 rxi
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the MIT license. See LICENSE for details.
+--
+
+
+-------------------------------------------------------------------------------
+-- Encode
+-------------------------------------------------------------------------------
+
+local encode
+
+local escape_char_map = {
+ [ "\\" ] = "\\\\",
+ [ "\"" ] = "\\\"",
+ [ "\b" ] = "\\b",
+ [ "\f" ] = "\\f",
+ [ "\n" ] = "\\n",
+ [ "\r" ] = "\\r",
+ [ "\t" ] = "\\t",
+}
+
+local escape_char_map_inv = { [ "\\/" ] = "/" }
+for k, v in pairs(escape_char_map) do
+ escape_char_map_inv[v] = k
+end
+
+
+local function escape_char(c)
+ return escape_char_map[c] or string.format("\\u%04x", c:byte())
+end
+
+
+local function encode_nil(val)
+ return "null"
+end
+
+
+local function encode_table(val, stack)
+ local res = {}
+ stack = stack or {}
+
+ -- Circular reference?
+ if stack[val] then error("circular reference") end
+
+ stack[val] = true
+
+ if val[1] ~= nil or next(val) == nil then
+ -- Treat as array -- check keys are valid and it is not sparse
+ local n = 0
+ for k in pairs(val) do
+ if type(k) ~= "number" then
+ error("invalid table: mixed or invalid key types")
+ end
+ n = n + 1
+ end
+ if n ~= #val then
+ error("invalid table: sparse array")
+ end
+ -- Encode
+ for i, v in ipairs(val) do
+ table.insert(res, encode(v, stack))
+ end
+ stack[val] = nil
+ return "[" .. table.concat(res, ",") .. "]"
+
+ else
+ -- Treat as an object
+ for k, v in pairs(val) do
+ if type(k) ~= "string" then
+ error("invalid table: mixed or invalid key types")
+ end
+ table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
+ end
+ stack[val] = nil
+ return "{" .. table.concat(res, ",") .. "}"
+ end
+end
+
+
+local function encode_string(val)
+ return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
+end
+
+
+local function encode_number(val)
+ -- Check for NaN, -inf and inf
+ if val ~= val or val <= -math.huge or val >= math.huge then
+ error("unexpected number value '" .. tostring(val) .. "'")
+ end
+ return string.format("%.14g", val)
+end
+
+
+local type_func_map = {
+ [ "nil" ] = encode_nil,
+ [ "table" ] = encode_table,
+ [ "string" ] = encode_string,
+ [ "number" ] = encode_number,
+ [ "boolean" ] = tostring,
+}
+
+
+encode = function(val, stack)
+ local t = type(val)
+ local f = type_func_map[t]
+ if f then
+ return f(val, stack)
+ end
+ error("unexpected type '" .. t .. "'")
+end
+
+
+function json.encode(val)
+ return ( encode(val) )
+end
+
+
+-------------------------------------------------------------------------------
+-- Decode
+-------------------------------------------------------------------------------
+
+local parse
+
+local function create_set(...)
+ local res = {}
+ for i = 1, select("#", ...) do
+ res[ select(i, ...) ] = true
+ end
+ return res
+end
+
+local space_chars = create_set(" ", "\t", "\r", "\n")
+local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
+local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
+local literals = create_set("true", "false", "null")
+
+local literal_map = {
+ [ "true" ] = true,
+ [ "false" ] = false,
+ [ "null" ] = nil,
+}
+
+
+local function next_char(str, idx, set, negate)
+ for i = idx, #str do
+ if set[str:sub(i, i)] ~= negate then
+ return i
+ end
+ end
+ return #str + 1
+end
+
+
+local function decode_error(str, idx, msg)
+ local line_count = 1
+ local col_count = 1
+ for i = 1, idx - 1 do
+ col_count = col_count + 1
+ if str:sub(i, i) == "\n" then
+ line_count = line_count + 1
+ col_count = 1
+ end
+ end
+ error( string.format("%s at line %d col %d", msg, line_count, col_count) )
+end
+
+
+local function codepoint_to_utf8(n)
+ -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
+ local f = math.floor
+ if n <= 0x7f then
+ return string.char(n)
+ elseif n <= 0x7ff then
+ return string.char(f(n / 64) + 192, n % 64 + 128)
+ elseif n <= 0xffff then
+ return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
+ elseif n <= 0x10ffff then
+ return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
+ f(n % 4096 / 64) + 128, n % 64 + 128)
+ end
+ error( string.format("invalid unicode codepoint '%x'", n) )
+end
+
+
+local function parse_unicode_escape(s)
+ local n1 = tonumber( s:sub(3, 6), 16 )
+ local n2 = tonumber( s:sub(9, 12), 16 )
+ -- Surrogate pair?
+ if n2 then
+ return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
+ else
+ return codepoint_to_utf8(n1)
+ end
+end
+
+
+local function parse_string(str, i)
+ local has_unicode_escape = false
+ local has_surrogate_escape = false
+ local has_escape = false
+ local last
+ for j = i + 1, #str do
+ local x = str:byte(j)
+
+ if x < 32 then
+ decode_error(str, j, "control character in string")
+ end
+
+ if last == 92 then -- "\\" (escape char)
+ if x == 117 then -- "u" (unicode escape sequence)
+ local hex = str:sub(j + 1, j + 5)
+ if not hex:find("%x%x%x%x") then
+ decode_error(str, j, "invalid unicode escape in string")
+ end
+ if hex:find("^[dD][89aAbB]") then
+ has_surrogate_escape = true
+ else
+ has_unicode_escape = true
+ end
+ else
+ local c = string.char(x)
+ if not escape_chars[c] then
+ decode_error(str, j, "invalid escape char '" .. c .. "' in string")
+ end
+ has_escape = true
+ end
+ last = nil
+
+ elseif x == 34 then -- '"' (end of string)
+ local s = str:sub(i + 1, j - 1)
+ if has_surrogate_escape then
+ s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
+ end
+ if has_unicode_escape then
+ s = s:gsub("\\u....", parse_unicode_escape)
+ end
+ if has_escape then
+ s = s:gsub("\\.", escape_char_map_inv)
+ end
+ return s, j + 1
+
+ else
+ last = x
+ end
+ end
+ decode_error(str, i, "expected closing quote for string")
+end
+
+
+local function parse_number(str, i)
+ local x = next_char(str, i, delim_chars)
+ local s = str:sub(i, x - 1)
+ local n = tonumber(s)
+ if not n then
+ decode_error(str, i, "invalid number '" .. s .. "'")
+ end
+ return n, x
+end
+
+
+local function parse_literal(str, i)
+ local x = next_char(str, i, delim_chars)
+ local word = str:sub(i, x - 1)
+ if not literals[word] then
+ decode_error(str, i, "invalid literal '" .. word .. "'")
+ end
+ return literal_map[word], x
+end
+
+
+local function parse_array(str, i)
+ local res = {}
+ local n = 1
+ i = i + 1
+ while 1 do
+ local x
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of array?
+ if str:sub(i, i) == "]" then
+ i = i + 1
+ break
+ end
+ -- Read token
+ x, i = parse(str, i)
+ res[n] = x
+ n = n + 1
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "]" then break end
+ if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
+ end
+ return res, i
+end
+
+
+local function parse_object(str, i)
+ local res = {}
+ i = i + 1
+ while 1 do
+ local key, val
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of object?
+ if str:sub(i, i) == "}" then
+ i = i + 1
+ break
+ end
+ -- Read key
+ if str:sub(i, i) ~= '"' then
+ decode_error(str, i, "expected string for key")
+ end
+ key, i = parse(str, i)
+ -- Read ':' delimiter
+ i = next_char(str, i, space_chars, true)
+ if str:sub(i, i) ~= ":" then
+ decode_error(str, i, "expected ':' after key")
+ end
+ i = next_char(str, i + 1, space_chars, true)
+ -- Read value
+ val, i = parse(str, i)
+ -- Set
+ res[key] = val
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "}" then break end
+ if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
+ end
+ return res, i
+end
+
+
+local char_func_map = {
+ [ '"' ] = parse_string,
+ [ "0" ] = parse_number,
+ [ "1" ] = parse_number,
+ [ "2" ] = parse_number,
+ [ "3" ] = parse_number,
+ [ "4" ] = parse_number,
+ [ "5" ] = parse_number,
+ [ "6" ] = parse_number,
+ [ "7" ] = parse_number,
+ [ "8" ] = parse_number,
+ [ "9" ] = parse_number,
+ [ "-" ] = parse_number,
+ [ "t" ] = parse_literal,
+ [ "f" ] = parse_literal,
+ [ "n" ] = parse_literal,
+ [ "[" ] = parse_array,
+ [ "{" ] = parse_object,
+}
+
+
+parse = function(str, idx)
+ local chr = str:sub(idx, idx)
+ local f = char_func_map[chr]
+ if f then
+ return f(str, idx)
+ end
+ decode_error(str, idx, "unexpected character '" .. chr .. "'")
+end
+
+
+function json.decode(str)
+ if type(str) ~= "string" then
+ error("expected argument of type string, got " .. type(str))
+ end
+ return ( parse(str, next_char(str, 1, space_chars, true)) )
+end
+
+
return json
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/embeds.xml b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/embeds.xml
similarity index 98%
rename from Artemis/Artemis/Modules/Games/WoW/Resources/Addon/embeds.xml
rename to Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/embeds.xml
index 781538970..36826abe0 100644
--- a/Artemis/Artemis/Modules/Games/WoW/Resources/Addon/embeds.xml
+++ b/Artemis/Artemis/Modules/Games/WoW/Resources/Addon source/embeds.xml
@@ -1,9 +1,9 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/Resources/wow-addon.zip b/Artemis/Artemis/Modules/Games/WoW/Resources/wow-addon.zip
new file mode 100644
index 000000000..70d820a9a
Binary files /dev/null and b/Artemis/Artemis/Modules/Games/WoW/Resources/wow-addon.zip differ
diff --git a/Artemis/Artemis/Modules/Games/WoW/WoWModel.cs b/Artemis/Artemis/Modules/Games/WoW/WoWModel.cs
index 8c95e8652..f3a53d9d9 100644
--- a/Artemis/Artemis/Modules/Games/WoW/WoWModel.cs
+++ b/Artemis/Artemis/Modules/Games/WoW/WoWModel.cs
@@ -2,22 +2,37 @@
using Artemis.Managers;
using Artemis.Modules.Abstract;
using Artemis.Modules.Games.WoW.Models;
+using Artemis.Properties;
+using Artemis.Services;
+using Artemis.Utilities;
+using Microsoft.Win32;
using Newtonsoft.Json.Linq;
+using System.IO;
+using System.IO.Compression;
namespace Artemis.Modules.Games.WoW
{
public class WoWModel : ModuleModel
{
+ private readonly MetroDialogService _dialogService;
private readonly WowPacketScanner _packetScanner;
- public WoWModel(DeviceManager deviceManager, LuaManager luaManager, WowPacketScanner packetScanner) : base(deviceManager, luaManager)
+ public WoWModel(DeviceManager deviceManager, LuaManager luaManager, WowPacketScanner packetScanner, MetroDialogService dialogService) : base(deviceManager, luaManager)
{
Settings = SettingsProvider.Load();
DataModel = new WoWDataModel();
ProcessNames.Add("Wow-64");
_packetScanner = packetScanner;
+ _dialogService = dialogService;
_packetScanner.RaiseDataReceived += (sender, args) => HandleGameData(args.Command, args.Data);
+
+ FindWoW();
+
+ // I simply cannot be sure wether this addon will bring people's accounts in trouble so
+ // lets remove it whenever Artemis isn't running the WoW module.
+ // (This also means the addon isnt left behind should the user uninstall Artemis.)
+ RemoveAddon();
}
public override string Name => "WoW";
@@ -26,12 +41,14 @@ namespace Artemis.Modules.Games.WoW
public override void Enable()
{
+ PlaceAddon();
_packetScanner.Start();
base.Enable();
}
public override void Dispose()
{
+ RemoveAddon();
_packetScanner.Stop();
base.Dispose();
}
@@ -44,6 +61,67 @@ namespace Artemis.Modules.Games.WoW
dataModel.Target.Update();
}
+ public void FindWoW()
+ {
+ var gameSettings = Settings as WoWSettings;
+ if (gameSettings == null)
+ return;
+
+ // If already propertly set up, don't do anything
+ if (gameSettings.GameDirectory != null && File.Exists(gameSettings.GameDirectory + @"\Wow.exe"))
+ return;
+
+ var key = Registry.LocalMachine.OpenSubKey(
+ @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\World of Warcraft");
+ var path = key?.GetValue("DisplayIcon")?.ToString();
+ if (string.IsNullOrEmpty(path) || !File.Exists(path))
+ return;
+
+ gameSettings.GameDirectory = path.Substring(0, path.Length - 8);
+ gameSettings.Save();
+ }
+
+ public void ChangeDirectory(string directory, bool checkExe)
+ {
+ var settings = (WoWSettings) Settings;
+ if (checkExe && !File.Exists(directory + @"\Wow.exe"))
+ {
+ _dialogService.ShowErrorMessageBox("Please select a valid WoW directory\n\n" +
+ @"By default WoW is in C:\Program Files (x86)\World of Warcraft");
+
+ settings.GameDirectory = string.Empty;
+ settings.Save();
+ return;
+ }
+ settings.GameDirectory = directory;
+ settings.Save();
+ }
+
+ public void PlaceAddon()
+ {
+ var settings = (WoWSettings) Settings;
+ var path = settings.GameDirectory;
+
+ if (!File.Exists(path + @"\Wow.exe"))
+ return;
+
+ // Load the ZIP from resources
+ using (var stream = new MemoryStream(Resources.wow_addon))
+ {
+ using (var archive = new ZipArchive(stream))
+ {
+ archive.ExtractToDirectory(settings.GameDirectory + @"\Interface\Addons\Artemis", true);
+ }
+ }
+ }
+
+ public void RemoveAddon()
+ {
+ var settings = (WoWSettings) Settings;
+ if (Directory.Exists(settings.GameDirectory + @"\Interface\Addons\Artemis"))
+ Directory.Delete(settings.GameDirectory + @"\Interface\Addons\Artemis", true);
+ }
+
private void HandleGameData(string command, string data)
{
JToken json = null;
@@ -180,4 +258,4 @@ namespace Artemis.Modules.Games.WoW
dataModel.Target.CastBar.Clear();
}
}
-}
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/WoW/WoWSettings.cs b/Artemis/Artemis/Modules/Games/WoW/WoWSettings.cs
index c4a3f3b5d..524fdac66 100644
--- a/Artemis/Artemis/Modules/Games/WoW/WoWSettings.cs
+++ b/Artemis/Artemis/Modules/Games/WoW/WoWSettings.cs
@@ -4,5 +4,6 @@ namespace Artemis.Modules.Games.WoW
{
public class WoWSettings : ModuleSettings
{
+ public string GameDirectory { get; set; }
}
}
diff --git a/Artemis/Artemis/Modules/Games/WoW/WoWView.xaml b/Artemis/Artemis/Modules/Games/WoW/WoWView.xaml
index 032a99375..78ef8d991 100644
--- a/Artemis/Artemis/Modules/Games/WoW/WoWView.xaml
+++ b/Artemis/Artemis/Modules/Games/WoW/WoWView.xaml
@@ -7,6 +7,7 @@
+
@@ -32,11 +33,20 @@
Style="{StaticResource MahApps.Metro.Styles.ToggleSwitchButton.Win10}" ToolTip="Note: You can't enable an module when Artemis is disabled" />
+
+
+
+
+
+
+
+
+
-
+
-
+
diff --git a/Artemis/Artemis/Modules/Games/WoW/WoWViewModel.cs b/Artemis/Artemis/Modules/Games/WoW/WoWViewModel.cs
index 5978f88c2..1aca250f2 100644
--- a/Artemis/Artemis/Modules/Games/WoW/WoWViewModel.cs
+++ b/Artemis/Artemis/Modules/Games/WoW/WoWViewModel.cs
@@ -1,4 +1,5 @@
-using Artemis.Managers;
+using System.Windows.Forms;
+using Artemis.Managers;
using Artemis.Modules.Abstract;
using Ninject;
@@ -13,5 +14,23 @@ namespace Artemis.Modules.Games.WoW
}
public override bool UsesProfileEditor => true;
+
+ public void PlaceAddon()
+ {
+ ((WoWModel) ModuleModel).PlaceAddon();
+ }
+
+ public void BrowseDirectory()
+ {
+ var dialog = new FolderBrowserDialog {SelectedPath = ((WoWSettings) Settings).GameDirectory};
+ var result = dialog.ShowDialog();
+ if (result != DialogResult.OK)
+ return;
+
+ ((WoWSettings) Settings).GameDirectory = dialog.SelectedPath;
+ ((WoWModel) ModuleModel).PlaceAddon();
+ Settings.Save();
+ NotifyOfPropertyChange(() => Settings);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Properties/Resources.Designer.cs b/Artemis/Artemis/Properties/Resources.Designer.cs
index 2ae2a31ad..be17825d8 100644
--- a/Artemis/Artemis/Properties/Resources.Designer.cs
+++ b/Artemis/Artemis/Properties/Resources.Designer.cs
@@ -19,7 +19,7 @@ namespace Artemis.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -105,6 +105,7 @@ namespace Artemis.Properties {
///{
/// "uri" "http://localhost:{{port}}/csgo_game_event"
/// "timeout" "0.1"
+ /// "heartbeat" "0.1"
/// "data"
/// {
/// "provider" "1"
@@ -437,5 +438,15 @@ namespace Artemis.Properties {
return ((byte[])(obj));
}
}
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] wow_addon {
+ get {
+ object obj = ResourceManager.GetObject("wow_addon", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
}
}
diff --git a/Artemis/Artemis/Properties/Resources.resx b/Artemis/Artemis/Properties/Resources.resx
index fa4d5de48..529de8e53 100644
--- a/Artemis/Artemis/Properties/Resources.resx
+++ b/Artemis/Artemis/Properties/Resources.resx
@@ -223,4 +223,7 @@
..\Resources\Keyboards\k95-platinum.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Modules\Games\WoW\Resources\wow-addon.zip;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
\ No newline at end of file
diff --git a/Artemis/Artemis/packages.config b/Artemis/Artemis/packages.config
index e12bb7f35..451475e77 100644
--- a/Artemis/Artemis/packages.config
+++ b/Artemis/Artemis/packages.config
@@ -4,10 +4,10 @@
-
+
-
+
@@ -28,7 +28,7 @@
-
+
\ No newline at end of file