flickerstreak@1: --[[ flickerstreak@1: Name: AceComm-2.0 flickerstreak@1: Revision: $Rev: 18708 $ flickerstreak@1: Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) flickerstreak@1: Inspired By: Ace 1.x by Turan (turan@gryphon.com) flickerstreak@1: Website: http://www.wowace.com/ flickerstreak@1: Documentation: http://www.wowace.com/index.php/AceComm-2.0 flickerstreak@1: SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceComm-2.0 flickerstreak@1: Description: Mixin to allow for inter-player addon communications. flickerstreak@1: Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, flickerstreak@1: ChatThrottleLib by Mikk (included) flickerstreak@1: ]] flickerstreak@1: flickerstreak@1: local MAJOR_VERSION = "AceComm-2.0" flickerstreak@1: local MINOR_VERSION = "$Revision: 18708 $" flickerstreak@1: flickerstreak@1: if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end flickerstreak@1: if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end flickerstreak@1: flickerstreak@1: if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end flickerstreak@1: flickerstreak@1: local _G = getfenv(0) flickerstreak@1: flickerstreak@1: local AceOO = AceLibrary("AceOO-2.0") flickerstreak@1: local Mixin = AceOO.Mixin flickerstreak@1: local AceComm = Mixin { flickerstreak@1: "SendCommMessage", flickerstreak@1: "SendPrioritizedCommMessage", flickerstreak@1: "RegisterComm", flickerstreak@1: "UnregisterComm", flickerstreak@1: "UnregisterAllComms", flickerstreak@1: "IsCommRegistered", flickerstreak@1: "SetDefaultCommPriority", flickerstreak@1: "SetCommPrefix", flickerstreak@1: "RegisterMemoizations", flickerstreak@1: "IsUserInChannel", flickerstreak@1: } flickerstreak@1: AceComm.hooks = {} flickerstreak@1: flickerstreak@1: local AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0") flickerstreak@1: flickerstreak@1: local string_byte = string.byte flickerstreak@1: flickerstreak@1: local byte_a = string_byte('a') flickerstreak@1: local byte_z = string_byte('z') flickerstreak@1: local byte_A = string_byte('A') flickerstreak@1: local byte_Z = string_byte('Z') flickerstreak@1: local byte_fake_s = string_byte('\015') flickerstreak@1: local byte_fake_S = string_byte('\020') flickerstreak@1: local byte_deg = string_byte('°') flickerstreak@1: local byte_percent = string_byte('%') -- 37 flickerstreak@1: flickerstreak@1: local byte_b = string_byte('b') flickerstreak@1: local byte_B = string_byte('B') flickerstreak@1: local byte_nil = string_byte('/') flickerstreak@1: local byte_plus = string_byte('+') flickerstreak@1: local byte_minus = string_byte('-') flickerstreak@1: local byte_d = string_byte('d') flickerstreak@1: local byte_D = string_byte('D') flickerstreak@1: local byte_e = string_byte('e') flickerstreak@1: local byte_E = string_byte('E') flickerstreak@1: local byte_m = string_byte('m') flickerstreak@1: local byte_s = string_byte('s') flickerstreak@1: local byte_S = string_byte('S') flickerstreak@1: local byte_o = string_byte('o') flickerstreak@1: local byte_O = string_byte('O') flickerstreak@1: local byte_t = string_byte('t') flickerstreak@1: local byte_T = string_byte('T') flickerstreak@1: local byte_u = string_byte('u') flickerstreak@1: local byte_U = string_byte('U') flickerstreak@1: local byte_i = string_byte('i') flickerstreak@1: local byte_inf = string_byte('@') flickerstreak@1: local byte_ninf = string_byte('$') flickerstreak@1: local byte_nan = string_byte('!') flickerstreak@1: flickerstreak@1: local inf = 1/0 flickerstreak@1: local nan = 0/0 flickerstreak@1: flickerstreak@1: local math_floor = math.floor flickerstreak@1: local math_mod = math.fmod flickerstreak@1: local math_floormod = function(value, m) flickerstreak@1: return math_mod(math_floor(value), m) flickerstreak@1: end flickerstreak@1: local string_gmatch = string.gmatch flickerstreak@1: local string_char = string.char flickerstreak@1: local string_len = string.len flickerstreak@1: local string_format = string.format flickerstreak@1: local string_gsub = string.gsub flickerstreak@1: local string_find = string.find flickerstreak@1: local table_insert = table.insert flickerstreak@1: local string_sub = string.sub flickerstreak@1: local table_concat = table.concat flickerstreak@1: local table_remove = table.remove flickerstreak@1: flickerstreak@1: local type = type flickerstreak@1: local unpack = unpack flickerstreak@1: local pairs = pairs flickerstreak@1: local next = next flickerstreak@1: flickerstreak@1: local player = UnitName("player") flickerstreak@1: flickerstreak@1: local NumericCheckSum, HexCheckSum, BinaryCheckSum flickerstreak@1: local TailoredNumericCheckSum, TailoredHexCheckSum, TailoredBinaryCheckSum flickerstreak@1: do flickerstreak@1: local SOME_PRIME = 16777213 flickerstreak@1: function NumericCheckSum(text) flickerstreak@1: local counter = 1 flickerstreak@1: local len = string_len(text) flickerstreak@1: for i = 1, len, 3 do flickerstreak@1: counter = math_mod(counter*8257, 16777259) + flickerstreak@1: (string_byte(text,i)) + flickerstreak@1: ((string_byte(text,i+1) or 1)*127) + flickerstreak@1: ((string_byte(text,i+2) or 2)*16383) flickerstreak@1: end flickerstreak@1: return math_mod(counter, 16777213) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function HexCheckSum(text) flickerstreak@1: return string_format("%06x", NumericCheckSum(text)) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function BinaryCheckSum(text) flickerstreak@1: local num = NumericCheckSum(text) flickerstreak@1: return string_char(math_floor(num / 65536), math_floormod(num / 256, 256), math_mod(num, 256)) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function TailoredNumericCheckSum(text) flickerstreak@1: local hash = NumericCheckSum(text) flickerstreak@1: local a = math_floor(hash / 65536) flickerstreak@1: local b = math_floormod(hash / 256, 256) flickerstreak@1: local c = math_mod(hash, 256) flickerstreak@1: -- \000, \n, |, °, s, S, \015, \020 flickerstreak@1: if a == 0 or a == 10 or a == 124 or a == 176 or a == 115 or a == 83 or a == 15 or a == 20 or a == 37 then flickerstreak@1: a = a + 1 flickerstreak@1: -- \t, \255 flickerstreak@1: elseif a == 9 or a == 255 then flickerstreak@1: a = a - 1 flickerstreak@1: end flickerstreak@1: if b == 0 or b == 10 or b == 124 or b == 176 or b == 115 or b == 83 or b == 15 or b == 20 or b == 37 then flickerstreak@1: b = b + 1 flickerstreak@1: elseif b == 9 or b == 255 then flickerstreak@1: b = b - 1 flickerstreak@1: end flickerstreak@1: if c == 0 or c == 10 or c == 124 or c == 176 or c == 115 or c == 83 or c == 15 or c == 20 or c == 37 then flickerstreak@1: c = c + 1 flickerstreak@1: elseif c == 9 or c == 255 then flickerstreak@1: c = c - 1 flickerstreak@1: end flickerstreak@1: return a * 65536 + b * 256 + c flickerstreak@1: end flickerstreak@1: flickerstreak@1: function TailoredHexCheckSum(text) flickerstreak@1: return string_format("%06x", TailoredNumericCheckSum(text)) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function TailoredBinaryCheckSum(text) flickerstreak@1: local num = TailoredNumericCheckSum(text) flickerstreak@1: return string_char(math_floor(num / 65536), math_floormod(num / 256, 256), math_mod(num, 256)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function GetLatency() flickerstreak@1: local _,_,lag = GetNetStats() flickerstreak@1: return lag / 1000 flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function IsInChannel(chan) flickerstreak@1: return GetChannelName(chan) ~= 0 flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- Package a message for transmission flickerstreak@1: local function Encode(text, drunk) flickerstreak@1: text = string_gsub(text, "°", "°±") flickerstreak@1: if drunk then flickerstreak@1: text = string_gsub(text, "\020", "°\021") flickerstreak@1: text = string_gsub(text, "\015", "°\016") flickerstreak@1: text = string_gsub(text, "S", "\020") flickerstreak@1: text = string_gsub(text, "s", "\015") flickerstreak@1: -- change S and s to a different set of character bytes. flickerstreak@1: end flickerstreak@1: text = string_gsub(text, "\255", "°\254") -- \255 (this is here because \000 is more common) flickerstreak@1: text = string_gsub(text, "%z", "\255") -- \000 flickerstreak@1: text = string_gsub(text, "\010", "°\011") -- \n flickerstreak@1: text = string_gsub(text, "\124", "°\125") -- | flickerstreak@1: text = string_gsub(text, "%%", "°\038") -- % flickerstreak@1: -- encode assorted prohibited characters flickerstreak@1: return text flickerstreak@1: end flickerstreak@1: flickerstreak@1: local func flickerstreak@1: -- Clean a received message flickerstreak@1: local function Decode(text, drunk) flickerstreak@1: if drunk then flickerstreak@1: text = string_gsub(text, "^(.*)°.-$", "%1") flickerstreak@1: -- get rid of " ...hic!" flickerstreak@1: end flickerstreak@1: if not func then flickerstreak@1: func = function(text) flickerstreak@1: if text == "\016" then flickerstreak@1: return "\015" flickerstreak@1: elseif text == "\021" then flickerstreak@1: return "\020" flickerstreak@1: elseif text == "±" then flickerstreak@1: return "°" flickerstreak@1: elseif text == "\254" then flickerstreak@1: return "\255" flickerstreak@1: elseif text == "\011" then flickerstreak@1: return "\010" flickerstreak@1: elseif text == "\125" then flickerstreak@1: return "\124" flickerstreak@1: elseif text == "\038" then flickerstreak@1: return "\037" flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: text = string_gsub(text, "\255", "\000") flickerstreak@1: if drunk then flickerstreak@1: text = string_gsub(text, "\020", "S") flickerstreak@1: text = string_gsub(text, "\015", "s") flickerstreak@1: end flickerstreak@1: text = string_gsub(text, drunk and "°([\016\021±\254\011\125\038])" or "°([±\254\011\125\038])", func) flickerstreak@1: -- remove the hidden character and refix the prohibited characters. flickerstreak@1: return text flickerstreak@1: end flickerstreak@1: flickerstreak@1: local lastChannelJoined flickerstreak@1: flickerstreak@1: function AceComm.hooks:JoinChannelByName(orig, channel, a,b,c,d,e,f,g,h,i) flickerstreak@1: lastChannelJoined = channel flickerstreak@1: return orig(channel, a,b,c,d,e,f,g,h,i) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function JoinChannel(channel) flickerstreak@1: if not IsInChannel(channel) then flickerstreak@1: LeaveChannelByName(channel) flickerstreak@1: AceComm:ScheduleEvent(JoinChannelByName, 0, channel) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function LeaveChannel(channel) flickerstreak@1: if IsInChannel(channel) then flickerstreak@1: LeaveChannelByName(channel) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local switches = {} flickerstreak@1: flickerstreak@1: local function SwitchChannel(former, latter) flickerstreak@1: if IsInChannel(former) then flickerstreak@1: LeaveChannelByName(former) flickerstreak@1: switches[{ flickerstreak@1: former = former, flickerstreak@1: latter = latter flickerstreak@1: }] = true flickerstreak@1: return flickerstreak@1: end flickerstreak@1: if not IsInChannel(latter) then flickerstreak@1: JoinChannelByName(latter) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local shutdown = false flickerstreak@1: flickerstreak@1: local zoneCache flickerstreak@1: local function GetCurrentZoneChannel() flickerstreak@1: if not zoneCache then flickerstreak@1: zoneCache = "AceCommZone" .. HexCheckSum(GetRealZoneText()) flickerstreak@1: end flickerstreak@1: return zoneCache flickerstreak@1: end flickerstreak@1: flickerstreak@1: local AceComm_registry flickerstreak@1: flickerstreak@1: local function SupposedToBeInChannel(chan) flickerstreak@1: if not string_find(chan, "^AceComm") then flickerstreak@1: return true flickerstreak@1: elseif shutdown or not AceEvent:IsFullyInitialized() then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: flickerstreak@1: if chan == "AceComm" then flickerstreak@1: return AceComm_registry.GLOBAL and next(AceComm_registry.GLOBAL) and true or false flickerstreak@1: elseif string_find(chan, "^AceCommZone%x%x%x%x%x%x$") then flickerstreak@1: if chan == GetCurrentZoneChannel() then flickerstreak@1: return AceComm_registry.ZONE and next(AceComm_registry.ZONE) and true or false flickerstreak@1: else flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: else flickerstreak@1: return AceComm_registry.CUSTOM and AceComm_registry.CUSTOM[chan] and next(AceComm_registry.CUSTOM[chan]) and true or false flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local tmp = {} flickerstreak@1: local function LeaveAceCommChannels(all) flickerstreak@1: if all then flickerstreak@1: shutdown = true flickerstreak@1: end flickerstreak@1: local _,a,_,b,_,c,_,d,_,e,_,f,_,g,_,h,_,i,_,j = GetChannelList() flickerstreak@1: tmp[1] = a flickerstreak@1: tmp[2] = b flickerstreak@1: tmp[3] = c flickerstreak@1: tmp[4] = d flickerstreak@1: tmp[5] = e flickerstreak@1: tmp[6] = f flickerstreak@1: tmp[7] = g flickerstreak@1: tmp[8] = h flickerstreak@1: tmp[9] = i flickerstreak@1: tmp[10] = j flickerstreak@1: for _,v in ipairs(tmp) do flickerstreak@1: if v and string_find(v, "^AceComm") then flickerstreak@1: if not SupposedToBeInChannel(v) then flickerstreak@1: LeaveChannelByName(v) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: for i = 1, 10 do flickerstreak@1: tmp[i] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local lastRefix = 0 flickerstreak@1: local function RefixAceCommChannelsAndEvents() flickerstreak@1: if GetTime() - lastRefix <= 5 then flickerstreak@1: AceComm:ScheduleEvent(RefixAceCommChannelsAndEvents, 1) flickerstreak@1: return flickerstreak@1: end flickerstreak@1: lastRefix = GetTime() flickerstreak@1: LeaveAceCommChannels(false) flickerstreak@1: flickerstreak@1: local channel = false flickerstreak@1: local whisper = false flickerstreak@1: local addon = false flickerstreak@1: if SupposedToBeInChannel("AceComm") then flickerstreak@1: JoinChannel("AceComm") flickerstreak@1: channel = true flickerstreak@1: end flickerstreak@1: if SupposedToBeInChannel(GetCurrentZoneChannel()) then flickerstreak@1: JoinChannel(GetCurrentZoneChannel()) flickerstreak@1: channel = true flickerstreak@1: end flickerstreak@1: if AceComm_registry.CUSTOM then flickerstreak@1: for k,v in pairs(AceComm_registry.CUSTOM) do flickerstreak@1: if next(v) and SupposedToBeInChannel(k) then flickerstreak@1: JoinChannel(k) flickerstreak@1: channel = true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if AceComm_registry.WHISPER then flickerstreak@1: whisper = true flickerstreak@1: end flickerstreak@1: if AceComm_registry.GROUP or AceComm_registry.PARTY or AceComm_registry.RAID or AceComm_registry.BATTLEGROUND or AceComm_registry.GUILD then flickerstreak@1: addon = true flickerstreak@1: end flickerstreak@1: flickerstreak@1: if channel then flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_CHANNEL") flickerstreak@1: end flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LIST") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_CHANNEL_LIST") flickerstreak@1: end flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_JOIN") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_CHANNEL_JOIN") flickerstreak@1: end flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LEAVE") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE") flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_CHANNEL") flickerstreak@1: end flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LIST") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_LIST") flickerstreak@1: end flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_JOIN") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_JOIN") flickerstreak@1: end flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LEAVE") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_LEAVE") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if whisper then flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_WHISPER") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_WHISPER") flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_WHISPER") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_WHISPER") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if addon then flickerstreak@1: if not AceComm:IsEventRegistered("CHAT_MSG_ADDON") then flickerstreak@1: AceComm:RegisterEvent("CHAT_MSG_ADDON") flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if AceComm:IsEventRegistered("CHAT_MSG_ADDON") then flickerstreak@1: AceComm:UnregisterEvent("CHAT_MSG_ADDON") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: do flickerstreak@1: local myFunc = function(k) flickerstreak@1: if not IsInChannel(k.latter) then flickerstreak@1: JoinChannelByName(k.latter) flickerstreak@1: end flickerstreak@1: switches[k] = nil flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_CHANNEL_NOTICE(kind, _, _, deadName, _, _, _, num, channel) flickerstreak@1: if kind == "YOU_LEFT" then flickerstreak@1: if not string_find(channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: for k in pairs(switches) do flickerstreak@1: if k.former == channel then flickerstreak@1: self:ScheduleEvent(myFunc, 0, k) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if channel == GetCurrentZoneChannel() then flickerstreak@1: self:TriggerEvent("AceComm_LeftChannel", "ZONE") flickerstreak@1: elseif channel == "AceComm" then flickerstreak@1: self:TriggerEvent("AceComm_LeftChannel", "GLOBAL") flickerstreak@1: else flickerstreak@1: self:TriggerEvent("AceComm_LeftChannel", "CUSTOM", string_sub(channel, 8)) flickerstreak@1: end flickerstreak@1: if string_find(channel, "^AceComm") and SupposedToBeInChannel(channel) then flickerstreak@1: self:ScheduleEvent(JoinChannel, 0, channel) flickerstreak@1: end flickerstreak@1: if AceComm.userRegistry[channel] then flickerstreak@1: AceComm.userRegistry[channel] = nil flickerstreak@1: end flickerstreak@1: elseif kind == "YOU_JOINED" then flickerstreak@1: if not string_find(num == 0 and deadName or channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: if num == 0 then flickerstreak@1: self:ScheduleEvent(LeaveChannelByName, 0, deadName) flickerstreak@1: switches[{ flickerstreak@1: former = deadName, flickerstreak@1: latter = deadName, flickerstreak@1: }] = true flickerstreak@1: elseif channel == GetCurrentZoneChannel() then flickerstreak@1: self:TriggerEvent("AceComm_JoinedChannel", "ZONE") flickerstreak@1: elseif channel == "AceComm" then flickerstreak@1: self:TriggerEvent("AceComm_JoinedChannel", "GLOBAL") flickerstreak@1: else flickerstreak@1: self:TriggerEvent("AceComm_JoinedChannel", "CUSTOM", string_sub(channel, 8)) flickerstreak@1: end flickerstreak@1: if num ~= 0 then flickerstreak@1: if not SupposedToBeInChannel(channel) then flickerstreak@1: LeaveChannel(channel) flickerstreak@1: else flickerstreak@1: ListChannelByName(channel) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local Serialize flickerstreak@1: do flickerstreak@1: local recurse flickerstreak@1: local function _Serialize(v, textToHash) flickerstreak@1: local kind = type(v) flickerstreak@1: if kind == "boolean" then flickerstreak@1: if v then flickerstreak@1: return "B" -- true flickerstreak@1: else flickerstreak@1: return "b" -- false flickerstreak@1: end flickerstreak@1: elseif not v then flickerstreak@1: return "/" flickerstreak@1: elseif kind == "number" then flickerstreak@1: if v == math_floor(v) then flickerstreak@1: if v <= 127 and v >= -128 then flickerstreak@1: if v < 0 then flickerstreak@1: v = v + 256 flickerstreak@1: end flickerstreak@1: return string_char(byte_d, v) flickerstreak@1: elseif v <= 32767 and v >= -32768 then flickerstreak@1: if v < 0 then flickerstreak@1: v = v + 65536 flickerstreak@1: end flickerstreak@1: return string_char(byte_D, math_floor(v / 256), math_mod(v, 256)) flickerstreak@1: elseif v <= 2147483647 and v >= -2147483648 then flickerstreak@1: if v < 0 then flickerstreak@1: v = v + 4294967296 flickerstreak@1: end flickerstreak@1: return string_char(byte_e, math_floor(v / 16777216), math_floormod(v / 65536, 256), math_floormod(v / 256, 256), math_mod(v, 256)) flickerstreak@1: elseif v <= 9223372036854775807 and v >= -9223372036854775808 then flickerstreak@1: if v < 0 then flickerstreak@1: v = v + 18446744073709551616 flickerstreak@1: end flickerstreak@1: return string_char(byte_E, math_floor(v / 72057594037927936), math_floormod(v / 281474976710656, 256), math_floormod(v / 1099511627776, 256), math_floormod(v / 4294967296, 256), math_floormod(v / 16777216, 256), math_floormod(v / 65536, 256), math_floormod(v / 256, 256), math_mod(v, 256)) flickerstreak@1: end flickerstreak@1: elseif v == inf then flickerstreak@1: return string_char(64 --[[byte_inf]]) flickerstreak@1: elseif v == -inf then flickerstreak@1: return string_char(36 --[[byte_ninf]]) flickerstreak@1: elseif v ~= v then flickerstreak@1: return string_char(33 --[[byte_nan]]) flickerstreak@1: end flickerstreak@1: -- do flickerstreak@1: -- local s = tostring(v) flickerstreak@1: -- local len = string_len(s) flickerstreak@1: -- return string_char(byte_plus, len) .. s flickerstreak@1: -- end flickerstreak@1: local sign = v < 0 or v == 0 and tostring(v) == "-0" flickerstreak@1: if sign then flickerstreak@1: v = -v flickerstreak@1: end flickerstreak@1: local m, exp = math.frexp(v) flickerstreak@1: m = m * 9007199254740992 flickerstreak@1: local x = exp + 1023 flickerstreak@1: local b = math_mod(m, 256) flickerstreak@1: local c = math_floormod(m / 256, 256) flickerstreak@1: m = math_floor(m / 65536) flickerstreak@1: m = m + x * 137438953472 flickerstreak@1: return string_char(sign and byte_minus or byte_plus, math_floormod(m / 1099511627776, 256), math_floormod(m / 4294967296, 256), math_floormod(m / 16777216, 256), math_floormod(m / 65536, 256), math_floormod(m / 256, 256), math_mod(m, 256), c, b) flickerstreak@1: elseif kind == "string" then flickerstreak@1: local hash = textToHash and textToHash[v] flickerstreak@1: if hash then flickerstreak@1: return string_char(byte_m, math_floor(hash / 65536), math_floormod(hash / 256, 256), math_mod(hash, 256)) flickerstreak@1: end flickerstreak@1: local _,_,A,B,C,D,E,F,G,H = string_find(v, "^|cff%x%x%x%x%x%x|Hitem:(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%-?%d+):(%d+)|h%[.+%]|h|r$") flickerstreak@1: if A then flickerstreak@1: -- item link flickerstreak@1: flickerstreak@1: A = A+0 -- convert to number flickerstreak@1: B = B+0 flickerstreak@1: C = C+0 flickerstreak@1: D = D+0 flickerstreak@1: E = E+0 flickerstreak@1: F = F+0 flickerstreak@1: G = G+0 flickerstreak@1: H = H+0 flickerstreak@1: flickerstreak@1: -- (1-35000):(1-3093):(1-3093):(1-3093):(1-3093):(?):(-57 to 2164):(0-4294967295) flickerstreak@1: flickerstreak@1: F = nil -- don't care flickerstreak@1: if G < 0 then flickerstreak@1: G = G + 65536 -- handle negatives flickerstreak@1: end flickerstreak@1: flickerstreak@1: H = math_mod(H, 65536) -- only lower 16 bits matter flickerstreak@1: flickerstreak@1: return string_char(byte_i, math_floormod(A / 256, 256), math_mod(A, 256), math_floormod(B / 256, 256), math_mod(B, 256), math_floormod(C / 256, 256), math_mod(C, 256), math_floormod(D / 256, 256), math_mod(D, 256), math_floormod(E / 256, 256), math_mod(E, 256), math_floormod(G / 256, 256), math_mod(G, 256), math_floormod(H / 256, 256), math_mod(H, 256)) flickerstreak@1: else flickerstreak@1: -- normal string flickerstreak@1: local len = string_len(v) flickerstreak@1: if len <= 255 then flickerstreak@1: return string_char(byte_s, len) .. v flickerstreak@1: else flickerstreak@1: return string_char(byte_S, math_floor(len / 256), math_mod(len, 256)) .. v flickerstreak@1: end flickerstreak@1: end flickerstreak@1: elseif kind == "function" then flickerstreak@1: AceComm:error("Cannot serialize a function") flickerstreak@1: elseif kind == "table" then flickerstreak@1: if recurse[v] then flickerstreak@1: for k in pairs(recurse) do flickerstreak@1: recurse[k] = nil flickerstreak@1: end flickerstreak@1: AceComm:error("Cannot serialize a recursive table") flickerstreak@1: return flickerstreak@1: end flickerstreak@1: recurse[v] = true flickerstreak@1: if AceOO.inherits(v, AceOO.Class) then flickerstreak@1: if not v.class then flickerstreak@1: AceComm:error("Cannot serialize an AceOO class, can only serialize objects") flickerstreak@1: elseif type(v.Serialize) ~= "function" then flickerstreak@1: AceComm:error("Cannot serialize an AceOO object without the `Serialize' method.") flickerstreak@1: elseif type(v.class.Deserialize) ~= "function" then flickerstreak@1: AceComm:error("Cannot serialize an AceOO object without the `Deserialize' static method.") flickerstreak@1: elseif type(v.class.GetLibraryVersion) ~= "function" or not AceLibrary:HasInstance(v.class:GetLibraryVersion()) then flickerstreak@1: AceComm:error("Cannot serialize an AceOO object if the class is not registered with AceLibrary.") flickerstreak@1: end flickerstreak@1: local classHash = TailoredBinaryCheckSum(v.class:GetLibraryVersion()) flickerstreak@1: local t = { classHash, v:Serialize() } flickerstreak@1: for i = 2, #t do flickerstreak@1: t[i] = _Serialize(t[i], textToHash) flickerstreak@1: end flickerstreak@1: if not notFirst then flickerstreak@1: for k in pairs(recurse) do flickerstreak@1: recurse[k] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local s = table.concat(t) flickerstreak@1: t = nil flickerstreak@1: local len = string_len(s) flickerstreak@1: if len <= 255 then flickerstreak@1: return string_char(byte_o, len) .. s flickerstreak@1: else flickerstreak@1: return string_char(byte_O, math_floor(len / 256), math_mod(len, 256)) .. s flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local t = {} flickerstreak@1: local islist = false flickerstreak@1: local n = #v flickerstreak@1: if n >= 1 then flickerstreak@1: islist = true flickerstreak@1: for k,u in pairs(v) do flickerstreak@1: if type(k) ~= "number" or k < 1 then flickerstreak@1: islist = false flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if islist then flickerstreak@1: n = n * 4 flickerstreak@1: while v[n] == nil do flickerstreak@1: n = n - 1 flickerstreak@1: end flickerstreak@1: for i = 1, n do flickerstreak@1: t[i] = _Serialize(v[i], textToHash) flickerstreak@1: end flickerstreak@1: else flickerstreak@1: local i = 1 flickerstreak@1: for k,u in pairs(v) do flickerstreak@1: t[i] = _Serialize(k, textToHash) flickerstreak@1: t[i+1] = _Serialize(u, textToHash) flickerstreak@1: i = i + 2 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if not notFirst then flickerstreak@1: for k in pairs(recurse) do flickerstreak@1: recurse[k] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local s = table.concat(t) flickerstreak@1: t = nil flickerstreak@1: local len = string_len(s) flickerstreak@1: if islist then flickerstreak@1: if len <= 255 then flickerstreak@1: return string_char(byte_u, len) .. s flickerstreak@1: else flickerstreak@1: return "U" .. string_char(math_floor(len / 256), math_mod(len, 256)) .. s flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if len <= 255 then flickerstreak@1: return "t" .. string_char(len) .. s flickerstreak@1: else flickerstreak@1: return "T" .. string_char(math_floor(len / 256), math_mod(len, 256)) .. s flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Serialize(value, textToHash) flickerstreak@1: if not recurse then flickerstreak@1: recurse = {} flickerstreak@1: end flickerstreak@1: local chunk = _Serialize(value, textToHash) flickerstreak@1: for k in pairs(recurse) do flickerstreak@1: recurse[k] = nil flickerstreak@1: end flickerstreak@1: return chunk flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local Deserialize flickerstreak@1: do flickerstreak@1: local tmp = {} flickerstreak@1: local function _Deserialize(value, position, hashToText) flickerstreak@1: if not position then flickerstreak@1: position = 1 flickerstreak@1: end flickerstreak@1: local x = string_byte(value, position) flickerstreak@1: if x == byte_b then flickerstreak@1: -- false flickerstreak@1: return false, position flickerstreak@1: elseif x == byte_B then flickerstreak@1: -- true flickerstreak@1: return true, position flickerstreak@1: elseif x == byte_nil then flickerstreak@1: -- nil flickerstreak@1: return nil, position flickerstreak@1: elseif x == byte_i then flickerstreak@1: -- 14-byte item link flickerstreak@1: local a1 = string_byte(value, position + 1) flickerstreak@1: local a2 = string_byte(value, position + 2) flickerstreak@1: local b1 = string_byte(value, position + 3) flickerstreak@1: local b2 = string_byte(value, position + 4) flickerstreak@1: local c1 = string_byte(value, position + 5) flickerstreak@1: local c2 = string_byte(value, position + 6) flickerstreak@1: local d1 = string_byte(value, position + 7) flickerstreak@1: local d2 = string_byte(value, position + 8) flickerstreak@1: local e1 = string_byte(value, position + 9) flickerstreak@1: local e2 = string_byte(value, position + 10) flickerstreak@1: local g1 = string_byte(value, position + 11) flickerstreak@1: local g2 = string_byte(value, position + 12) flickerstreak@1: local h1 = string_byte(value, position + 13) flickerstreak@1: local h2 = string_byte(value, position + 14) flickerstreak@1: local A = a1 * 256 + a2 flickerstreak@1: local B = b1 * 256 + b2 flickerstreak@1: local C = c1 * 256 + c2 flickerstreak@1: local D = d1 * 256 + d2 flickerstreak@1: local E = e1 * 256 + e2 flickerstreak@1: local G = g1 * 256 + g2 flickerstreak@1: local H = h1 * 256 + h2 flickerstreak@1: if G >= 32768 then flickerstreak@1: G = G - 65536 flickerstreak@1: end flickerstreak@1: local s = string.format("item:%d:%d:%d:%d:%d:%d:%d:%d", A, B, C, D, E, 0, G, H) flickerstreak@1: local _, link = GetItemInfo(s) flickerstreak@1: return link, position + 14 flickerstreak@1: elseif x == byte_m then flickerstreak@1: local hash = string_byte(value, position + 1) * 65536 + string_byte(value, position + 2) * 256 + string_byte(value, position + 3) flickerstreak@1: return hashToText[hash], position + 3 flickerstreak@1: elseif x == byte_s then flickerstreak@1: -- 0-255-byte string flickerstreak@1: local len = string_byte(value, position + 1) flickerstreak@1: return string.sub(value, position + 2, position + 1 + len), position + 1 + len flickerstreak@1: elseif x == byte_S then flickerstreak@1: -- 256-65535-byte string flickerstreak@1: local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2) flickerstreak@1: return string.sub(value, position + 3, position + 2 + len), position + 2 + len flickerstreak@1: elseif x == 64 --[[byte_inf]] then flickerstreak@1: return inf, position flickerstreak@1: elseif x == 36 --[[byte_ninf]] then flickerstreak@1: return -inf, position flickerstreak@1: elseif x == 33 --[[byte_nan]] then flickerstreak@1: return nan, position flickerstreak@1: elseif x == byte_d then flickerstreak@1: -- 1-byte integer flickerstreak@1: local a = string_byte(value, position + 1) flickerstreak@1: if a >= 128 then flickerstreak@1: a = a - 256 flickerstreak@1: end flickerstreak@1: return a, position + 1 flickerstreak@1: elseif x == byte_D then flickerstreak@1: -- 2-byte integer flickerstreak@1: local a = string_byte(value, position + 1) flickerstreak@1: local b = string_byte(value, position + 2) flickerstreak@1: local N = a * 256 + b flickerstreak@1: if N >= 32768 then flickerstreak@1: N = N - 65536 flickerstreak@1: end flickerstreak@1: return N, position + 2 flickerstreak@1: elseif x == byte_e then flickerstreak@1: -- 4-byte integer flickerstreak@1: local a = string_byte(value, position + 1) flickerstreak@1: local b = string_byte(value, position + 2) flickerstreak@1: local c = string_byte(value, position + 3) flickerstreak@1: local d = string_byte(value, position + 4) flickerstreak@1: local N = a * 16777216 + b * 65536 + c * 256 + d flickerstreak@1: if N >= 2147483648 then flickerstreak@1: N = N - 4294967296 flickerstreak@1: end flickerstreak@1: return N, position + 4 flickerstreak@1: elseif x == byte_E then flickerstreak@1: -- 8-byte integer flickerstreak@1: local a = string_byte(value, position + 1) flickerstreak@1: local b = string_byte(value, position + 2) flickerstreak@1: local c = string_byte(value, position + 3) flickerstreak@1: local d = string_byte(value, position + 4) flickerstreak@1: local e = string_byte(value, position + 5) flickerstreak@1: local f = string_byte(value, position + 6) flickerstreak@1: local g = string_byte(value, position + 7) flickerstreak@1: local h = string_byte(value, position + 8) flickerstreak@1: local N = a * 72057594037927936 + b * 281474976710656 + c * 1099511627776 + d * 4294967296 + e * 16777216 + f * 65536 + g * 256 + h flickerstreak@1: if N >= 9223372036854775808 then flickerstreak@1: N = N - 18446744073709551616 flickerstreak@1: end flickerstreak@1: return N, position + 8 flickerstreak@1: elseif x == byte_plus or x == byte_minus then flickerstreak@1: local a = string_byte(value, position + 1) flickerstreak@1: local b = string_byte(value, position + 2) flickerstreak@1: local c = string_byte(value, position + 3) flickerstreak@1: local d = string_byte(value, position + 4) flickerstreak@1: local e = string_byte(value, position + 5) flickerstreak@1: local f = string_byte(value, position + 6) flickerstreak@1: local g = string_byte(value, position + 7) flickerstreak@1: local h = string_byte(value, position + 8) flickerstreak@1: local N = a * 1099511627776 + b * 4294967296 + c * 16777216 + d * 65536 + e * 256 + f flickerstreak@1: local sign = x flickerstreak@1: local x = math.floor(N / 137438953472) flickerstreak@1: local m = math_mod(N, 137438953472) * 65536 + g * 256 + h flickerstreak@1: local mantissa = m / 9007199254740992 flickerstreak@1: local exp = x - 1023 flickerstreak@1: local val = math.ldexp(mantissa, exp) flickerstreak@1: if sign == byte_minus then flickerstreak@1: return -val, position + 8 flickerstreak@1: end flickerstreak@1: return val, position + 8 flickerstreak@1: elseif x == byte_u or x == byte_U then flickerstreak@1: -- numerically-indexed table flickerstreak@1: local finish flickerstreak@1: local start flickerstreak@1: if x == byte_u then flickerstreak@1: local len = string_byte(value, position + 1) flickerstreak@1: finish = position + 1 + len flickerstreak@1: start = position + 2 flickerstreak@1: else flickerstreak@1: local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2) flickerstreak@1: finish = position + 2 + len flickerstreak@1: start = position + 3 flickerstreak@1: end flickerstreak@1: local t = {} flickerstreak@1: local n = 0 flickerstreak@1: local curr = start - 1 flickerstreak@1: while curr < finish do flickerstreak@1: local v flickerstreak@1: v, curr = _Deserialize(value, curr + 1, hashToText) flickerstreak@1: n = n + 1 flickerstreak@1: t[n] = v flickerstreak@1: end flickerstreak@1: return t, finish flickerstreak@1: elseif x == byte_o or x == byte_O then flickerstreak@1: -- numerically-indexed table flickerstreak@1: local finish flickerstreak@1: local start flickerstreak@1: if x == byte_o then flickerstreak@1: local len = string_byte(value, position + 1) flickerstreak@1: finish = position + 1 + len flickerstreak@1: start = position + 2 flickerstreak@1: else flickerstreak@1: local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2) flickerstreak@1: finish = position + 2 + len flickerstreak@1: start = position + 3 flickerstreak@1: end flickerstreak@1: local hash = string_byte(value, start) * 65536 + string_byte(value, start + 1) * 256 + string_byte(value, start + 2) flickerstreak@1: local curr = start + 2 flickerstreak@1: if not AceComm.classes[hash] then flickerstreak@1: return nil, finish flickerstreak@1: end flickerstreak@1: local class = AceComm.classes[hash] flickerstreak@1: if type(class.Deserialize) ~= "function" or type(class.prototype.Serialize) ~= "function" then flickerstreak@1: return nil, finish flickerstreak@1: end flickerstreak@1: local n = 0 flickerstreak@1: while curr < finish do flickerstreak@1: local v flickerstreak@1: v, curr = _Deserialize(value, curr + 1, hashToText) flickerstreak@1: n = n + 1 flickerstreak@1: tmp[n] = v flickerstreak@1: end flickerstreak@1: local object = class:Deserialize(unpack(tmp)) flickerstreak@1: for i = 1, n do flickerstreak@1: tmp[i] = nil flickerstreak@1: end flickerstreak@1: return object, finish flickerstreak@1: elseif x == byte_t or x == byte_T then flickerstreak@1: -- table flickerstreak@1: local finish flickerstreak@1: local start flickerstreak@1: if x == byte_t then flickerstreak@1: local len = string_byte(value, position + 1) flickerstreak@1: finish = position + 1 + len flickerstreak@1: start = position + 2 flickerstreak@1: else flickerstreak@1: local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2) flickerstreak@1: finish = position + 2 + len flickerstreak@1: start = position + 3 flickerstreak@1: end flickerstreak@1: local t = {} flickerstreak@1: local curr = start - 1 flickerstreak@1: while curr < finish do flickerstreak@1: local key, l = _Deserialize(value, curr + 1, hashToText) flickerstreak@1: local value, m = _Deserialize(value, l + 1, hashToText) flickerstreak@1: curr = m flickerstreak@1: t[key] = value flickerstreak@1: end flickerstreak@1: if type(t.n) ~= "number" then flickerstreak@1: local i = 1 flickerstreak@1: while t[i] ~= nil do flickerstreak@1: i = i + 1 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return t, finish flickerstreak@1: else flickerstreak@1: error("Improper serialized value provided") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Deserialize(value, hashToText) flickerstreak@1: local ret,msg = pcall(_Deserialize, value, nil, hashToText) flickerstreak@1: if ret then flickerstreak@1: return msg flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function GetCurrentGroupDistribution() flickerstreak@1: if MiniMapBattlefieldFrame.status == "active" then flickerstreak@1: return "BATTLEGROUND" flickerstreak@1: elseif UnitInRaid("player") then flickerstreak@1: return "RAID" flickerstreak@1: elseif UnitInParty("player") then flickerstreak@1: return "PARTY" flickerstreak@1: else flickerstreak@1: return nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function IsInDistribution(dist, customChannel) flickerstreak@1: if dist == "GROUP" then flickerstreak@1: return GetCurrentGroupDistribution() and true or false flickerstreak@1: elseif dist == "BATTLEGROUND" then flickerstreak@1: return MiniMapBattlefieldFrame.status == "active" flickerstreak@1: elseif dist == "RAID" then flickerstreak@1: return UnitInRaid("player") == 1 flickerstreak@1: elseif dist == "PARTY" then flickerstreak@1: return UnitInParty("player") == 1 flickerstreak@1: elseif dist == "GUILD" then flickerstreak@1: return IsInGuild() == 1 flickerstreak@1: elseif dist == "GLOBAL" then flickerstreak@1: return IsInChannel("AceComm") flickerstreak@1: elseif dist == "ZONE" then flickerstreak@1: return IsInChannel(GetCurrentZoneChannel()) flickerstreak@1: elseif dist == "WHISPER" then flickerstreak@1: return true flickerstreak@1: elseif dist == "CUSTOM" then flickerstreak@1: return IsInChannel(customChannel) flickerstreak@1: end flickerstreak@1: error("unknown distribution: " .. dist, 2) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:RegisterComm(prefix, distribution, method, a4) flickerstreak@1: AceComm:argCheck(prefix, 2, "string") flickerstreak@1: AceComm:argCheck(distribution, 3, "string") flickerstreak@1: if distribution ~= "GLOBAL" and distribution ~= "WHISPER" and distribution ~= "PARTY" and distribution ~= "RAID" and distribution ~= "GUILD" and distribution ~= "BATTLEGROUND" and distribution ~= "GROUP" and distribution ~= "ZONE" and distribution ~= "CUSTOM" then flickerstreak@1: AceComm:error('Argument #3 to `RegisterComm\' must be either "GLOBAL", "ZONE", "WHISPER", "PARTY", "RAID", "GUILD", "BATTLEGROUND", "GROUP", or "CUSTOM". %q is not appropriate', distribution) flickerstreak@1: end flickerstreak@1: local customChannel flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: customChannel, method = method, a4 flickerstreak@1: AceComm:argCheck(customChannel, 4, "string") flickerstreak@1: if string_len(customChannel) == 0 then flickerstreak@1: AceComm:error('Argument #4 to `RegisterComm\' must be a non-zero-length string.') flickerstreak@1: elseif string_find(customChannel, "%s") then flickerstreak@1: AceComm:error('Argument #4 to `RegisterComm\' must not have spaces.') flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if self == AceComm then flickerstreak@1: AceComm:argCheck(method, customChannel and 5 or 4, "function", "table") flickerstreak@1: self = method flickerstreak@1: else flickerstreak@1: AceComm:argCheck(method, customChannel and 5 or 4, "string", "function", "table", "nil") flickerstreak@1: end flickerstreak@1: if not method then flickerstreak@1: method = "OnCommReceive" flickerstreak@1: end flickerstreak@1: if type(method) == "string" and type(self[method]) ~= "function" and type(self[method]) ~= "table" then flickerstreak@1: AceEvent:error("Cannot register comm %q to method %q, it does not exist", prefix, method) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local registry = AceComm_registry flickerstreak@1: if not registry[distribution] then flickerstreak@1: registry[distribution] = {} flickerstreak@1: end flickerstreak@1: if customChannel then flickerstreak@1: customChannel = "AceComm" .. customChannel flickerstreak@1: if not registry[distribution][customChannel] then flickerstreak@1: registry[distribution][customChannel] = {} flickerstreak@1: end flickerstreak@1: if not registry[distribution][customChannel][prefix] then flickerstreak@1: registry[distribution][customChannel][prefix] = {} flickerstreak@1: end flickerstreak@1: registry[distribution][customChannel][prefix][self] = method flickerstreak@1: else flickerstreak@1: if not registry[distribution][prefix] then flickerstreak@1: registry[distribution][prefix] = {} flickerstreak@1: end flickerstreak@1: registry[distribution][prefix][self] = method flickerstreak@1: end flickerstreak@1: flickerstreak@1: RefixAceCommChannelsAndEvents() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:UnregisterComm(prefix, distribution, customChannel) flickerstreak@1: AceComm:argCheck(prefix, 2, "string") flickerstreak@1: AceComm:argCheck(distribution, 3, "string", "nil") flickerstreak@1: if distribution and distribution ~= "GLOBAL" and distribution ~= "WHISPER" and distribution ~= "PARTY" and distribution ~= "RAID" and distribution ~= "GUILD" and distribution ~= "BATTLEGROUND" and distribution ~= "GROUP" and distribution ~= "CUSTOM" then flickerstreak@1: AceComm:error('Argument #3 to `UnregisterComm\' must be either nil, "GLOBAL", "WHISPER", "PARTY", "RAID", "GUILD", "BATTLEGROUND", "GROUP", or "CUSTOM". %q is not appropriate', distribution) flickerstreak@1: end flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: AceComm:argCheck(customChannel, 3, "string") flickerstreak@1: if string_len(customChannel) == 0 then flickerstreak@1: AceComm:error('Argument #3 to `UnregisterComm\' must be a non-zero-length string.') flickerstreak@1: end flickerstreak@1: else flickerstreak@1: AceComm:argCheck(customChannel, 3, "nil") flickerstreak@1: end flickerstreak@1: flickerstreak@1: local registry = AceComm_registry flickerstreak@1: if not distribution then flickerstreak@1: for k,v in pairs(registry) do flickerstreak@1: if k == "CUSTOM" then flickerstreak@1: for l,u in pairs(v) do flickerstreak@1: if u[prefix] and u[prefix][self] then flickerstreak@1: AceComm.UnregisterComm(self, prefix, k, string.sub(l, 8)) flickerstreak@1: if not registry[k] then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if v[prefix] and v[prefix][self] then flickerstreak@1: AceComm.UnregisterComm(self, prefix, k) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return flickerstreak@1: end flickerstreak@1: if self == AceComm then flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: error(string_format("Cannot unregister comm %q::%q. Improperly unregistering from AceComm-2.0.", distribution, customChannel), 2) flickerstreak@1: else flickerstreak@1: error(string_format("Cannot unregister comm %q. Improperly unregistering from AceComm-2.0.", distribution), 2) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: customChannel = "AceComm" .. customChannel flickerstreak@1: if not registry[distribution] or not registry[distribution][customChannel] or not registry[distribution][customChannel][prefix] or not registry[distribution][customChannel][prefix][self] then flickerstreak@1: AceComm:error("Cannot unregister comm %q. %q is not registered with it.", distribution, self) flickerstreak@1: end flickerstreak@1: registry[distribution][customChannel][prefix][self] = nil flickerstreak@1: flickerstreak@1: if not next(registry[distribution][customChannel][prefix]) then flickerstreak@1: registry[distribution][customChannel][prefix] = nil flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not next(registry[distribution][customChannel]) then flickerstreak@1: registry[distribution][customChannel] = nil flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if not registry[distribution] or not registry[distribution][prefix] or not registry[distribution][prefix][self] then flickerstreak@1: AceComm:error("Cannot unregister comm %q. %q is not registered with it.", distribution, self) flickerstreak@1: end flickerstreak@1: registry[distribution][prefix][self] = nil flickerstreak@1: flickerstreak@1: if not next(registry[distribution][prefix]) then flickerstreak@1: registry[distribution][prefix] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not next(registry[distribution]) then flickerstreak@1: registry[distribution] = nil flickerstreak@1: end flickerstreak@1: flickerstreak@1: RefixAceCommChannelsAndEvents() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:UnregisterAllComms() flickerstreak@1: local registry = AceComm_registry flickerstreak@1: for k, distribution in pairs(registry) do flickerstreak@1: if k == "CUSTOM" then flickerstreak@1: for l, channel in pairs(distribution) do flickerstreak@1: local j = next(channel) flickerstreak@1: while j ~= nil do flickerstreak@1: local prefix = channel[j] flickerstreak@1: if prefix[self] then flickerstreak@1: AceComm.UnregisterComm(self, j) flickerstreak@1: if distribution[l] and registry[k] then flickerstreak@1: j = next(channel) flickerstreak@1: else flickerstreak@1: l = nil flickerstreak@1: k = nil flickerstreak@1: break flickerstreak@1: end flickerstreak@1: else flickerstreak@1: j = next(channel, j) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if k == nil then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: local j = next(distribution) flickerstreak@1: while j ~= nil do flickerstreak@1: local prefix = distribution[j] flickerstreak@1: if prefix[self] then flickerstreak@1: AceComm.UnregisterComm(self, j) flickerstreak@1: if registry[k] then flickerstreak@1: j = next(distribution) flickerstreak@1: else flickerstreak@1: k = nil flickerstreak@1: break flickerstreak@1: end flickerstreak@1: else flickerstreak@1: j = next(distribution, j) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:IsCommRegistered(prefix, distribution, customChannel) flickerstreak@1: AceComm:argCheck(prefix, 2, "string") flickerstreak@1: AceComm:argCheck(distribution, 3, "string", "nil") flickerstreak@1: if distribution and distribution ~= "GLOBAL" and distribution ~= "WHISPER" and distribution ~= "PARTY" and distribution ~= "RAID" and distribution ~= "GUILD" and distribution ~= "BATTLEGROUND" and distribution ~= "GROUP" and distribution ~= "ZONE" and distribution ~= "CUSTOM" then flickerstreak@1: AceComm:error('Argument #3 to `IsCommRegistered\' must be either "GLOBAL", "WHISPER", "PARTY", "RAID", "GUILD", "BATTLEGROUND", "GROUP", "ZONE", or "CUSTOM". %q is not appropriate', distribution) flickerstreak@1: end flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: AceComm:argCheck(customChannel, 4, "nil", "string") flickerstreak@1: if customChannel == "" then flickerstreak@1: AceComm:error('Argument #4 to `IsCommRegistered\' must be a non-zero-length string or nil.') flickerstreak@1: end flickerstreak@1: else flickerstreak@1: AceComm:argCheck(customChannel, 4, "nil") flickerstreak@1: end flickerstreak@1: local registry = AceComm_registry flickerstreak@1: if not distribution then flickerstreak@1: for k,v in pairs(registry) do flickerstreak@1: if k == "CUSTOM" then flickerstreak@1: for l,u in pairs(v) do flickerstreak@1: if u[prefix] and u[prefix][self] then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if v[prefix] and v[prefix][self] then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return false flickerstreak@1: elseif distribution == "CUSTOM" and not customChannel then flickerstreak@1: if not registry[distribution] then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: for l,u in pairs(registry[distribution]) do flickerstreak@1: if u[prefix] and u[prefix][self] then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return false flickerstreak@1: elseif distribution == "CUSTOM" then flickerstreak@1: customChannel = "AceComm" .. customChannel flickerstreak@1: return registry[distribution] and registry[distribution][customChannel] and registry[distribution][customChannel][prefix] and registry[distribution][customChannel][prefix][self] and true or false flickerstreak@1: end flickerstreak@1: return registry[distribution] and registry[distribution][prefix] and registry[distribution][prefix][self] and true or false flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:OnEmbedDisable(target) flickerstreak@1: self.UnregisterAllComms(target) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local id = byte_Z flickerstreak@1: flickerstreak@1: local function encodedChar(x) flickerstreak@1: if x == 10 then flickerstreak@1: return "°\011" flickerstreak@1: elseif x == 0 then flickerstreak@1: return "\255" flickerstreak@1: elseif x == 255 then flickerstreak@1: return "°\254" flickerstreak@1: elseif x == 124 then flickerstreak@1: return "°\125" flickerstreak@1: elseif x == byte_s then flickerstreak@1: return "\015" flickerstreak@1: elseif x == byte_S then flickerstreak@1: return "\020" flickerstreak@1: elseif x == 15 then flickerstreak@1: return "°\016" flickerstreak@1: elseif x == 20 then flickerstreak@1: return "°\021" flickerstreak@1: elseif x == byte_deg then flickerstreak@1: return "°±" flickerstreak@1: elseif x == 37 then flickerstreak@1: return "°\038" flickerstreak@1: end flickerstreak@1: return string_char(x) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function soberEncodedChar(x) flickerstreak@1: if x == 10 then flickerstreak@1: return "°\011" flickerstreak@1: elseif x == 0 then flickerstreak@1: return "\255" flickerstreak@1: elseif x == 255 then flickerstreak@1: return "°\254" flickerstreak@1: elseif x == 124 then flickerstreak@1: return "°\125" flickerstreak@1: elseif x == byte_deg then flickerstreak@1: return "°±" flickerstreak@1: elseif x == 37 then flickerstreak@1: return "°\038" flickerstreak@1: end flickerstreak@1: return string_char(x) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function SendMessage(prefix, priority, distribution, person, message, textToHash) flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: person = "AceComm" .. person flickerstreak@1: end flickerstreak@1: if not IsInDistribution(distribution, person) then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: if distribution == "GROUP" then flickerstreak@1: distribution = GetCurrentGroupDistribution() flickerstreak@1: if not distribution then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if id == byte_Z then flickerstreak@1: id = byte_a flickerstreak@1: elseif id == byte_z then flickerstreak@1: id = byte_A flickerstreak@1: else flickerstreak@1: id = id + 1 flickerstreak@1: end flickerstreak@1: if id == byte_s or id == byte_S then flickerstreak@1: id = id + 1 flickerstreak@1: end flickerstreak@1: local id = string_char(id) flickerstreak@1: local drunk = distribution == "GLOBAL" or distribution == "WHISPER" or distribution == "ZONE" or distribution == "CUSTOM" flickerstreak@1: prefix = Encode(prefix, drunk) flickerstreak@1: message = Serialize(message, textToHash) flickerstreak@1: message = Encode(message, drunk) flickerstreak@1: local headerLen = string_len(prefix) + 6 flickerstreak@1: local messageLen = string_len(message) flickerstreak@1: if distribution == "WHISPER" then flickerstreak@1: AceComm.recentWhispers[string.lower(person)] = GetTime() flickerstreak@1: end flickerstreak@1: local max = math_floor(messageLen / (250 - headerLen) + 1) flickerstreak@1: if max > 1 then flickerstreak@1: local segment = math_floor(messageLen / max + 0.5) flickerstreak@1: local last = 0 flickerstreak@1: local good = true flickerstreak@1: for i = 1, max do flickerstreak@1: local bit flickerstreak@1: if i == max then flickerstreak@1: bit = string_sub(message, last + 1) flickerstreak@1: else flickerstreak@1: local next = segment * i flickerstreak@1: if string_byte(message, next) == byte_deg then flickerstreak@1: next = next + 1 flickerstreak@1: end flickerstreak@1: bit = string_sub(message, last + 1, next) flickerstreak@1: last = next flickerstreak@1: end flickerstreak@1: if distribution == "WHISPER" then flickerstreak@1: bit = "/" .. prefix .. "\t" .. id .. encodedChar(i) .. encodedChar(max) .. "\t" .. bit .. "°" flickerstreak@1: ChatThrottleLib:SendChatMessage(priority, prefix, bit, "WHISPER", nil, person) flickerstreak@1: elseif distribution == "GLOBAL" or distribution == "ZONE" or distribution == "CUSTOM" then flickerstreak@1: bit = prefix .. "\t" .. id .. encodedChar(i) .. encodedChar(max) .. "\t" .. bit .. "°" flickerstreak@1: local channel flickerstreak@1: if distribution == "GLOBAL" then flickerstreak@1: channel = "AceComm" flickerstreak@1: elseif distribution == "ZONE" then flickerstreak@1: channel = GetCurrentZoneChannel() flickerstreak@1: elseif distribution == "CUSTOM" then flickerstreak@1: channel = person flickerstreak@1: end flickerstreak@1: local index = GetChannelName(channel) flickerstreak@1: if index and index > 0 then flickerstreak@1: ChatThrottleLib:SendChatMessage(priority, prefix, bit, "CHANNEL", nil, index) flickerstreak@1: else flickerstreak@1: good = false flickerstreak@1: end flickerstreak@1: else flickerstreak@1: bit = id .. soberEncodedChar(i) .. soberEncodedChar(max) .. "\t" .. bit flickerstreak@1: ChatThrottleLib:SendAddonMessage(priority, prefix, bit, distribution) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return good flickerstreak@1: else flickerstreak@1: if distribution == "WHISPER" then flickerstreak@1: message = "/" .. prefix .. "\t" .. id .. string_char(1) .. string_char(1) .. "\t" .. message .. "°" flickerstreak@1: ChatThrottleLib:SendChatMessage(priority, prefix, message, "WHISPER", nil, person) flickerstreak@1: return true flickerstreak@1: elseif distribution == "GLOBAL" or distribution == "ZONE" or distribution == "CUSTOM" then flickerstreak@1: message = prefix .. "\t" .. id .. string_char(1) .. string_char(1) .. "\t" .. message .. "°" flickerstreak@1: local channel flickerstreak@1: if distribution == "GLOBAL" then flickerstreak@1: channel = "AceComm" flickerstreak@1: elseif distribution == "ZONE" then flickerstreak@1: channel = GetCurrentZoneChannel() flickerstreak@1: elseif distribution == "CUSTOM" then flickerstreak@1: channel = person flickerstreak@1: end flickerstreak@1: local index = GetChannelName(channel) flickerstreak@1: if index and index > 0 then flickerstreak@1: ChatThrottleLib:SendChatMessage(priority, prefix, message, "CHANNEL", nil, index) flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: else flickerstreak@1: message = id .. string_char(1) .. string_char(1) .. "\t" .. message flickerstreak@1: ChatThrottleLib:SendAddonMessage(priority, prefix, message, distribution) flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: flickerstreak@1: local tmp = {} flickerstreak@1: function AceComm:SendPrioritizedCommMessage(priority, distribution, person, ...) flickerstreak@1: AceComm:argCheck(priority, 2, "string") flickerstreak@1: if priority ~= "NORMAL" and priority ~= "BULK" and priority ~= "ALERT" then flickerstreak@1: AceComm:error('Argument #2 to `SendPrioritizedCommMessage\' must be either "NORMAL", "BULK", or "ALERT"') flickerstreak@1: end flickerstreak@1: AceComm:argCheck(distribution, 3, "string") flickerstreak@1: local includePerson = true flickerstreak@1: if distribution == "WHISPER" or distribution == "CUSTOM" then flickerstreak@1: includePerson = false flickerstreak@1: AceComm:argCheck(person, 4, "string") flickerstreak@1: if string_len(person) == 0 then flickerstreak@1: AceComm:error("Argument #4 to `SendPrioritizedCommMessage' must be a non-zero-length string") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if self == AceComm then flickerstreak@1: AceComm:error("Cannot send a comm message from AceComm directly.") flickerstreak@1: end flickerstreak@1: if distribution and distribution ~= "GLOBAL" and distribution ~= "WHISPER" and distribution ~= "PARTY" and distribution ~= "RAID" and distribution ~= "GUILD" and distribution ~= "BATTLEGROUND" and distribution ~= "GROUP" and distribution ~= "ZONE" and distribution ~= "CUSTOM" then flickerstreak@1: AceComm:error('Argument #4 to `SendPrioritizedCommMessage\' must be either nil, "GLOBAL", "ZONE", "WHISPER", "PARTY", "RAID", "GUILD", "BATTLEGROUND", "GROUP", or "CUSTOM". %q is not appropriate', distribution) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local prefix = self.commPrefix flickerstreak@1: if type(prefix) ~= "string" then flickerstreak@1: AceComm:error("`SetCommPrefix' must be called before sending a message.") flickerstreak@1: end flickerstreak@1: flickerstreak@1: local message flickerstreak@1: flickerstreak@1: if includePerson and select('#', ...) == 0 and type(person) ~= "table" then flickerstreak@1: message = person flickerstreak@1: elseif not includePerson and select('#', ...) == 1 and type((...)) ~= "table" then flickerstreak@1: message = ... flickerstreak@1: else flickerstreak@1: message = tmp flickerstreak@1: local n = 1 flickerstreak@1: if includePerson then flickerstreak@1: tmp[1] = person flickerstreak@1: n = 2 flickerstreak@1: end flickerstreak@1: for i = 1, select('#', ...) do flickerstreak@1: tmp[n] = select(i, ...) flickerstreak@1: n = n + 1 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local ret = SendMessage(AceComm.prefixTextToHash[prefix], priority, distribution, person, message, self.commMemoTextToHash) flickerstreak@1: flickerstreak@1: if message == tmp then flickerstreak@1: local n = #tmp flickerstreak@1: for i = 1, n do flickerstreak@1: tmp[i] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: return ret flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:SendCommMessage(distribution, person, ...) flickerstreak@1: AceComm:argCheck(distribution, 2, "string") flickerstreak@1: local includePerson = true flickerstreak@1: if distribution == "WHISPER" or distribution == "CUSTOM" then flickerstreak@1: includePerson = false flickerstreak@1: AceComm:argCheck(person, 3, "string") flickerstreak@1: if string_len(person) == 0 then flickerstreak@1: AceComm:error("Argument #3 to `SendCommMessage' must be a non-zero-length string") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if self == AceComm then flickerstreak@1: AceComm:error("Cannot send a comm message from AceComm directly.") flickerstreak@1: end flickerstreak@1: if distribution and distribution ~= "GLOBAL" and distribution ~= "WHISPER" and distribution ~= "PARTY" and distribution ~= "RAID" and distribution ~= "GUILD" and distribution ~= "BATTLEGROUND" and distribution ~= "GROUP" and distribution ~= "ZONE" and distribution ~= "CUSTOM" then flickerstreak@1: AceComm:error('Argument #2 to `SendCommMessage\' must be either nil, "GLOBAL", "ZONE", "WHISPER", "PARTY", "RAID", "GUILD", "BATTLEGROUND", "GROUP", or "CUSTOM". %q is not appropriate', distribution) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local prefix = self.commPrefix flickerstreak@1: if type(prefix) ~= "string" then flickerstreak@1: AceComm:error("`SetCommPrefix' must be called before sending a message.") flickerstreak@1: end flickerstreak@1: flickerstreak@1: if includePerson and select('#', ...) == 0 and type(person) ~= "table" then flickerstreak@1: message = person flickerstreak@1: elseif not includePerson and select('#', ...) == 1 and type((...)) ~= "table" then flickerstreak@1: message = ... flickerstreak@1: else flickerstreak@1: message = tmp flickerstreak@1: local n = 1 flickerstreak@1: if includePerson then flickerstreak@1: tmp[1] = person flickerstreak@1: n = 2 flickerstreak@1: end flickerstreak@1: for i = 1, select('#', ...) do flickerstreak@1: tmp[n] = select(i, ...) flickerstreak@1: n = n + 1 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local priority = self.commPriority or "NORMAL" flickerstreak@1: flickerstreak@1: local ret = SendMessage(AceComm.prefixTextToHash[prefix], priority, distribution, person, message, self.commMemoTextToHash) flickerstreak@1: flickerstreak@1: if message == tmp then flickerstreak@1: local n = #tmp flickerstreak@1: for i = 1, n do flickerstreak@1: tmp[i] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: return ret flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:SetDefaultCommPriority(priority) flickerstreak@1: AceComm:argCheck(priority, 2, "string") flickerstreak@1: if priority ~= "NORMAL" and priority ~= "BULK" and priority ~= "ALERT" then flickerstreak@1: AceComm:error('Argument #2 must be either "NORMAL", "BULK", or "ALERT"') flickerstreak@1: end flickerstreak@1: flickerstreak@1: if self.commPriority then flickerstreak@1: AceComm:error("Cannot call `SetDefaultCommPriority' more than once") flickerstreak@1: end flickerstreak@1: flickerstreak@1: self.commPriority = priority flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:SetCommPrefix(prefix) flickerstreak@1: AceComm:argCheck(prefix, 2, "string") flickerstreak@1: flickerstreak@1: if self.commPrefix then flickerstreak@1: AceComm:error("Cannot call `SetCommPrefix' more than once.") flickerstreak@1: end flickerstreak@1: flickerstreak@1: if AceComm.prefixes[prefix] then flickerstreak@1: AceComm:error("Cannot set prefix to %q, it is already in use.", prefix) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local hash = TailoredBinaryCheckSum(prefix) flickerstreak@1: if AceComm.prefixHashToText[hash] then flickerstreak@1: AceComm:error("Cannot set prefix to %q, its hash is used by another prefix: %q", prefix, AceComm.prefixHashToText[hash]) flickerstreak@1: end flickerstreak@1: flickerstreak@1: AceComm.prefixes[prefix] = true flickerstreak@1: self.commPrefix = prefix flickerstreak@1: AceComm.prefixHashToText[hash] = prefix flickerstreak@1: AceComm.prefixTextToHash[prefix] = hash flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:RegisterMemoizations(values) flickerstreak@1: AceComm:argCheck(values, 2, "table") flickerstreak@1: for k,v in pairs(values) do flickerstreak@1: if type(k) ~= "number" then flickerstreak@1: AceComm:error("Bad argument #2 to `RegisterMemoizations'. All keys must be numbers") flickerstreak@1: elseif type(v) ~= "string" then flickerstreak@1: AceComm:error("Bad argument #2 to `RegisterMemoizations'. All values must be strings") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if self.commMemoHashToText or self.commMemoTextToHash then flickerstreak@1: AceComm:error("You can only call `RegisterMemoizations' once.") flickerstreak@1: elseif not self.commPrefix then flickerstreak@1: AceComm:error("You can only call `RegisterCommPrefix' before calling `RegisterMemoizations'.") flickerstreak@1: elseif AceComm.prefixMemoizations[self.commPrefix] then flickerstreak@1: AceComm:error("Another addon with prefix %q has already registered memoizations.", self.commPrefix) flickerstreak@1: end flickerstreak@1: local hashToText = {} flickerstreak@1: local textToHash = {} flickerstreak@1: for _,text in ipairs(values) do flickerstreak@1: local hash = TailoredNumericCheckSum(text) flickerstreak@1: if hashToText[hash] then flickerstreak@1: AceComm:error("%q and %q have the same checksum. You must remove one of them for memoization to work properly", hashToText[hash], text) flickerstreak@1: else flickerstreak@1: textToHash[text] = hash flickerstreak@1: hashToText[hash] = text flickerstreak@1: end flickerstreak@1: end flickerstreak@1: values = nil flickerstreak@1: self.commMemoHashToText = hashToText flickerstreak@1: self.commMemoTextToHash = textToHash flickerstreak@1: AceComm.prefixMemoizations[self.commPrefix] = hashToText flickerstreak@1: end flickerstreak@1: flickerstreak@1: local lastCheck = GetTime() flickerstreak@1: local function CheckRefix() flickerstreak@1: if GetTime() - lastCheck >= 120 then flickerstreak@1: lastCheck = GetTime() flickerstreak@1: RefixAceCommChannelsAndEvents() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local stack = setmetatable({}, {__mode='k'}) flickerstreak@1: local function HandleMessage(prefix, message, distribution, sender, customChannel) flickerstreak@1: local isGroup = GetCurrentGroupDistribution() == distribution flickerstreak@1: local isCustom = distribution == "CUSTOM" flickerstreak@1: if (not AceComm_registry[distribution] and (not isGroup or not AceComm_registry.GROUP)) or (isCustom and not AceComm_registry.CUSTOM[customChannel]) then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: local _, id, current, max flickerstreak@1: if not message then flickerstreak@1: if distribution == "WHISPER" then flickerstreak@1: _,_, prefix, id, current, max, message = string_find(prefix, "^/(...)\t(.)(.)(.)\t(.*)$") flickerstreak@1: else flickerstreak@1: _,_, prefix, id, current, max, message = string_find(prefix, "^(...)\t(.)(.)(.)\t(.*)$") flickerstreak@1: end flickerstreak@1: prefix = AceComm.prefixHashToText[prefix] flickerstreak@1: if not prefix then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: if isCustom then flickerstreak@1: if not AceComm_registry.CUSTOM[customChannel][prefix] then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if (not AceComm_registry[distribution] or not AceComm_registry[distribution][prefix]) and (not isGroup or not AceComm_registry.GROUP or not AceComm_registry.GROUP[prefix]) then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: _,_, id, current, max, message = string_find(message, "^(.)(.)(.)\t(.*)$") flickerstreak@1: end flickerstreak@1: if not message then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: local smallCustomChannel = customChannel and string_sub(customChannel, 8) flickerstreak@1: current = string_byte(current) flickerstreak@1: max = string_byte(max) flickerstreak@1: if max > 1 then flickerstreak@1: local queue = AceComm.recvQueue flickerstreak@1: local x flickerstreak@1: if distribution == "CUSTOM" then flickerstreak@1: x = prefix .. ":" .. sender .. distribution .. customChannel .. id flickerstreak@1: else flickerstreak@1: x = prefix .. ":" .. sender .. distribution .. id flickerstreak@1: end flickerstreak@1: if not queue[x] then flickerstreak@1: if current ~= 1 then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: local t = next(stack) or {} flickerstreak@1: stack[t] = nil flickerstreak@1: queue[x] = t flickerstreak@1: end flickerstreak@1: local chunk = queue[x] flickerstreak@1: chunk.time = GetTime() flickerstreak@1: chunk[current] = message flickerstreak@1: if current == max then flickerstreak@1: message = table_concat(chunk) flickerstreak@1: local t = queue[x] flickerstreak@1: queue[x] = nil flickerstreak@1: for k in pairs(t) do flickerstreak@1: t[k] = nil flickerstreak@1: end flickerstreak@1: stack[t] = true flickerstreak@1: else flickerstreak@1: return flickerstreak@1: end flickerstreak@1: end flickerstreak@1: message = Deserialize(message, AceComm.prefixMemoizations[prefix]) flickerstreak@1: local isTable = type(message) == "table" flickerstreak@1: local n flickerstreak@1: if isTable then flickerstreak@1: n = #message * 4 flickerstreak@1: if n < 40 then flickerstreak@1: n = 40 flickerstreak@1: end flickerstreak@1: while message[n] == nil do flickerstreak@1: n = n - 1 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if AceComm_registry[distribution] then flickerstreak@1: if isTable then flickerstreak@1: if isCustom then flickerstreak@1: if AceComm_registry.CUSTOM[customChannel][prefix] then flickerstreak@1: for k,v in pairs(AceComm_registry.CUSTOM[customChannel][prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = f[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(k, prefix, sender, distribution, smallCustomChannel, unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, distribution, smallCustomChannel, unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = v[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(prefix, sender, distribution, smallCustomChannel, unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, distribution, smallCustomChannel, unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if AceComm_registry[distribution][prefix] then flickerstreak@1: for k,v in pairs(AceComm_registry[distribution][prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = f[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(k, prefix, sender, distribution, unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, distribution, unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = v[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(prefix, sender, distribution, unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, distribution, unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if isCustom then flickerstreak@1: if AceComm_registry.CUSTOM[customChannel][prefix] then flickerstreak@1: for k,v in pairs(AceComm_registry.CUSTOM[customChannel][prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local g = f[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, distribution, smallCustomChannel) flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, distribution, smallCustomChannel, message) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local g = v[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, distribution, smallCustomChannel) flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, distribution, smallCustomChannel, message) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: if AceComm_registry[distribution][prefix] then flickerstreak@1: for k,v in pairs(AceComm_registry[distribution][prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local g = f[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, distribution) flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, distribution, message) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local g = v[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, distribution) flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, distribution, message) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if isGroup and AceComm_registry.GROUP and AceComm_registry.GROUP[prefix] then flickerstreak@1: if isTable then flickerstreak@1: for k,v in pairs(AceComm_registry.GROUP[prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = f[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(k, prefix, sender, "GROUP", unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, "GROUP", unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local i = 1 flickerstreak@1: local g = v[message[i]] flickerstreak@1: while g do flickerstreak@1: if type(g) ~= "table" then -- function flickerstreak@1: g(prefix, sender, "GROUP", unpack(message, i+1, n)) flickerstreak@1: break flickerstreak@1: else flickerstreak@1: i = i + 1 flickerstreak@1: g = g[message[i]] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, "GROUP", unpack(message, 1, n)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: else flickerstreak@1: for k,v in pairs(AceComm_registry.GROUP[prefix]) do flickerstreak@1: local type_v = type(v) flickerstreak@1: if type_v == "string" then flickerstreak@1: local f = k[v] flickerstreak@1: if type(f) == "table" then flickerstreak@1: local g = f[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, "GROUP") flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: f(k, prefix, sender, "GROUP", message) flickerstreak@1: end flickerstreak@1: elseif type_v == "table" then flickerstreak@1: local g = v[message] flickerstreak@1: if g and type(g) == "function" then flickerstreak@1: g(k, prefix, sender, "GROUP") flickerstreak@1: end flickerstreak@1: else -- function flickerstreak@1: v(prefix, sender, "GROUP", message) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_ADDON(prefix, message, distribution, sender) flickerstreak@1: if sender == player then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: prefix = self.prefixHashToText[prefix] flickerstreak@1: if not prefix then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: local isGroup = GetCurrentGroupDistribution() == distribution flickerstreak@1: if not AceComm_registry[distribution] and (not isGroup or not AceComm_registry.GROUP) then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: prefix = Decode(prefix) flickerstreak@1: if (not AceComm_registry[distribution] or not AceComm_registry[distribution][prefix]) and (not isGroup or not AceComm_registry.GROUP or not AceComm_registry.GROUP[prefix]) then flickerstreak@1: return CheckRefix() flickerstreak@1: end flickerstreak@1: message = Decode(message) flickerstreak@1: return HandleMessage(prefix, message, distribution, sender) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_WHISPER(text, sender) flickerstreak@1: if not string_find(text, "^/") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: text = Decode(text, true) flickerstreak@1: return HandleMessage(text, nil, "WHISPER", sender) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_CHANNEL(text, sender, _, _, _, _, _, _, channel) flickerstreak@1: if sender == player or not string_find(channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: text = Decode(text, true) flickerstreak@1: local distribution flickerstreak@1: local customChannel flickerstreak@1: if channel == "AceComm" then flickerstreak@1: distribution = "GLOBAL" flickerstreak@1: elseif channel == GetCurrentZoneChannel() then flickerstreak@1: distribution = "ZONE" flickerstreak@1: else flickerstreak@1: distribution = "CUSTOM" flickerstreak@1: customChannel = channel flickerstreak@1: end flickerstreak@1: return HandleMessage(text, nil, distribution, sender, customChannel) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:IsUserInChannel(userName, distribution, customChannel) flickerstreak@1: AceComm:argCheck(userName, 2, "string", "nil") flickerstreak@1: if not userName then flickerstreak@1: userName = player flickerstreak@1: end flickerstreak@1: AceComm:argCheck(distribution, 3, "string") flickerstreak@1: local channel flickerstreak@1: if distribution == "GLOBAL" then flickerstreak@1: channel = "AceComm" flickerstreak@1: elseif distribution == "ZONE" then flickerstreak@1: channel = GetCurrentZoneChannel() flickerstreak@1: elseif distribution == "CUSTOM" then flickerstreak@1: AceComm:argCheck(customChannel, 4, "string") flickerstreak@1: channel = "AceComm" .. customChannel flickerstreak@1: else flickerstreak@1: AceComm:error('Argument #3 to `IsUserInChannel\' must be "GLOBAL", "CUSTOM", or "ZONE"') flickerstreak@1: end flickerstreak@1: flickerstreak@1: return AceComm.userRegistry[channel] and AceComm.userRegistry[channel][userName] or false flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_CHANNEL_LIST(text, _, _, _, _, _, _, _, channel) flickerstreak@1: if not string_find(channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not AceComm.userRegistry[channel] then flickerstreak@1: AceComm.userRegistry[channel] = {} flickerstreak@1: end flickerstreak@1: local t = AceComm.userRegistry[channel] flickerstreak@1: for k in string_gmatch(text, "[^, @%*#]+") do flickerstreak@1: t[k] = true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_CHANNEL_JOIN(_, user, _, _, _, _, _, _, channel) flickerstreak@1: if not string_find(channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not AceComm.userRegistry[channel] then flickerstreak@1: AceComm.userRegistry[channel] = {} flickerstreak@1: end flickerstreak@1: local t = AceComm.userRegistry[channel] flickerstreak@1: t[user] = true flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_CHANNEL_LEAVE(_, user, _, _, _, _, _, _, channel) flickerstreak@1: if not string_find(channel, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not AceComm.userRegistry[channel] then flickerstreak@1: AceComm.userRegistry[channel] = {} flickerstreak@1: end flickerstreak@1: local t = AceComm.userRegistry[channel] flickerstreak@1: if t[user] then flickerstreak@1: t[user] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:AceEvent_FullyInitialized() flickerstreak@1: RefixAceCommChannelsAndEvents() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:PLAYER_LOGOUT() flickerstreak@1: LeaveAceCommChannels(true) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:ZONE_CHANGED_NEW_AREA() flickerstreak@1: local lastZone = zoneCache flickerstreak@1: zoneCache = nil flickerstreak@1: local newZone = GetCurrentZoneChannel() flickerstreak@1: if self.registry.ZONE and next(self.registry.ZONE) then flickerstreak@1: if lastZone then flickerstreak@1: SwitchChannel(lastZone, newZone) flickerstreak@1: else flickerstreak@1: JoinChannel(newZone) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:embed(target) flickerstreak@1: self.super.embed(self, target) flickerstreak@1: if not AceEvent then flickerstreak@1: AceComm:error(MAJOR_VERSION .. " requires AceEvent-2.0") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local recentNotSeen = {} flickerstreak@1: local notSeenString = '^' .. string_gsub(string_gsub(ERR_CHAT_PLAYER_NOT_FOUND_S, "%%s", "(.-)"), "%%1%$s", "(.-)") .. '$' flickerstreak@1: local ambiguousString = '^' .. string_gsub(string_gsub(ERR_CHAT_PLAYER_AMBIGUOUS_S, "%%s", "(.-)"), "%%1%$s", "(.-)") .. '$' flickerstreak@1: function AceComm.hooks:ChatFrame_MessageEventHandler(orig, event) flickerstreak@1: if event == "CHAT_MSG_WHISPER" or event == "CHAT_MSG_WHISPER_INFORM" then flickerstreak@1: if string_find(arg1, "^/") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: elseif event == "CHAT_MSG_AFK" or event == "CHAT_MSG_DND" then flickerstreak@1: local t = self.recentWhispers[string.lower(arg2)] flickerstreak@1: if t and GetTime() - t <= 15 then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: elseif event == "CHAT_MSG_CHANNEL" or event == "CHAT_MSG_CHANNEL_LIST" then flickerstreak@1: if string_find(arg9, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: elseif event == "CHAT_MSG_SYSTEM" then flickerstreak@1: local _,_,player = string_find(arg1, notSeenString) flickerstreak@1: if not player then flickerstreak@1: _,_,player = string_find(arg1, ambiguousString) flickerstreak@1: end flickerstreak@1: if player then flickerstreak@1: local t = GetTime() flickerstreak@1: if recentNotSeen[player] and recentNotSeen[player] > t then flickerstreak@1: recentNotSeen[player] = t + 10 flickerstreak@1: return flickerstreak@1: else flickerstreak@1: recentNotSeen[player] = t + 10 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return orig(event) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local id, loggingOut flickerstreak@1: function AceComm.hooks:Logout(orig) flickerstreak@1: if IsResting() then flickerstreak@1: LeaveAceCommChannels(true) flickerstreak@1: else flickerstreak@1: id = self:ScheduleEvent(LeaveAceCommChannels, 15, true) flickerstreak@1: end flickerstreak@1: loggingOut = true flickerstreak@1: return orig() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm.hooks:CancelLogout(orig) flickerstreak@1: shutdown = false flickerstreak@1: if id then flickerstreak@1: self:CancelScheduledEvent(id) flickerstreak@1: id = nil flickerstreak@1: end flickerstreak@1: RefixAceCommChannelsAndEvents() flickerstreak@1: loggingOut = false flickerstreak@1: return orig() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm.hooks:Quit(orig) flickerstreak@1: if IsResting() then flickerstreak@1: LeaveAceCommChannels(true) flickerstreak@1: else flickerstreak@1: id = self:ScheduleEvent(LeaveAceCommChannels, 15, true) flickerstreak@1: end flickerstreak@1: loggingOut = true flickerstreak@1: return orig() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm.hooks:FCFDropDown_LoadChannels(orig, ...) flickerstreak@1: local arg = { ... } flickerstreak@1: for i = 1, #arg, 2 do flickerstreak@1: if not arg[i] then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: if type(arg[i + 1]) == "string" and string_find(arg[i + 1], "^AceComm") then flickerstreak@1: table.remove(arg, i + 1) flickerstreak@1: table.remove(arg, i) flickerstreak@1: i = i - 2 flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return orig(unpack(arg)) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function AceComm:CHAT_MSG_SYSTEM(text) flickerstreak@1: if text ~= ERR_TOO_MANY_CHAT_CHANNELS then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: local chan = lastChannelJoined flickerstreak@1: if not chan then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: if not string_find(lastChannelJoined, "^AceComm") then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: local text flickerstreak@1: if chan == "AceComm" then flickerstreak@1: local addon = self.registry.GLOBAL and next(AceComm_registry.GLOBAL) flickerstreak@1: if not addon then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: addon = tostring(addon) flickerstreak@1: text = string_format("%s has tried to join the AceComm global channel, but there are not enough channels available. %s may not work because of this", addon, addon) flickerstreak@1: elseif chan == GetCurrentZoneChannel() then flickerstreak@1: local addon = AceComm_registry.ZONE and next(AceComm_registry.ZONE) flickerstreak@1: if not addon then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: addon = tostring(addon) flickerstreak@1: text = string_format("%s has tried to join the AceComm zone channel, but there are not enough channels available. %s may not work because of this", addon, addon) flickerstreak@1: else flickerstreak@1: local addon = AceComm_registry.CUSTOM and AceComm_registry.CUSTOM[chan] and next(AceComm_registry.CUSTOM[chan]) flickerstreak@1: if not addon then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: addon = tostring(addon) flickerstreak@1: text = string_format("%s has tried to join the AceComm custom channel %s, but there are not enough channels available. %s may not work because of this", addon, chan, addon) flickerstreak@1: end flickerstreak@1: flickerstreak@1: StaticPopupDialogs["ACECOMM_TOO_MANY_CHANNELS"] = { flickerstreak@1: text = text, flickerstreak@1: button1 = CLOSE, flickerstreak@1: timeout = 0, flickerstreak@1: whileDead = 1, flickerstreak@1: hideOnEscape = 1, flickerstreak@1: } flickerstreak@1: StaticPopup_Show("ACECOMM_TOO_MANY_CHANNELS") flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function activate(self, oldLib, oldDeactivate) flickerstreak@1: AceComm = self flickerstreak@1: flickerstreak@1: if oldLib then flickerstreak@1: self.frame = oldLib.frame flickerstreak@1: self.frame:UnregisterAllEvents() flickerstreak@1: self.recvQueue = oldLib.recvQueue flickerstreak@1: self.registry = oldLib.registry flickerstreak@1: self.channels = oldLib.channels flickerstreak@1: self.prefixes = oldLib.prefixes flickerstreak@1: self.classes = oldLib.classes flickerstreak@1: self.prefixMemoizations = oldLib.prefixMemoizations flickerstreak@1: self.prefixHashToText = oldLib.prefixHashToText flickerstreak@1: self.prefixTextToHash = oldLib.prefixTextToHash flickerstreak@1: self.recentWhispers = oldLib.recentWhispers flickerstreak@1: self.userRegistry = oldLib.userRegistry flickerstreak@1: else flickerstreak@1: local old_ChatFrame_MessageEventHandler = ChatFrame_MessageEventHandler flickerstreak@1: function ChatFrame_MessageEventHandler(event) flickerstreak@1: if self.hooks.ChatFrame_MessageEventHandler then flickerstreak@1: return self.hooks.ChatFrame_MessageEventHandler(self, old_ChatFrame_MessageEventHandler, event) flickerstreak@1: else flickerstreak@1: return old_ChatFrame_MessageEventHandler(event) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local id flickerstreak@1: local loggingOut = false flickerstreak@1: local old_Logout = Logout flickerstreak@1: function Logout() flickerstreak@1: if self.hooks.Logout then flickerstreak@1: return self.hooks.Logout(self, old_Logout) flickerstreak@1: else flickerstreak@1: return old_Logout() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local old_CancelLogout = CancelLogout flickerstreak@1: function CancelLogout() flickerstreak@1: if self.hooks.CancelLogout then flickerstreak@1: return self.hooks.CancelLogout(self, old_CancelLogout) flickerstreak@1: else flickerstreak@1: return old_CancelLogout() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local old_Quit = Quit flickerstreak@1: function Quit() flickerstreak@1: if self.hooks.Quit then flickerstreak@1: return self.hooks.Quit(self, old_Quit) flickerstreak@1: else flickerstreak@1: return old_Quit() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local old_FCFDropDown_LoadChannels = FCFDropDown_LoadChannels flickerstreak@1: function FCFDropDown_LoadChannels(...) flickerstreak@1: if self.hooks.FCFDropDown_LoadChannels then flickerstreak@1: return self.hooks.FCFDropDown_LoadChannels(self, old_FCFDropDown_LoadChannels, ...) flickerstreak@1: else flickerstreak@1: return old_FCFDropDown_LoadChannels(...) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local old_JoinChannelByName = JoinChannelByName flickerstreak@1: function JoinChannelByName(a,b,c,d,e,f,g,h,i,j) flickerstreak@1: if self.hooks.JoinChannelByName then flickerstreak@1: return self.hooks.JoinChannelByName(self, old_JoinChannelByName, a,b,c,d,e,f,g,h,i,j) flickerstreak@1: else flickerstreak@1: return old_JoinChannelByName(a,b,c,d,e,f,g,h,i,j) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if not self.recvQueue then flickerstreak@1: self.recvQueue = {} flickerstreak@1: end flickerstreak@1: if not self.registry then flickerstreak@1: self.registry = {} flickerstreak@1: end flickerstreak@1: AceComm_registry = self.registry flickerstreak@1: if not self.prefixes then flickerstreak@1: self.prefixes = {} flickerstreak@1: end flickerstreak@1: if not self.classes then flickerstreak@1: self.classes = {} flickerstreak@1: else flickerstreak@1: for k in pairs(self.classes) do flickerstreak@1: self.classes[k] = nil flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if not self.prefixMemoizations then flickerstreak@1: self.prefixMemoizations = {} flickerstreak@1: end flickerstreak@1: if not self.prefixHashToText then flickerstreak@1: self.prefixHashToText = {} flickerstreak@1: end flickerstreak@1: if not self.prefixTextToHash then flickerstreak@1: self.prefixTextToHash = {} flickerstreak@1: end flickerstreak@1: if not self.recentWhispers then flickerstreak@1: self.recentWhispers = {} flickerstreak@1: end flickerstreak@1: if not self.userRegistry then flickerstreak@1: self.userRegistry = {} flickerstreak@1: end flickerstreak@1: flickerstreak@1: SetCVar("spamFilter", 0) flickerstreak@1: flickerstreak@1: self:activate(oldLib, oldDeactivate) flickerstreak@1: flickerstreak@1: if oldDeactivate then flickerstreak@1: oldDeactivate(oldLib) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function external(self, major, instance) flickerstreak@1: if major == "AceEvent-2.0" then flickerstreak@1: AceEvent = instance flickerstreak@1: flickerstreak@1: AceEvent:embed(AceComm) flickerstreak@1: flickerstreak@1: self:UnregisterAllEvents() flickerstreak@1: self:CancelAllScheduledEvents() flickerstreak@1: flickerstreak@1: if AceEvent:IsFullyInitialized() then flickerstreak@1: self:AceEvent_FullyInitialized() flickerstreak@1: else flickerstreak@1: self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true) flickerstreak@1: end flickerstreak@1: flickerstreak@1: self:RegisterEvent("PLAYER_LOGOUT") flickerstreak@1: self:RegisterEvent("ZONE_CHANGED_NEW_AREA") flickerstreak@1: self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE") flickerstreak@1: self:RegisterEvent("CHAT_MSG_SYSTEM") flickerstreak@1: else flickerstreak@1: if AceOO.inherits(instance, AceOO.Class) and not instance.class then flickerstreak@1: self.classes[TailoredNumericCheckSum(major)] = instance flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: AceLibrary:Register(AceComm, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- flickerstreak@1: -- ChatThrottleLib by Mikk flickerstreak@1: -- flickerstreak@1: -- Manages AddOn chat output to keep player from getting kicked off. flickerstreak@1: -- flickerstreak@1: -- ChatThrottleLib.SendChatMessage/.SendAddonMessage functions that accept flickerstreak@1: -- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage. flickerstreak@1: -- flickerstreak@1: -- Priorities get an equal share of available bandwidth when fully loaded. flickerstreak@1: -- Communication channels are separated on extension+chattype+destination and flickerstreak@1: -- get round-robinned. (Destination only matters for whispers and channels, flickerstreak@1: -- obviously) flickerstreak@1: -- flickerstreak@1: -- Will install hooks for SendChatMessage and SendAdd[Oo]nMessage to measure flickerstreak@1: -- bandwidth bypassing the library and use less bandwidth itself. flickerstreak@1: -- flickerstreak@1: -- flickerstreak@1: -- Fully embeddable library. Just copy this file into your addon directory, flickerstreak@1: -- add it to the .toc, and it's done. flickerstreak@1: -- flickerstreak@1: -- Can run as a standalone addon also, but, really, just embed it! :-) flickerstreak@1: -- flickerstreak@1: flickerstreak@1: local CTL_VERSION = 13 flickerstreak@1: flickerstreak@1: local MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800. flickerstreak@1: local MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff flickerstreak@1: flickerstreak@1: local BURST = 4000 -- WoW's server buffer seems to be about 32KB. 8KB should be safe, but seen disconnects on _some_ servers. Using 4KB now. flickerstreak@1: flickerstreak@1: local MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value flickerstreak@1: flickerstreak@1: if(ChatThrottleLib and ChatThrottleLib.version>=CTL_VERSION) then flickerstreak@1: -- There's already a newer (or same) version loaded. Buh-bye. flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: if(not ChatThrottleLib) then flickerstreak@1: ChatThrottleLib = {} flickerstreak@1: end flickerstreak@1: flickerstreak@1: local ChatThrottleLib = ChatThrottleLib flickerstreak@1: local strlen = strlen flickerstreak@1: local setmetatable = setmetatable flickerstreak@1: local getn = getn flickerstreak@1: local tremove = tremove flickerstreak@1: local tinsert = tinsert flickerstreak@1: local tostring = tostring flickerstreak@1: local GetTime = GetTime flickerstreak@1: local format = format flickerstreak@1: flickerstreak@1: ChatThrottleLib.version=CTL_VERSION; flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- Double-linked ring implementation flickerstreak@1: flickerstreak@1: local Ring = {} flickerstreak@1: local RingMeta = { __index=Ring } flickerstreak@1: flickerstreak@1: function Ring:New() flickerstreak@1: local ret = {} flickerstreak@1: setmetatable(ret, RingMeta) flickerstreak@1: return ret; flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position) flickerstreak@1: if(self.pos) then flickerstreak@1: obj.prev = self.pos.prev; flickerstreak@1: obj.prev.next = obj; flickerstreak@1: obj.next = self.pos; flickerstreak@1: obj.next.prev = obj; flickerstreak@1: else flickerstreak@1: obj.next = obj; flickerstreak@1: obj.prev = obj; flickerstreak@1: self.pos = obj; flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Ring:Remove(obj) flickerstreak@1: obj.next.prev = obj.prev; flickerstreak@1: obj.prev.next = obj.next; flickerstreak@1: if(self.pos == obj) then flickerstreak@1: self.pos = obj.next; flickerstreak@1: if(self.pos == obj) then flickerstreak@1: self.pos = nil; flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- Recycling bin for pipes (kept in a linked list because that's flickerstreak@1: -- how they're worked with in the rotating rings; just reusing members) flickerstreak@1: flickerstreak@1: ChatThrottleLib.PipeBin = { count=0 } flickerstreak@1: flickerstreak@1: function ChatThrottleLib.PipeBin:Put(pipe) flickerstreak@1: for i=getn(pipe),1,-1 do flickerstreak@1: tremove(pipe, i); flickerstreak@1: end flickerstreak@1: pipe.prev = nil; flickerstreak@1: pipe.next = self.list; flickerstreak@1: self.list = pipe; flickerstreak@1: self.count = self.count+1; flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ChatThrottleLib.PipeBin:Get() flickerstreak@1: if(self.list) then flickerstreak@1: local ret = self.list; flickerstreak@1: self.list = ret.next; flickerstreak@1: ret.next=nil; flickerstreak@1: self.count = self.count - 1; flickerstreak@1: return ret; flickerstreak@1: end flickerstreak@1: return {}; flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ChatThrottleLib.PipeBin:Tidy() flickerstreak@1: if(self.count < 25) then flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: flickerstreak@1: if(self.count > 100) then flickerstreak@1: n=self.count-90; flickerstreak@1: else flickerstreak@1: n=10; flickerstreak@1: end flickerstreak@1: for i=2,n do flickerstreak@1: self.list = self.list.next; flickerstreak@1: end flickerstreak@1: local delme = self.list; flickerstreak@1: self.list = self.list.next; flickerstreak@1: delme.next = nil; flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- Recycling bin for messages flickerstreak@1: flickerstreak@1: ChatThrottleLib.MsgBin = {} flickerstreak@1: flickerstreak@1: function ChatThrottleLib.MsgBin:Put(msg) flickerstreak@1: msg.text = nil; flickerstreak@1: tinsert(self, msg); flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ChatThrottleLib.MsgBin:Get() flickerstreak@1: local ret = tremove(self, getn(self)); flickerstreak@1: if(ret) then return ret; end flickerstreak@1: return {}; flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ChatThrottleLib.MsgBin:Tidy() flickerstreak@1: if(getn(self)<50) then flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: if(getn(self)>150) then -- "can't happen" but ... flickerstreak@1: for n=getn(self),120,-1 do flickerstreak@1: tremove(self,n); flickerstreak@1: end flickerstreak@1: else flickerstreak@1: for n=getn(self),getn(self)-20,-1 do flickerstreak@1: tremove(self,n); flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- ChatThrottleLib:Init flickerstreak@1: -- Initialize queues, set up frame for OnUpdate, etc flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib:Init() flickerstreak@1: flickerstreak@1: -- Set up queues flickerstreak@1: if(not self.Prio) then flickerstreak@1: self.Prio = {} flickerstreak@1: self.Prio["ALERT"] = { ByName={}, Ring = Ring:New(), avail=0 }; flickerstreak@1: self.Prio["NORMAL"] = { ByName={}, Ring = Ring:New(), avail=0 }; flickerstreak@1: self.Prio["BULK"] = { ByName={}, Ring = Ring:New(), avail=0 }; flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- v4: total send counters per priority flickerstreak@1: for _,Prio in pairs(self.Prio) do flickerstreak@1: Prio.nTotalSent = Prio.nTotalSent or 0; flickerstreak@1: end flickerstreak@1: flickerstreak@1: self.avail = self.avail or 0; -- v5 flickerstreak@1: self.nTotalSent = self.nTotalSent or 0; -- v5 flickerstreak@1: flickerstreak@1: flickerstreak@1: -- Set up a frame to get OnUpdate events flickerstreak@1: if(not self.Frame) then flickerstreak@1: self.Frame = CreateFrame("Frame"); flickerstreak@1: self.Frame:Hide(); flickerstreak@1: end flickerstreak@1: self.Frame.Show = self.Frame.Show; -- cache for speed flickerstreak@1: self.Frame.Hide = self.Frame.Hide; -- cache for speed flickerstreak@1: self.Frame:SetScript("OnUpdate", self.OnUpdate); flickerstreak@1: self.Frame:SetScript("OnEvent", self.OnEvent); -- v11: Monitor P_E_W so we can throttle hard for a few seconds flickerstreak@1: self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD"); flickerstreak@1: self.OnUpdateDelay=0; flickerstreak@1: self.LastAvailUpdate=GetTime(); flickerstreak@1: self.HardThrottlingBeginTime=GetTime(); -- v11: Throttle hard for a few seconds after startup flickerstreak@1: flickerstreak@1: -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7) flickerstreak@1: if(not self.ORIG_SendChatMessage) then flickerstreak@1: --SendChatMessage flickerstreak@1: self.ORIG_SendChatMessage = SendChatMessage; flickerstreak@1: SendChatMessage = function(a1,a2,a3,a4) return ChatThrottleLib.Hook_SendChatMessage(a1,a2,a3,a4); end flickerstreak@1: --SendAdd[Oo]nMessage flickerstreak@1: if(SendAddonMessage or SendAddOnMessage) then -- v10: don't pretend like it doesn't exist if it doesn't! flickerstreak@1: self.ORIG_SendAddonMessage = SendAddonMessage or SendAddOnMessage; flickerstreak@1: SendAddonMessage = function(a1,a2,a3) return ChatThrottleLib.Hook_SendAddonMessage(a1,a2,a3); end flickerstreak@1: if(SendAddOnMessage) then -- in case Slouken changes his mind... flickerstreak@1: SendAddOnMessage = SendAddonMessage; flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: self.nBypass = 0; flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage flickerstreak@1: function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination) flickerstreak@1: local self = ChatThrottleLib; flickerstreak@1: local size = strlen(tostring(text or "")) + strlen(tostring(chattype or "")) + strlen(tostring(destination or "")) + 40; flickerstreak@1: self.avail = self.avail - size; flickerstreak@1: self.nBypass = self.nBypass + size; flickerstreak@1: return self.ORIG_SendChatMessage(text, chattype, language, destination); flickerstreak@1: end flickerstreak@1: function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype) flickerstreak@1: local self = ChatThrottleLib; flickerstreak@1: local size = strlen(tostring(text or "")) + strlen(tostring(chattype or "")) + strlen(tostring(prefix or "")) + 40; flickerstreak@1: self.avail = self.avail - size; flickerstreak@1: self.nBypass = self.nBypass + size; flickerstreak@1: return self.ORIG_SendAddonMessage(prefix, text, chattype); flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- ChatThrottleLib:UpdateAvail flickerstreak@1: -- Update self.avail with how much bandwidth is currently available flickerstreak@1: flickerstreak@1: function ChatThrottleLib:UpdateAvail() flickerstreak@1: local now = GetTime(); flickerstreak@1: local newavail = MAX_CPS * (now-self.LastAvailUpdate); flickerstreak@1: flickerstreak@1: if(now - self.HardThrottlingBeginTime < 5) then flickerstreak@1: -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then flickerstreak@1: self.avail = min(self.avail + (newavail*0.1), MAX_CPS*0.5); flickerstreak@1: elseif(GetFramerate()ring.pos[1].nSize) do flickerstreak@1: local msg = tremove(Prio.Ring.pos, 1); flickerstreak@1: if(not Prio.Ring.pos[1]) then flickerstreak@1: local pipe = Prio.Ring.pos; flickerstreak@1: Prio.Ring:Remove(pipe); flickerstreak@1: Prio.ByName[pipe.name] = nil; flickerstreak@1: self.PipeBin:Put(pipe); flickerstreak@1: else flickerstreak@1: Prio.Ring.pos = Prio.Ring.pos.next; flickerstreak@1: end flickerstreak@1: Prio.avail = Prio.avail - msg.nSize; flickerstreak@1: msg.f(msg[1], msg[2], msg[3], msg[4]); flickerstreak@1: Prio.nTotalSent = Prio.nTotalSent + msg.nSize; flickerstreak@1: self.MsgBin:Put(msg); flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib.OnEvent() flickerstreak@1: -- v11: We know that the rate limiter is touchy after login. Assume that it's touch after zoning, too. flickerstreak@1: self = ChatThrottleLib; flickerstreak@1: if(event == "PLAYER_ENTERING_WORLD") then flickerstreak@1: self.HardThrottlingBeginTime=GetTime(); -- Throttle hard for a few seconds after zoning flickerstreak@1: self.avail = 0; flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib.OnUpdate() flickerstreak@1: self = ChatThrottleLib; flickerstreak@1: flickerstreak@1: self.OnUpdateDelay = self.OnUpdateDelay + arg1; flickerstreak@1: if(self.OnUpdateDelay < 0.08) then flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: self.OnUpdateDelay = 0; flickerstreak@1: flickerstreak@1: self:UpdateAvail(); flickerstreak@1: flickerstreak@1: if(self.avail<0) then flickerstreak@1: return; -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu. flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- See how many of or priorities have queued messages flickerstreak@1: local n=0; flickerstreak@1: for prioname,Prio in pairs(self.Prio) do flickerstreak@1: if(Prio.Ring.pos or Prio.avail<0) then flickerstreak@1: n=n+1; flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- Anything queued still? flickerstreak@1: if(n<1) then flickerstreak@1: -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing flickerstreak@1: for prioname,Prio in pairs(self.Prio) do flickerstreak@1: self.avail = self.avail + Prio.avail; flickerstreak@1: Prio.avail = 0; flickerstreak@1: end flickerstreak@1: self.bQueueing = false; flickerstreak@1: self.Frame:Hide(); flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues flickerstreak@1: local avail= self.avail/n; flickerstreak@1: self.avail = 0; flickerstreak@1: flickerstreak@1: for prioname,Prio in pairs(self.Prio) do flickerstreak@1: if(Prio.Ring.pos or Prio.avail<0) then flickerstreak@1: Prio.avail = Prio.avail + avail; flickerstreak@1: if(Prio.Ring.pos and Prio.avail>Prio.Ring.pos[1].nSize) then flickerstreak@1: self:Despool(Prio); flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- Expire recycled tables if needed flickerstreak@1: self.MsgBin:Tidy(); flickerstreak@1: self.PipeBin:Tidy(); flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- Spooling logic flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib:Enqueue(prioname, pipename, msg) flickerstreak@1: local Prio = self.Prio[prioname]; flickerstreak@1: local pipe = Prio.ByName[pipename]; flickerstreak@1: if(not pipe) then flickerstreak@1: self.Frame:Show(); flickerstreak@1: pipe = self.PipeBin:Get(); flickerstreak@1: pipe.name = pipename; flickerstreak@1: Prio.ByName[pipename] = pipe; flickerstreak@1: Prio.Ring:Add(pipe); flickerstreak@1: end flickerstreak@1: flickerstreak@1: tinsert(pipe, msg); flickerstreak@1: flickerstreak@1: self.bQueueing = true; flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination) flickerstreak@1: if(not (self and prio and text and self.Prio[prio] ) ) then flickerstreak@1: error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix" or nil, "text"[, "chattype"[, "language"[, "destination"]]]', 2); flickerstreak@1: end flickerstreak@1: flickerstreak@1: prefix = prefix or tostring(this); -- each frame gets its own queue if prefix is not given flickerstreak@1: flickerstreak@1: local nSize = strlen(text) + MSG_OVERHEAD; flickerstreak@1: flickerstreak@1: -- Check if there's room in the global available bandwidth gauge to send directly flickerstreak@1: if(not self.bQueueing and nSize < self:UpdateAvail()) then flickerstreak@1: self.avail = self.avail - nSize; flickerstreak@1: self.ORIG_SendChatMessage(text, chattype, language, destination); flickerstreak@1: self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize; flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- Message needs to be queued flickerstreak@1: msg=self.MsgBin:Get(); flickerstreak@1: msg.f=self.ORIG_SendChatMessage flickerstreak@1: msg[1]=text; flickerstreak@1: msg[2]=chattype or "SAY"; flickerstreak@1: msg[3]=language; flickerstreak@1: msg[4]=destination; flickerstreak@1: msg.n = 4 flickerstreak@1: msg.nSize = nSize; flickerstreak@1: flickerstreak@1: self:Enqueue(prio, format("%s/%s/%s", prefix, chattype, destination or ""), msg); flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype) flickerstreak@1: if(not (self and prio and prefix and text and chattype and self.Prio[prio] ) ) then flickerstreak@1: error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype")', 0); flickerstreak@1: end flickerstreak@1: flickerstreak@1: local nSize = strlen(prefix) + 1 + strlen(text) + MSG_OVERHEAD; flickerstreak@1: flickerstreak@1: -- Check if there's room in the global available bandwidth gauge to send directly flickerstreak@1: if(not self.bQueueing and nSize < self:UpdateAvail()) then flickerstreak@1: self.avail = self.avail - nSize; flickerstreak@1: self.ORIG_SendAddonMessage(prefix, text, chattype); flickerstreak@1: self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize; flickerstreak@1: return; flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- Message needs to be queued flickerstreak@1: msg=self.MsgBin:Get(); flickerstreak@1: msg.f=self.ORIG_SendAddonMessage; flickerstreak@1: msg[1]=prefix; flickerstreak@1: msg[2]=text; flickerstreak@1: msg[3]=chattype; flickerstreak@1: msg.n = 3 flickerstreak@1: msg.nSize = nSize; flickerstreak@1: flickerstreak@1: self:Enqueue(prio, format("%s/%s", prefix, chattype), msg); flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: ----------------------------------------------------------------------- flickerstreak@1: -- Get the ball rolling! flickerstreak@1: flickerstreak@1: ChatThrottleLib:Init(); flickerstreak@1: flickerstreak@1: --[[ WoWBench debugging snippet flickerstreak@1: if(WOWB_VER) then flickerstreak@1: local function SayTimer() flickerstreak@1: print("SAY: "..GetTime().." "..arg1); flickerstreak@1: end flickerstreak@1: ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer); flickerstreak@1: ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY"); flickerstreak@1: end flickerstreak@1: ]]