annotate libs/AceComm-2.0/AceComm-2.0.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children
rev   line source
flickerstreak@1 1 --[[
flickerstreak@1 2 Name: AceComm-2.0
flickerstreak@1 3 Revision: $Rev: 18708 $
flickerstreak@1 4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
flickerstreak@1 5 Inspired By: Ace 1.x by Turan (turan@gryphon.com)
flickerstreak@1 6 Website: http://www.wowace.com/
flickerstreak@1 7 Documentation: http://www.wowace.com/index.php/AceComm-2.0
flickerstreak@1 8 SVN: http://svn.wowace.com/wowace/trunk/Ace2/AceComm-2.0
flickerstreak@1 9 Description: Mixin to allow for inter-player addon communications.
flickerstreak@1 10 Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0,
flickerstreak@1 11 ChatThrottleLib by Mikk (included)
flickerstreak@1 12 ]]
flickerstreak@1 13
flickerstreak@1 14 local MAJOR_VERSION = "AceComm-2.0"
flickerstreak@1 15 local MINOR_VERSION = "$Revision: 18708 $"
flickerstreak@1 16
flickerstreak@1 17 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
flickerstreak@1 18 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
flickerstreak@1 19
flickerstreak@1 20 if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
flickerstreak@1 21
flickerstreak@1 22 local _G = getfenv(0)
flickerstreak@1 23
flickerstreak@1 24 local AceOO = AceLibrary("AceOO-2.0")
flickerstreak@1 25 local Mixin = AceOO.Mixin
flickerstreak@1 26 local AceComm = Mixin {
flickerstreak@1 27 "SendCommMessage",
flickerstreak@1 28 "SendPrioritizedCommMessage",
flickerstreak@1 29 "RegisterComm",
flickerstreak@1 30 "UnregisterComm",
flickerstreak@1 31 "UnregisterAllComms",
flickerstreak@1 32 "IsCommRegistered",
flickerstreak@1 33 "SetDefaultCommPriority",
flickerstreak@1 34 "SetCommPrefix",
flickerstreak@1 35 "RegisterMemoizations",
flickerstreak@1 36 "IsUserInChannel",
flickerstreak@1 37 }
flickerstreak@1 38 AceComm.hooks = {}
flickerstreak@1 39
flickerstreak@1 40 local AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")
flickerstreak@1 41
flickerstreak@1 42 local string_byte = string.byte
flickerstreak@1 43
flickerstreak@1 44 local byte_a = string_byte('a')
flickerstreak@1 45 local byte_z = string_byte('z')
flickerstreak@1 46 local byte_A = string_byte('A')
flickerstreak@1 47 local byte_Z = string_byte('Z')
flickerstreak@1 48 local byte_fake_s = string_byte('\015')
flickerstreak@1 49 local byte_fake_S = string_byte('\020')
flickerstreak@1 50 local byte_deg = string_byte('°')
flickerstreak@1 51 local byte_percent = string_byte('%') -- 37
flickerstreak@1 52
flickerstreak@1 53 local byte_b = string_byte('b')
flickerstreak@1 54 local byte_B = string_byte('B')
flickerstreak@1 55 local byte_nil = string_byte('/')
flickerstreak@1 56 local byte_plus = string_byte('+')
flickerstreak@1 57 local byte_minus = string_byte('-')
flickerstreak@1 58 local byte_d = string_byte('d')
flickerstreak@1 59 local byte_D = string_byte('D')
flickerstreak@1 60 local byte_e = string_byte('e')
flickerstreak@1 61 local byte_E = string_byte('E')
flickerstreak@1 62 local byte_m = string_byte('m')
flickerstreak@1 63 local byte_s = string_byte('s')
flickerstreak@1 64 local byte_S = string_byte('S')
flickerstreak@1 65 local byte_o = string_byte('o')
flickerstreak@1 66 local byte_O = string_byte('O')
flickerstreak@1 67 local byte_t = string_byte('t')
flickerstreak@1 68 local byte_T = string_byte('T')
flickerstreak@1 69 local byte_u = string_byte('u')
flickerstreak@1 70 local byte_U = string_byte('U')
flickerstreak@1 71 local byte_i = string_byte('i')
flickerstreak@1 72 local byte_inf = string_byte('@')
flickerstreak@1 73 local byte_ninf = string_byte('$')
flickerstreak@1 74 local byte_nan = string_byte('!')
flickerstreak@1 75
flickerstreak@1 76 local inf = 1/0
flickerstreak@1 77 local nan = 0/0
flickerstreak@1 78
flickerstreak@1 79 local math_floor = math.floor
flickerstreak@1 80 local math_mod = math.fmod
flickerstreak@1 81 local math_floormod = function(value, m)
flickerstreak@1 82 return math_mod(math_floor(value), m)
flickerstreak@1 83 end
flickerstreak@1 84 local string_gmatch = string.gmatch
flickerstreak@1 85 local string_char = string.char
flickerstreak@1 86 local string_len = string.len
flickerstreak@1 87 local string_format = string.format
flickerstreak@1 88 local string_gsub = string.gsub
flickerstreak@1 89 local string_find = string.find
flickerstreak@1 90 local table_insert = table.insert
flickerstreak@1 91 local string_sub = string.sub
flickerstreak@1 92 local table_concat = table.concat
flickerstreak@1 93 local table_remove = table.remove
flickerstreak@1 94
flickerstreak@1 95 local type = type
flickerstreak@1 96 local unpack = unpack
flickerstreak@1 97 local pairs = pairs
flickerstreak@1 98 local next = next
flickerstreak@1 99
flickerstreak@1 100 local player = UnitName("player")
flickerstreak@1 101
flickerstreak@1 102 local NumericCheckSum, HexCheckSum, BinaryCheckSum
flickerstreak@1 103 local TailoredNumericCheckSum, TailoredHexCheckSum, TailoredBinaryCheckSum
flickerstreak@1 104 do
flickerstreak@1 105 local SOME_PRIME = 16777213
flickerstreak@1 106 function NumericCheckSum(text)
flickerstreak@1 107 local counter = 1
flickerstreak@1 108 local len = string_len(text)
flickerstreak@1 109 for i = 1, len, 3 do
flickerstreak@1 110 counter = math_mod(counter*8257, 16777259) +
flickerstreak@1 111 (string_byte(text,i)) +
flickerstreak@1 112 ((string_byte(text,i+1) or 1)*127) +
flickerstreak@1 113 ((string_byte(text,i+2) or 2)*16383)
flickerstreak@1 114 end
flickerstreak@1 115 return math_mod(counter, 16777213)
flickerstreak@1 116 end
flickerstreak@1 117
flickerstreak@1 118 function HexCheckSum(text)
flickerstreak@1 119 return string_format("%06x", NumericCheckSum(text))
flickerstreak@1 120 end
flickerstreak@1 121
flickerstreak@1 122 function BinaryCheckSum(text)
flickerstreak@1 123 local num = NumericCheckSum(text)
flickerstreak@1 124 return string_char(math_floor(num / 65536), math_floormod(num / 256, 256), math_mod(num, 256))
flickerstreak@1 125 end
flickerstreak@1 126
flickerstreak@1 127 function TailoredNumericCheckSum(text)
flickerstreak@1 128 local hash = NumericCheckSum(text)
flickerstreak@1 129 local a = math_floor(hash / 65536)
flickerstreak@1 130 local b = math_floormod(hash / 256, 256)
flickerstreak@1 131 local c = math_mod(hash, 256)
flickerstreak@1 132 -- \000, \n, |, °, s, S, \015, \020
flickerstreak@1 133 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 134 a = a + 1
flickerstreak@1 135 -- \t, \255
flickerstreak@1 136 elseif a == 9 or a == 255 then
flickerstreak@1 137 a = a - 1
flickerstreak@1 138 end
flickerstreak@1 139 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 140 b = b + 1
flickerstreak@1 141 elseif b == 9 or b == 255 then
flickerstreak@1 142 b = b - 1
flickerstreak@1 143 end
flickerstreak@1 144 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 145 c = c + 1
flickerstreak@1 146 elseif c == 9 or c == 255 then
flickerstreak@1 147 c = c - 1
flickerstreak@1 148 end
flickerstreak@1 149 return a * 65536 + b * 256 + c
flickerstreak@1 150 end
flickerstreak@1 151
flickerstreak@1 152 function TailoredHexCheckSum(text)
flickerstreak@1 153 return string_format("%06x", TailoredNumericCheckSum(text))
flickerstreak@1 154 end
flickerstreak@1 155
flickerstreak@1 156 function TailoredBinaryCheckSum(text)
flickerstreak@1 157 local num = TailoredNumericCheckSum(text)
flickerstreak@1 158 return string_char(math_floor(num / 65536), math_floormod(num / 256, 256), math_mod(num, 256))
flickerstreak@1 159 end
flickerstreak@1 160 end
flickerstreak@1 161
flickerstreak@1 162 local function GetLatency()
flickerstreak@1 163 local _,_,lag = GetNetStats()
flickerstreak@1 164 return lag / 1000
flickerstreak@1 165 end
flickerstreak@1 166
flickerstreak@1 167 local function IsInChannel(chan)
flickerstreak@1 168 return GetChannelName(chan) ~= 0
flickerstreak@1 169 end
flickerstreak@1 170
flickerstreak@1 171 -- Package a message for transmission
flickerstreak@1 172 local function Encode(text, drunk)
flickerstreak@1 173 text = string_gsub(text, "°", "°±")
flickerstreak@1 174 if drunk then
flickerstreak@1 175 text = string_gsub(text, "\020", "°\021")
flickerstreak@1 176 text = string_gsub(text, "\015", "°\016")
flickerstreak@1 177 text = string_gsub(text, "S", "\020")
flickerstreak@1 178 text = string_gsub(text, "s", "\015")
flickerstreak@1 179 -- change S and s to a different set of character bytes.
flickerstreak@1 180 end
flickerstreak@1 181 text = string_gsub(text, "\255", "°\254") -- \255 (this is here because \000 is more common)
flickerstreak@1 182 text = string_gsub(text, "%z", "\255") -- \000
flickerstreak@1 183 text = string_gsub(text, "\010", "°\011") -- \n
flickerstreak@1 184 text = string_gsub(text, "\124", "°\125") -- |
flickerstreak@1 185 text = string_gsub(text, "%%", "°\038") -- %
flickerstreak@1 186 -- encode assorted prohibited characters
flickerstreak@1 187 return text
flickerstreak@1 188 end
flickerstreak@1 189
flickerstreak@1 190 local func
flickerstreak@1 191 -- Clean a received message
flickerstreak@1 192 local function Decode(text, drunk)
flickerstreak@1 193 if drunk then
flickerstreak@1 194 text = string_gsub(text, "^(.*)°.-$", "%1")
flickerstreak@1 195 -- get rid of " ...hic!"
flickerstreak@1 196 end
flickerstreak@1 197 if not func then
flickerstreak@1 198 func = function(text)
flickerstreak@1 199 if text == "\016" then
flickerstreak@1 200 return "\015"
flickerstreak@1 201 elseif text == "\021" then
flickerstreak@1 202 return "\020"
flickerstreak@1 203 elseif text == "±" then
flickerstreak@1 204 return "°"
flickerstreak@1 205 elseif text == "\254" then
flickerstreak@1 206 return "\255"
flickerstreak@1 207 elseif text == "\011" then
flickerstreak@1 208 return "\010"
flickerstreak@1 209 elseif text == "\125" then
flickerstreak@1 210 return "\124"
flickerstreak@1 211 elseif text == "\038" then
flickerstreak@1 212 return "\037"
flickerstreak@1 213 end
flickerstreak@1 214 end
flickerstreak@1 215 end
flickerstreak@1 216 text = string_gsub(text, "\255", "\000")
flickerstreak@1 217 if drunk then
flickerstreak@1 218 text = string_gsub(text, "\020", "S")
flickerstreak@1 219 text = string_gsub(text, "\015", "s")
flickerstreak@1 220 end
flickerstreak@1 221 text = string_gsub(text, drunk and "°([\016\021±\254\011\125\038])" or "°([±\254\011\125\038])", func)
flickerstreak@1 222 -- remove the hidden character and refix the prohibited characters.
flickerstreak@1 223 return text
flickerstreak@1 224 end
flickerstreak@1 225
flickerstreak@1 226 local lastChannelJoined
flickerstreak@1 227
flickerstreak@1 228 function AceComm.hooks:JoinChannelByName(orig, channel, a,b,c,d,e,f,g,h,i)
flickerstreak@1 229 lastChannelJoined = channel
flickerstreak@1 230 return orig(channel, a,b,c,d,e,f,g,h,i)
flickerstreak@1 231 end
flickerstreak@1 232
flickerstreak@1 233 local function JoinChannel(channel)
flickerstreak@1 234 if not IsInChannel(channel) then
flickerstreak@1 235 LeaveChannelByName(channel)
flickerstreak@1 236 AceComm:ScheduleEvent(JoinChannelByName, 0, channel)
flickerstreak@1 237 end
flickerstreak@1 238 end
flickerstreak@1 239
flickerstreak@1 240 local function LeaveChannel(channel)
flickerstreak@1 241 if IsInChannel(channel) then
flickerstreak@1 242 LeaveChannelByName(channel)
flickerstreak@1 243 end
flickerstreak@1 244 end
flickerstreak@1 245
flickerstreak@1 246 local switches = {}
flickerstreak@1 247
flickerstreak@1 248 local function SwitchChannel(former, latter)
flickerstreak@1 249 if IsInChannel(former) then
flickerstreak@1 250 LeaveChannelByName(former)
flickerstreak@1 251 switches[{
flickerstreak@1 252 former = former,
flickerstreak@1 253 latter = latter
flickerstreak@1 254 }] = true
flickerstreak@1 255 return
flickerstreak@1 256 end
flickerstreak@1 257 if not IsInChannel(latter) then
flickerstreak@1 258 JoinChannelByName(latter)
flickerstreak@1 259 end
flickerstreak@1 260 end
flickerstreak@1 261
flickerstreak@1 262 local shutdown = false
flickerstreak@1 263
flickerstreak@1 264 local zoneCache
flickerstreak@1 265 local function GetCurrentZoneChannel()
flickerstreak@1 266 if not zoneCache then
flickerstreak@1 267 zoneCache = "AceCommZone" .. HexCheckSum(GetRealZoneText())
flickerstreak@1 268 end
flickerstreak@1 269 return zoneCache
flickerstreak@1 270 end
flickerstreak@1 271
flickerstreak@1 272 local AceComm_registry
flickerstreak@1 273
flickerstreak@1 274 local function SupposedToBeInChannel(chan)
flickerstreak@1 275 if not string_find(chan, "^AceComm") then
flickerstreak@1 276 return true
flickerstreak@1 277 elseif shutdown or not AceEvent:IsFullyInitialized() then
flickerstreak@1 278 return false
flickerstreak@1 279 end
flickerstreak@1 280
flickerstreak@1 281 if chan == "AceComm" then
flickerstreak@1 282 return AceComm_registry.GLOBAL and next(AceComm_registry.GLOBAL) and true or false
flickerstreak@1 283 elseif string_find(chan, "^AceCommZone%x%x%x%x%x%x$") then
flickerstreak@1 284 if chan == GetCurrentZoneChannel() then
flickerstreak@1 285 return AceComm_registry.ZONE and next(AceComm_registry.ZONE) and true or false
flickerstreak@1 286 else
flickerstreak@1 287 return false
flickerstreak@1 288 end
flickerstreak@1 289 else
flickerstreak@1 290 return AceComm_registry.CUSTOM and AceComm_registry.CUSTOM[chan] and next(AceComm_registry.CUSTOM[chan]) and true or false
flickerstreak@1 291 end
flickerstreak@1 292 end
flickerstreak@1 293
flickerstreak@1 294 local tmp = {}
flickerstreak@1 295 local function LeaveAceCommChannels(all)
flickerstreak@1 296 if all then
flickerstreak@1 297 shutdown = true
flickerstreak@1 298 end
flickerstreak@1 299 local _,a,_,b,_,c,_,d,_,e,_,f,_,g,_,h,_,i,_,j = GetChannelList()
flickerstreak@1 300 tmp[1] = a
flickerstreak@1 301 tmp[2] = b
flickerstreak@1 302 tmp[3] = c
flickerstreak@1 303 tmp[4] = d
flickerstreak@1 304 tmp[5] = e
flickerstreak@1 305 tmp[6] = f
flickerstreak@1 306 tmp[7] = g
flickerstreak@1 307 tmp[8] = h
flickerstreak@1 308 tmp[9] = i
flickerstreak@1 309 tmp[10] = j
flickerstreak@1 310 for _,v in ipairs(tmp) do
flickerstreak@1 311 if v and string_find(v, "^AceComm") then
flickerstreak@1 312 if not SupposedToBeInChannel(v) then
flickerstreak@1 313 LeaveChannelByName(v)
flickerstreak@1 314 end
flickerstreak@1 315 end
flickerstreak@1 316 end
flickerstreak@1 317 for i = 1, 10 do
flickerstreak@1 318 tmp[i] = nil
flickerstreak@1 319 end
flickerstreak@1 320 end
flickerstreak@1 321
flickerstreak@1 322 local lastRefix = 0
flickerstreak@1 323 local function RefixAceCommChannelsAndEvents()
flickerstreak@1 324 if GetTime() - lastRefix <= 5 then
flickerstreak@1 325 AceComm:ScheduleEvent(RefixAceCommChannelsAndEvents, 1)
flickerstreak@1 326 return
flickerstreak@1 327 end
flickerstreak@1 328 lastRefix = GetTime()
flickerstreak@1 329 LeaveAceCommChannels(false)
flickerstreak@1 330
flickerstreak@1 331 local channel = false
flickerstreak@1 332 local whisper = false
flickerstreak@1 333 local addon = false
flickerstreak@1 334 if SupposedToBeInChannel("AceComm") then
flickerstreak@1 335 JoinChannel("AceComm")
flickerstreak@1 336 channel = true
flickerstreak@1 337 end
flickerstreak@1 338 if SupposedToBeInChannel(GetCurrentZoneChannel()) then
flickerstreak@1 339 JoinChannel(GetCurrentZoneChannel())
flickerstreak@1 340 channel = true
flickerstreak@1 341 end
flickerstreak@1 342 if AceComm_registry.CUSTOM then
flickerstreak@1 343 for k,v in pairs(AceComm_registry.CUSTOM) do
flickerstreak@1 344 if next(v) and SupposedToBeInChannel(k) then
flickerstreak@1 345 JoinChannel(k)
flickerstreak@1 346 channel = true
flickerstreak@1 347 end
flickerstreak@1 348 end
flickerstreak@1 349 end
flickerstreak@1 350 if AceComm_registry.WHISPER then
flickerstreak@1 351 whisper = true
flickerstreak@1 352 end
flickerstreak@1 353 if AceComm_registry.GROUP or AceComm_registry.PARTY or AceComm_registry.RAID or AceComm_registry.BATTLEGROUND or AceComm_registry.GUILD then
flickerstreak@1 354 addon = true
flickerstreak@1 355 end
flickerstreak@1 356
flickerstreak@1 357 if channel then
flickerstreak@1 358 if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL") then
flickerstreak@1 359 AceComm:RegisterEvent("CHAT_MSG_CHANNEL")
flickerstreak@1 360 end
flickerstreak@1 361 if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LIST") then
flickerstreak@1 362 AceComm:RegisterEvent("CHAT_MSG_CHANNEL_LIST")
flickerstreak@1 363 end
flickerstreak@1 364 if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_JOIN") then
flickerstreak@1 365 AceComm:RegisterEvent("CHAT_MSG_CHANNEL_JOIN")
flickerstreak@1 366 end
flickerstreak@1 367 if not AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LEAVE") then
flickerstreak@1 368 AceComm:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE")
flickerstreak@1 369 end
flickerstreak@1 370 else
flickerstreak@1 371 if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL") then
flickerstreak@1 372 AceComm:UnregisterEvent("CHAT_MSG_CHANNEL")
flickerstreak@1 373 end
flickerstreak@1 374 if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LIST") then
flickerstreak@1 375 AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_LIST")
flickerstreak@1 376 end
flickerstreak@1 377 if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_JOIN") then
flickerstreak@1 378 AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_JOIN")
flickerstreak@1 379 end
flickerstreak@1 380 if AceComm:IsEventRegistered("CHAT_MSG_CHANNEL_LEAVE") then
flickerstreak@1 381 AceComm:UnregisterEvent("CHAT_MSG_CHANNEL_LEAVE")
flickerstreak@1 382 end
flickerstreak@1 383 end
flickerstreak@1 384
flickerstreak@1 385 if whisper then
flickerstreak@1 386 if not AceComm:IsEventRegistered("CHAT_MSG_WHISPER") then
flickerstreak@1 387 AceComm:RegisterEvent("CHAT_MSG_WHISPER")
flickerstreak@1 388 end
flickerstreak@1 389 else
flickerstreak@1 390 if AceComm:IsEventRegistered("CHAT_MSG_WHISPER") then
flickerstreak@1 391 AceComm:UnregisterEvent("CHAT_MSG_WHISPER")
flickerstreak@1 392 end
flickerstreak@1 393 end
flickerstreak@1 394
flickerstreak@1 395 if addon then
flickerstreak@1 396 if not AceComm:IsEventRegistered("CHAT_MSG_ADDON") then
flickerstreak@1 397 AceComm:RegisterEvent("CHAT_MSG_ADDON")
flickerstreak@1 398 end
flickerstreak@1 399 else
flickerstreak@1 400 if AceComm:IsEventRegistered("CHAT_MSG_ADDON") then
flickerstreak@1 401 AceComm:UnregisterEvent("CHAT_MSG_ADDON")
flickerstreak@1 402 end
flickerstreak@1 403 end
flickerstreak@1 404 end
flickerstreak@1 405
flickerstreak@1 406
flickerstreak@1 407 do
flickerstreak@1 408 local myFunc = function(k)
flickerstreak@1 409 if not IsInChannel(k.latter) then
flickerstreak@1 410 JoinChannelByName(k.latter)
flickerstreak@1 411 end
flickerstreak@1 412 switches[k] = nil
flickerstreak@1 413 end
flickerstreak@1 414
flickerstreak@1 415 function AceComm:CHAT_MSG_CHANNEL_NOTICE(kind, _, _, deadName, _, _, _, num, channel)
flickerstreak@1 416 if kind == "YOU_LEFT" then
flickerstreak@1 417 if not string_find(channel, "^AceComm") then
flickerstreak@1 418 return
flickerstreak@1 419 end
flickerstreak@1 420 for k in pairs(switches) do
flickerstreak@1 421 if k.former == channel then
flickerstreak@1 422 self:ScheduleEvent(myFunc, 0, k)
flickerstreak@1 423 end
flickerstreak@1 424 end
flickerstreak@1 425 if channel == GetCurrentZoneChannel() then
flickerstreak@1 426 self:TriggerEvent("AceComm_LeftChannel", "ZONE")
flickerstreak@1 427 elseif channel == "AceComm" then
flickerstreak@1 428 self:TriggerEvent("AceComm_LeftChannel", "GLOBAL")
flickerstreak@1 429 else
flickerstreak@1 430 self:TriggerEvent("AceComm_LeftChannel", "CUSTOM", string_sub(channel, 8))
flickerstreak@1 431 end
flickerstreak@1 432 if string_find(channel, "^AceComm") and SupposedToBeInChannel(channel) then
flickerstreak@1 433 self:ScheduleEvent(JoinChannel, 0, channel)
flickerstreak@1 434 end
flickerstreak@1 435 if AceComm.userRegistry[channel] then
flickerstreak@1 436 AceComm.userRegistry[channel] = nil
flickerstreak@1 437 end
flickerstreak@1 438 elseif kind == "YOU_JOINED" then
flickerstreak@1 439 if not string_find(num == 0 and deadName or channel, "^AceComm") then
flickerstreak@1 440 return
flickerstreak@1 441 end
flickerstreak@1 442 if num == 0 then
flickerstreak@1 443 self:ScheduleEvent(LeaveChannelByName, 0, deadName)
flickerstreak@1 444 switches[{
flickerstreak@1 445 former = deadName,
flickerstreak@1 446 latter = deadName,
flickerstreak@1 447 }] = true
flickerstreak@1 448 elseif channel == GetCurrentZoneChannel() then
flickerstreak@1 449 self:TriggerEvent("AceComm_JoinedChannel", "ZONE")
flickerstreak@1 450 elseif channel == "AceComm" then
flickerstreak@1 451 self:TriggerEvent("AceComm_JoinedChannel", "GLOBAL")
flickerstreak@1 452 else
flickerstreak@1 453 self:TriggerEvent("AceComm_JoinedChannel", "CUSTOM", string_sub(channel, 8))
flickerstreak@1 454 end
flickerstreak@1 455 if num ~= 0 then
flickerstreak@1 456 if not SupposedToBeInChannel(channel) then
flickerstreak@1 457 LeaveChannel(channel)
flickerstreak@1 458 else
flickerstreak@1 459 ListChannelByName(channel)
flickerstreak@1 460 end
flickerstreak@1 461 end
flickerstreak@1 462 end
flickerstreak@1 463 end
flickerstreak@1 464 end
flickerstreak@1 465
flickerstreak@1 466 local Serialize
flickerstreak@1 467 do
flickerstreak@1 468 local recurse
flickerstreak@1 469 local function _Serialize(v, textToHash)
flickerstreak@1 470 local kind = type(v)
flickerstreak@1 471 if kind == "boolean" then
flickerstreak@1 472 if v then
flickerstreak@1 473 return "B" -- true
flickerstreak@1 474 else
flickerstreak@1 475 return "b" -- false
flickerstreak@1 476 end
flickerstreak@1 477 elseif not v then
flickerstreak@1 478 return "/"
flickerstreak@1 479 elseif kind == "number" then
flickerstreak@1 480 if v == math_floor(v) then
flickerstreak@1 481 if v <= 127 and v >= -128 then
flickerstreak@1 482 if v < 0 then
flickerstreak@1 483 v = v + 256
flickerstreak@1 484 end
flickerstreak@1 485 return string_char(byte_d, v)
flickerstreak@1 486 elseif v <= 32767 and v >= -32768 then
flickerstreak@1 487 if v < 0 then
flickerstreak@1 488 v = v + 65536
flickerstreak@1 489 end
flickerstreak@1 490 return string_char(byte_D, math_floor(v / 256), math_mod(v, 256))
flickerstreak@1 491 elseif v <= 2147483647 and v >= -2147483648 then
flickerstreak@1 492 if v < 0 then
flickerstreak@1 493 v = v + 4294967296
flickerstreak@1 494 end
flickerstreak@1 495 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 496 elseif v <= 9223372036854775807 and v >= -9223372036854775808 then
flickerstreak@1 497 if v < 0 then
flickerstreak@1 498 v = v + 18446744073709551616
flickerstreak@1 499 end
flickerstreak@1 500 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 501 end
flickerstreak@1 502 elseif v == inf then
flickerstreak@1 503 return string_char(64 --[[byte_inf]])
flickerstreak@1 504 elseif v == -inf then
flickerstreak@1 505 return string_char(36 --[[byte_ninf]])
flickerstreak@1 506 elseif v ~= v then
flickerstreak@1 507 return string_char(33 --[[byte_nan]])
flickerstreak@1 508 end
flickerstreak@1 509 -- do
flickerstreak@1 510 -- local s = tostring(v)
flickerstreak@1 511 -- local len = string_len(s)
flickerstreak@1 512 -- return string_char(byte_plus, len) .. s
flickerstreak@1 513 -- end
flickerstreak@1 514 local sign = v < 0 or v == 0 and tostring(v) == "-0"
flickerstreak@1 515 if sign then
flickerstreak@1 516 v = -v
flickerstreak@1 517 end
flickerstreak@1 518 local m, exp = math.frexp(v)
flickerstreak@1 519 m = m * 9007199254740992
flickerstreak@1 520 local x = exp + 1023
flickerstreak@1 521 local b = math_mod(m, 256)
flickerstreak@1 522 local c = math_floormod(m / 256, 256)
flickerstreak@1 523 m = math_floor(m / 65536)
flickerstreak@1 524 m = m + x * 137438953472
flickerstreak@1 525 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 526 elseif kind == "string" then
flickerstreak@1 527 local hash = textToHash and textToHash[v]
flickerstreak@1 528 if hash then
flickerstreak@1 529 return string_char(byte_m, math_floor(hash / 65536), math_floormod(hash / 256, 256), math_mod(hash, 256))
flickerstreak@1 530 end
flickerstreak@1 531 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 532 if A then
flickerstreak@1 533 -- item link
flickerstreak@1 534
flickerstreak@1 535 A = A+0 -- convert to number
flickerstreak@1 536 B = B+0
flickerstreak@1 537 C = C+0
flickerstreak@1 538 D = D+0
flickerstreak@1 539 E = E+0
flickerstreak@1 540 F = F+0
flickerstreak@1 541 G = G+0
flickerstreak@1 542 H = H+0
flickerstreak@1 543
flickerstreak@1 544 -- (1-35000):(1-3093):(1-3093):(1-3093):(1-3093):(?):(-57 to 2164):(0-4294967295)
flickerstreak@1 545
flickerstreak@1 546 F = nil -- don't care
flickerstreak@1 547 if G < 0 then
flickerstreak@1 548 G = G + 65536 -- handle negatives
flickerstreak@1 549 end
flickerstreak@1 550
flickerstreak@1 551 H = math_mod(H, 65536) -- only lower 16 bits matter
flickerstreak@1 552
flickerstreak@1 553 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 554 else
flickerstreak@1 555 -- normal string
flickerstreak@1 556 local len = string_len(v)
flickerstreak@1 557 if len <= 255 then
flickerstreak@1 558 return string_char(byte_s, len) .. v
flickerstreak@1 559 else
flickerstreak@1 560 return string_char(byte_S, math_floor(len / 256), math_mod(len, 256)) .. v
flickerstreak@1 561 end
flickerstreak@1 562 end
flickerstreak@1 563 elseif kind == "function" then
flickerstreak@1 564 AceComm:error("Cannot serialize a function")
flickerstreak@1 565 elseif kind == "table" then
flickerstreak@1 566 if recurse[v] then
flickerstreak@1 567 for k in pairs(recurse) do
flickerstreak@1 568 recurse[k] = nil
flickerstreak@1 569 end
flickerstreak@1 570 AceComm:error("Cannot serialize a recursive table")
flickerstreak@1 571 return
flickerstreak@1 572 end
flickerstreak@1 573 recurse[v] = true
flickerstreak@1 574 if AceOO.inherits(v, AceOO.Class) then
flickerstreak@1 575 if not v.class then
flickerstreak@1 576 AceComm:error("Cannot serialize an AceOO class, can only serialize objects")
flickerstreak@1 577 elseif type(v.Serialize) ~= "function" then
flickerstreak@1 578 AceComm:error("Cannot serialize an AceOO object without the `Serialize' method.")
flickerstreak@1 579 elseif type(v.class.Deserialize) ~= "function" then
flickerstreak@1 580 AceComm:error("Cannot serialize an AceOO object without the `Deserialize' static method.")
flickerstreak@1 581 elseif type(v.class.GetLibraryVersion) ~= "function" or not AceLibrary:HasInstance(v.class:GetLibraryVersion()) then
flickerstreak@1 582 AceComm:error("Cannot serialize an AceOO object if the class is not registered with AceLibrary.")
flickerstreak@1 583 end
flickerstreak@1 584 local classHash = TailoredBinaryCheckSum(v.class:GetLibraryVersion())
flickerstreak@1 585 local t = { classHash, v:Serialize() }
flickerstreak@1 586 for i = 2, #t do
flickerstreak@1 587 t[i] = _Serialize(t[i], textToHash)
flickerstreak@1 588 end
flickerstreak@1 589 if not notFirst then
flickerstreak@1 590 for k in pairs(recurse) do
flickerstreak@1 591 recurse[k] = nil
flickerstreak@1 592 end
flickerstreak@1 593 end
flickerstreak@1 594 local s = table.concat(t)
flickerstreak@1 595 t = nil
flickerstreak@1 596 local len = string_len(s)
flickerstreak@1 597 if len <= 255 then
flickerstreak@1 598 return string_char(byte_o, len) .. s
flickerstreak@1 599 else
flickerstreak@1 600 return string_char(byte_O, math_floor(len / 256), math_mod(len, 256)) .. s
flickerstreak@1 601 end
flickerstreak@1 602 end
flickerstreak@1 603 local t = {}
flickerstreak@1 604 local islist = false
flickerstreak@1 605 local n = #v
flickerstreak@1 606 if n >= 1 then
flickerstreak@1 607 islist = true
flickerstreak@1 608 for k,u in pairs(v) do
flickerstreak@1 609 if type(k) ~= "number" or k < 1 then
flickerstreak@1 610 islist = false
flickerstreak@1 611 break
flickerstreak@1 612 end
flickerstreak@1 613 end
flickerstreak@1 614 end
flickerstreak@1 615 if islist then
flickerstreak@1 616 n = n * 4
flickerstreak@1 617 while v[n] == nil do
flickerstreak@1 618 n = n - 1
flickerstreak@1 619 end
flickerstreak@1 620 for i = 1, n do
flickerstreak@1 621 t[i] = _Serialize(v[i], textToHash)
flickerstreak@1 622 end
flickerstreak@1 623 else
flickerstreak@1 624 local i = 1
flickerstreak@1 625 for k,u in pairs(v) do
flickerstreak@1 626 t[i] = _Serialize(k, textToHash)
flickerstreak@1 627 t[i+1] = _Serialize(u, textToHash)
flickerstreak@1 628 i = i + 2
flickerstreak@1 629 end
flickerstreak@1 630 end
flickerstreak@1 631 if not notFirst then
flickerstreak@1 632 for k in pairs(recurse) do
flickerstreak@1 633 recurse[k] = nil
flickerstreak@1 634 end
flickerstreak@1 635 end
flickerstreak@1 636 local s = table.concat(t)
flickerstreak@1 637 t = nil
flickerstreak@1 638 local len = string_len(s)
flickerstreak@1 639 if islist then
flickerstreak@1 640 if len <= 255 then
flickerstreak@1 641 return string_char(byte_u, len) .. s
flickerstreak@1 642 else
flickerstreak@1 643 return "U" .. string_char(math_floor(len / 256), math_mod(len, 256)) .. s
flickerstreak@1 644 end
flickerstreak@1 645 else
flickerstreak@1 646 if len <= 255 then
flickerstreak@1 647 return "t" .. string_char(len) .. s
flickerstreak@1 648 else
flickerstreak@1 649 return "T" .. string_char(math_floor(len / 256), math_mod(len, 256)) .. s
flickerstreak@1 650 end
flickerstreak@1 651 end
flickerstreak@1 652 end
flickerstreak@1 653 end
flickerstreak@1 654
flickerstreak@1 655 function Serialize(value, textToHash)
flickerstreak@1 656 if not recurse then
flickerstreak@1 657 recurse = {}
flickerstreak@1 658 end
flickerstreak@1 659 local chunk = _Serialize(value, textToHash)
flickerstreak@1 660 for k in pairs(recurse) do
flickerstreak@1 661 recurse[k] = nil
flickerstreak@1 662 end
flickerstreak@1 663 return chunk
flickerstreak@1 664 end
flickerstreak@1 665 end
flickerstreak@1 666
flickerstreak@1 667 local Deserialize
flickerstreak@1 668 do
flickerstreak@1 669 local tmp = {}
flickerstreak@1 670 local function _Deserialize(value, position, hashToText)
flickerstreak@1 671 if not position then
flickerstreak@1 672 position = 1
flickerstreak@1 673 end
flickerstreak@1 674 local x = string_byte(value, position)
flickerstreak@1 675 if x == byte_b then
flickerstreak@1 676 -- false
flickerstreak@1 677 return false, position
flickerstreak@1 678 elseif x == byte_B then
flickerstreak@1 679 -- true
flickerstreak@1 680 return true, position
flickerstreak@1 681 elseif x == byte_nil then
flickerstreak@1 682 -- nil
flickerstreak@1 683 return nil, position
flickerstreak@1 684 elseif x == byte_i then
flickerstreak@1 685 -- 14-byte item link
flickerstreak@1 686 local a1 = string_byte(value, position + 1)
flickerstreak@1 687 local a2 = string_byte(value, position + 2)
flickerstreak@1 688 local b1 = string_byte(value, position + 3)
flickerstreak@1 689 local b2 = string_byte(value, position + 4)
flickerstreak@1 690 local c1 = string_byte(value, position + 5)
flickerstreak@1 691 local c2 = string_byte(value, position + 6)
flickerstreak@1 692 local d1 = string_byte(value, position + 7)
flickerstreak@1 693 local d2 = string_byte(value, position + 8)
flickerstreak@1 694 local e1 = string_byte(value, position + 9)
flickerstreak@1 695 local e2 = string_byte(value, position + 10)
flickerstreak@1 696 local g1 = string_byte(value, position + 11)
flickerstreak@1 697 local g2 = string_byte(value, position + 12)
flickerstreak@1 698 local h1 = string_byte(value, position + 13)
flickerstreak@1 699 local h2 = string_byte(value, position + 14)
flickerstreak@1 700 local A = a1 * 256 + a2
flickerstreak@1 701 local B = b1 * 256 + b2
flickerstreak@1 702 local C = c1 * 256 + c2
flickerstreak@1 703 local D = d1 * 256 + d2
flickerstreak@1 704 local E = e1 * 256 + e2
flickerstreak@1 705 local G = g1 * 256 + g2
flickerstreak@1 706 local H = h1 * 256 + h2
flickerstreak@1 707 if G >= 32768 then
flickerstreak@1 708 G = G - 65536
flickerstreak@1 709 end
flickerstreak@1 710 local s = string.format("item:%d:%d:%d:%d:%d:%d:%d:%d", A, B, C, D, E, 0, G, H)
flickerstreak@1 711 local _, link = GetItemInfo(s)
flickerstreak@1 712 return link, position + 14
flickerstreak@1 713 elseif x == byte_m then
flickerstreak@1 714 local hash = string_byte(value, position + 1) * 65536 + string_byte(value, position + 2) * 256 + string_byte(value, position + 3)
flickerstreak@1 715 return hashToText[hash], position + 3
flickerstreak@1 716 elseif x == byte_s then
flickerstreak@1 717 -- 0-255-byte string
flickerstreak@1 718 local len = string_byte(value, position + 1)
flickerstreak@1 719 return string.sub(value, position + 2, position + 1 + len), position + 1 + len
flickerstreak@1 720 elseif x == byte_S then
flickerstreak@1 721 -- 256-65535-byte string
flickerstreak@1 722 local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2)
flickerstreak@1 723 return string.sub(value, position + 3, position + 2 + len), position + 2 + len
flickerstreak@1 724 elseif x == 64 --[[byte_inf]] then
flickerstreak@1 725 return inf, position
flickerstreak@1 726 elseif x == 36 --[[byte_ninf]] then
flickerstreak@1 727 return -inf, position
flickerstreak@1 728 elseif x == 33 --[[byte_nan]] then
flickerstreak@1 729 return nan, position
flickerstreak@1 730 elseif x == byte_d then
flickerstreak@1 731 -- 1-byte integer
flickerstreak@1 732 local a = string_byte(value, position + 1)
flickerstreak@1 733 if a >= 128 then
flickerstreak@1 734 a = a - 256
flickerstreak@1 735 end
flickerstreak@1 736 return a, position + 1
flickerstreak@1 737 elseif x == byte_D then
flickerstreak@1 738 -- 2-byte integer
flickerstreak@1 739 local a = string_byte(value, position + 1)
flickerstreak@1 740 local b = string_byte(value, position + 2)
flickerstreak@1 741 local N = a * 256 + b
flickerstreak@1 742 if N >= 32768 then
flickerstreak@1 743 N = N - 65536
flickerstreak@1 744 end
flickerstreak@1 745 return N, position + 2
flickerstreak@1 746 elseif x == byte_e then
flickerstreak@1 747 -- 4-byte integer
flickerstreak@1 748 local a = string_byte(value, position + 1)
flickerstreak@1 749 local b = string_byte(value, position + 2)
flickerstreak@1 750 local c = string_byte(value, position + 3)
flickerstreak@1 751 local d = string_byte(value, position + 4)
flickerstreak@1 752 local N = a * 16777216 + b * 65536 + c * 256 + d
flickerstreak@1 753 if N >= 2147483648 then
flickerstreak@1 754 N = N - 4294967296
flickerstreak@1 755 end
flickerstreak@1 756 return N, position + 4
flickerstreak@1 757 elseif x == byte_E then
flickerstreak@1 758 -- 8-byte integer
flickerstreak@1 759 local a = string_byte(value, position + 1)
flickerstreak@1 760 local b = string_byte(value, position + 2)
flickerstreak@1 761 local c = string_byte(value, position + 3)
flickerstreak@1 762 local d = string_byte(value, position + 4)
flickerstreak@1 763 local e = string_byte(value, position + 5)
flickerstreak@1 764 local f = string_byte(value, position + 6)
flickerstreak@1 765 local g = string_byte(value, position + 7)
flickerstreak@1 766 local h = string_byte(value, position + 8)
flickerstreak@1 767 local N = a * 72057594037927936 + b * 281474976710656 + c * 1099511627776 + d * 4294967296 + e * 16777216 + f * 65536 + g * 256 + h
flickerstreak@1 768 if N >= 9223372036854775808 then
flickerstreak@1 769 N = N - 18446744073709551616
flickerstreak@1 770 end
flickerstreak@1 771 return N, position + 8
flickerstreak@1 772 elseif x == byte_plus or x == byte_minus then
flickerstreak@1 773 local a = string_byte(value, position + 1)
flickerstreak@1 774 local b = string_byte(value, position + 2)
flickerstreak@1 775 local c = string_byte(value, position + 3)
flickerstreak@1 776 local d = string_byte(value, position + 4)
flickerstreak@1 777 local e = string_byte(value, position + 5)
flickerstreak@1 778 local f = string_byte(value, position + 6)
flickerstreak@1 779 local g = string_byte(value, position + 7)
flickerstreak@1 780 local h = string_byte(value, position + 8)
flickerstreak@1 781 local N = a * 1099511627776 + b * 4294967296 + c * 16777216 + d * 65536 + e * 256 + f
flickerstreak@1 782 local sign = x
flickerstreak@1 783 local x = math.floor(N / 137438953472)
flickerstreak@1 784 local m = math_mod(N, 137438953472) * 65536 + g * 256 + h
flickerstreak@1 785 local mantissa = m / 9007199254740992
flickerstreak@1 786 local exp = x - 1023
flickerstreak@1 787 local val = math.ldexp(mantissa, exp)
flickerstreak@1 788 if sign == byte_minus then
flickerstreak@1 789 return -val, position + 8
flickerstreak@1 790 end
flickerstreak@1 791 return val, position + 8
flickerstreak@1 792 elseif x == byte_u or x == byte_U then
flickerstreak@1 793 -- numerically-indexed table
flickerstreak@1 794 local finish
flickerstreak@1 795 local start
flickerstreak@1 796 if x == byte_u then
flickerstreak@1 797 local len = string_byte(value, position + 1)
flickerstreak@1 798 finish = position + 1 + len
flickerstreak@1 799 start = position + 2
flickerstreak@1 800 else
flickerstreak@1 801 local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2)
flickerstreak@1 802 finish = position + 2 + len
flickerstreak@1 803 start = position + 3
flickerstreak@1 804 end
flickerstreak@1 805 local t = {}
flickerstreak@1 806 local n = 0
flickerstreak@1 807 local curr = start - 1
flickerstreak@1 808 while curr < finish do
flickerstreak@1 809 local v
flickerstreak@1 810 v, curr = _Deserialize(value, curr + 1, hashToText)
flickerstreak@1 811 n = n + 1
flickerstreak@1 812 t[n] = v
flickerstreak@1 813 end
flickerstreak@1 814 return t, finish
flickerstreak@1 815 elseif x == byte_o or x == byte_O then
flickerstreak@1 816 -- numerically-indexed table
flickerstreak@1 817 local finish
flickerstreak@1 818 local start
flickerstreak@1 819 if x == byte_o then
flickerstreak@1 820 local len = string_byte(value, position + 1)
flickerstreak@1 821 finish = position + 1 + len
flickerstreak@1 822 start = position + 2
flickerstreak@1 823 else
flickerstreak@1 824 local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2)
flickerstreak@1 825 finish = position + 2 + len
flickerstreak@1 826 start = position + 3
flickerstreak@1 827 end
flickerstreak@1 828 local hash = string_byte(value, start) * 65536 + string_byte(value, start + 1) * 256 + string_byte(value, start + 2)
flickerstreak@1 829 local curr = start + 2
flickerstreak@1 830 if not AceComm.classes[hash] then
flickerstreak@1 831 return nil, finish
flickerstreak@1 832 end
flickerstreak@1 833 local class = AceComm.classes[hash]
flickerstreak@1 834 if type(class.Deserialize) ~= "function" or type(class.prototype.Serialize) ~= "function" then
flickerstreak@1 835 return nil, finish
flickerstreak@1 836 end
flickerstreak@1 837 local n = 0
flickerstreak@1 838 while curr < finish do
flickerstreak@1 839 local v
flickerstreak@1 840 v, curr = _Deserialize(value, curr + 1, hashToText)
flickerstreak@1 841 n = n + 1
flickerstreak@1 842 tmp[n] = v
flickerstreak@1 843 end
flickerstreak@1 844 local object = class:Deserialize(unpack(tmp))
flickerstreak@1 845 for i = 1, n do
flickerstreak@1 846 tmp[i] = nil
flickerstreak@1 847 end
flickerstreak@1 848 return object, finish
flickerstreak@1 849 elseif x == byte_t or x == byte_T then
flickerstreak@1 850 -- table
flickerstreak@1 851 local finish
flickerstreak@1 852 local start
flickerstreak@1 853 if x == byte_t then
flickerstreak@1 854 local len = string_byte(value, position + 1)
flickerstreak@1 855 finish = position + 1 + len
flickerstreak@1 856 start = position + 2
flickerstreak@1 857 else
flickerstreak@1 858 local len = string_byte(value, position + 1) * 256 + string_byte(value, position + 2)
flickerstreak@1 859 finish = position + 2 + len
flickerstreak@1 860 start = position + 3
flickerstreak@1 861 end
flickerstreak@1 862 local t = {}
flickerstreak@1 863 local curr = start - 1
flickerstreak@1 864 while curr < finish do
flickerstreak@1 865 local key, l = _Deserialize(value, curr + 1, hashToText)
flickerstreak@1 866 local value, m = _Deserialize(value, l + 1, hashToText)
flickerstreak@1 867 curr = m
flickerstreak@1 868 t[key] = value
flickerstreak@1 869 end
flickerstreak@1 870 if type(t.n) ~= "number" then
flickerstreak@1 871 local i = 1
flickerstreak@1 872 while t[i] ~= nil do
flickerstreak@1 873 i = i + 1
flickerstreak@1 874 end
flickerstreak@1 875 end
flickerstreak@1 876 return t, finish
flickerstreak@1 877 else
flickerstreak@1 878 error("Improper serialized value provided")
flickerstreak@1 879 end
flickerstreak@1 880 end
flickerstreak@1 881
flickerstreak@1 882 function Deserialize(value, hashToText)
flickerstreak@1 883 local ret,msg = pcall(_Deserialize, value, nil, hashToText)
flickerstreak@1 884 if ret then
flickerstreak@1 885 return msg
flickerstreak@1 886 end
flickerstreak@1 887 end
flickerstreak@1 888 end
flickerstreak@1 889
flickerstreak@1 890 local function GetCurrentGroupDistribution()
flickerstreak@1 891 if MiniMapBattlefieldFrame.status == "active" then
flickerstreak@1 892 return "BATTLEGROUND"
flickerstreak@1 893 elseif UnitInRaid("player") then
flickerstreak@1 894 return "RAID"
flickerstreak@1 895 elseif UnitInParty("player") then
flickerstreak@1 896 return "PARTY"
flickerstreak@1 897 else
flickerstreak@1 898 return nil
flickerstreak@1 899 end
flickerstreak@1 900 end
flickerstreak@1 901
flickerstreak@1 902 local function IsInDistribution(dist, customChannel)
flickerstreak@1 903 if dist == "GROUP" then
flickerstreak@1 904 return GetCurrentGroupDistribution() and true or false
flickerstreak@1 905 elseif dist == "BATTLEGROUND" then
flickerstreak@1 906 return MiniMapBattlefieldFrame.status == "active"
flickerstreak@1 907 elseif dist == "RAID" then
flickerstreak@1 908 return UnitInRaid("player") == 1
flickerstreak@1 909 elseif dist == "PARTY" then
flickerstreak@1 910 return UnitInParty("player") == 1
flickerstreak@1 911 elseif dist == "GUILD" then
flickerstreak@1 912 return IsInGuild() == 1
flickerstreak@1 913 elseif dist == "GLOBAL" then
flickerstreak@1 914 return IsInChannel("AceComm")
flickerstreak@1 915 elseif dist == "ZONE" then
flickerstreak@1 916 return IsInChannel(GetCurrentZoneChannel())
flickerstreak@1 917 elseif dist == "WHISPER" then
flickerstreak@1 918 return true
flickerstreak@1 919 elseif dist == "CUSTOM" then
flickerstreak@1 920 return IsInChannel(customChannel)
flickerstreak@1 921 end
flickerstreak@1 922 error("unknown distribution: " .. dist, 2)
flickerstreak@1 923 end
flickerstreak@1 924
flickerstreak@1 925 function AceComm:RegisterComm(prefix, distribution, method, a4)
flickerstreak@1 926 AceComm:argCheck(prefix, 2, "string")
flickerstreak@1 927 AceComm:argCheck(distribution, 3, "string")
flickerstreak@1 928 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 929 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 930 end
flickerstreak@1 931 local customChannel
flickerstreak@1 932 if distribution == "CUSTOM" then
flickerstreak@1 933 customChannel, method = method, a4
flickerstreak@1 934 AceComm:argCheck(customChannel, 4, "string")
flickerstreak@1 935 if string_len(customChannel) == 0 then
flickerstreak@1 936 AceComm:error('Argument #4 to `RegisterComm\' must be a non-zero-length string.')
flickerstreak@1 937 elseif string_find(customChannel, "%s") then
flickerstreak@1 938 AceComm:error('Argument #4 to `RegisterComm\' must not have spaces.')
flickerstreak@1 939 end
flickerstreak@1 940 end
flickerstreak@1 941 if self == AceComm then
flickerstreak@1 942 AceComm:argCheck(method, customChannel and 5 or 4, "function", "table")
flickerstreak@1 943 self = method
flickerstreak@1 944 else
flickerstreak@1 945 AceComm:argCheck(method, customChannel and 5 or 4, "string", "function", "table", "nil")
flickerstreak@1 946 end
flickerstreak@1 947 if not method then
flickerstreak@1 948 method = "OnCommReceive"
flickerstreak@1 949 end
flickerstreak@1 950 if type(method) == "string" and type(self[method]) ~= "function" and type(self[method]) ~= "table" then
flickerstreak@1 951 AceEvent:error("Cannot register comm %q to method %q, it does not exist", prefix, method)
flickerstreak@1 952 end
flickerstreak@1 953
flickerstreak@1 954 local registry = AceComm_registry
flickerstreak@1 955 if not registry[distribution] then
flickerstreak@1 956 registry[distribution] = {}
flickerstreak@1 957 end
flickerstreak@1 958 if customChannel then
flickerstreak@1 959 customChannel = "AceComm" .. customChannel
flickerstreak@1 960 if not registry[distribution][customChannel] then
flickerstreak@1 961 registry[distribution][customChannel] = {}
flickerstreak@1 962 end
flickerstreak@1 963 if not registry[distribution][customChannel][prefix] then
flickerstreak@1 964 registry[distribution][customChannel][prefix] = {}
flickerstreak@1 965 end
flickerstreak@1 966 registry[distribution][customChannel][prefix][self] = method
flickerstreak@1 967 else
flickerstreak@1 968 if not registry[distribution][prefix] then
flickerstreak@1 969 registry[distribution][prefix] = {}
flickerstreak@1 970 end
flickerstreak@1 971 registry[distribution][prefix][self] = method
flickerstreak@1 972 end
flickerstreak@1 973
flickerstreak@1 974 RefixAceCommChannelsAndEvents()
flickerstreak@1 975 end
flickerstreak@1 976
flickerstreak@1 977 function AceComm:UnregisterComm(prefix, distribution, customChannel)
flickerstreak@1 978 AceComm:argCheck(prefix, 2, "string")
flickerstreak@1 979 AceComm:argCheck(distribution, 3, "string", "nil")
flickerstreak@1 980 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 981 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 982 end
flickerstreak@1 983 if distribution == "CUSTOM" then
flickerstreak@1 984 AceComm:argCheck(customChannel, 3, "string")
flickerstreak@1 985 if string_len(customChannel) == 0 then
flickerstreak@1 986 AceComm:error('Argument #3 to `UnregisterComm\' must be a non-zero-length string.')
flickerstreak@1 987 end
flickerstreak@1 988 else
flickerstreak@1 989 AceComm:argCheck(customChannel, 3, "nil")
flickerstreak@1 990 end
flickerstreak@1 991
flickerstreak@1 992 local registry = AceComm_registry
flickerstreak@1 993 if not distribution then
flickerstreak@1 994 for k,v in pairs(registry) do
flickerstreak@1 995 if k == "CUSTOM" then
flickerstreak@1 996 for l,u in pairs(v) do
flickerstreak@1 997 if u[prefix] and u[prefix][self] then
flickerstreak@1 998 AceComm.UnregisterComm(self, prefix, k, string.sub(l, 8))
flickerstreak@1 999 if not registry[k] then
flickerstreak@1 1000 break
flickerstreak@1 1001 end
flickerstreak@1 1002 end
flickerstreak@1 1003 end
flickerstreak@1 1004 else
flickerstreak@1 1005 if v[prefix] and v[prefix][self] then
flickerstreak@1 1006 AceComm.UnregisterComm(self, prefix, k)
flickerstreak@1 1007 end
flickerstreak@1 1008 end
flickerstreak@1 1009 end
flickerstreak@1 1010 return
flickerstreak@1 1011 end
flickerstreak@1 1012 if self == AceComm then
flickerstreak@1 1013 if distribution == "CUSTOM" then
flickerstreak@1 1014 error(string_format("Cannot unregister comm %q::%q. Improperly unregistering from AceComm-2.0.", distribution, customChannel), 2)
flickerstreak@1 1015 else
flickerstreak@1 1016 error(string_format("Cannot unregister comm %q. Improperly unregistering from AceComm-2.0.", distribution), 2)
flickerstreak@1 1017 end
flickerstreak@1 1018 end
flickerstreak@1 1019 if distribution == "CUSTOM" then
flickerstreak@1 1020 customChannel = "AceComm" .. customChannel
flickerstreak@1 1021 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 1022 AceComm:error("Cannot unregister comm %q. %q is not registered with it.", distribution, self)
flickerstreak@1 1023 end
flickerstreak@1 1024 registry[distribution][customChannel][prefix][self] = nil
flickerstreak@1 1025
flickerstreak@1 1026 if not next(registry[distribution][customChannel][prefix]) then
flickerstreak@1 1027 registry[distribution][customChannel][prefix] = nil
flickerstreak@1 1028 end
flickerstreak@1 1029
flickerstreak@1 1030 if not next(registry[distribution][customChannel]) then
flickerstreak@1 1031 registry[distribution][customChannel] = nil
flickerstreak@1 1032 end
flickerstreak@1 1033 else
flickerstreak@1 1034 if not registry[distribution] or not registry[distribution][prefix] or not registry[distribution][prefix][self] then
flickerstreak@1 1035 AceComm:error("Cannot unregister comm %q. %q is not registered with it.", distribution, self)
flickerstreak@1 1036 end
flickerstreak@1 1037 registry[distribution][prefix][self] = nil
flickerstreak@1 1038
flickerstreak@1 1039 if not next(registry[distribution][prefix]) then
flickerstreak@1 1040 registry[distribution][prefix] = nil
flickerstreak@1 1041 end
flickerstreak@1 1042 end
flickerstreak@1 1043
flickerstreak@1 1044 if not next(registry[distribution]) then
flickerstreak@1 1045 registry[distribution] = nil
flickerstreak@1 1046 end
flickerstreak@1 1047
flickerstreak@1 1048 RefixAceCommChannelsAndEvents()
flickerstreak@1 1049 end
flickerstreak@1 1050
flickerstreak@1 1051 function AceComm:UnregisterAllComms()
flickerstreak@1 1052 local registry = AceComm_registry
flickerstreak@1 1053 for k, distribution in pairs(registry) do
flickerstreak@1 1054 if k == "CUSTOM" then
flickerstreak@1 1055 for l, channel in pairs(distribution) do
flickerstreak@1 1056 local j = next(channel)
flickerstreak@1 1057 while j ~= nil do
flickerstreak@1 1058 local prefix = channel[j]
flickerstreak@1 1059 if prefix[self] then
flickerstreak@1 1060 AceComm.UnregisterComm(self, j)
flickerstreak@1 1061 if distribution[l] and registry[k] then
flickerstreak@1 1062 j = next(channel)
flickerstreak@1 1063 else
flickerstreak@1 1064 l = nil
flickerstreak@1 1065 k = nil
flickerstreak@1 1066 break
flickerstreak@1 1067 end
flickerstreak@1 1068 else
flickerstreak@1 1069 j = next(channel, j)
flickerstreak@1 1070 end
flickerstreak@1 1071 end
flickerstreak@1 1072 if k == nil then
flickerstreak@1 1073 break
flickerstreak@1 1074 end
flickerstreak@1 1075 end
flickerstreak@1 1076 else
flickerstreak@1 1077 local j = next(distribution)
flickerstreak@1 1078 while j ~= nil do
flickerstreak@1 1079 local prefix = distribution[j]
flickerstreak@1 1080 if prefix[self] then
flickerstreak@1 1081 AceComm.UnregisterComm(self, j)
flickerstreak@1 1082 if registry[k] then
flickerstreak@1 1083 j = next(distribution)
flickerstreak@1 1084 else
flickerstreak@1 1085 k = nil
flickerstreak@1 1086 break
flickerstreak@1 1087 end
flickerstreak@1 1088 else
flickerstreak@1 1089 j = next(distribution, j)
flickerstreak@1 1090 end
flickerstreak@1 1091 end
flickerstreak@1 1092 end
flickerstreak@1 1093 end
flickerstreak@1 1094 end
flickerstreak@1 1095
flickerstreak@1 1096 function AceComm:IsCommRegistered(prefix, distribution, customChannel)
flickerstreak@1 1097 AceComm:argCheck(prefix, 2, "string")
flickerstreak@1 1098 AceComm:argCheck(distribution, 3, "string", "nil")
flickerstreak@1 1099 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 1100 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 1101 end
flickerstreak@1 1102 if distribution == "CUSTOM" then
flickerstreak@1 1103 AceComm:argCheck(customChannel, 4, "nil", "string")
flickerstreak@1 1104 if customChannel == "" then
flickerstreak@1 1105 AceComm:error('Argument #4 to `IsCommRegistered\' must be a non-zero-length string or nil.')
flickerstreak@1 1106 end
flickerstreak@1 1107 else
flickerstreak@1 1108 AceComm:argCheck(customChannel, 4, "nil")
flickerstreak@1 1109 end
flickerstreak@1 1110 local registry = AceComm_registry
flickerstreak@1 1111 if not distribution then
flickerstreak@1 1112 for k,v in pairs(registry) do
flickerstreak@1 1113 if k == "CUSTOM" then
flickerstreak@1 1114 for l,u in pairs(v) do
flickerstreak@1 1115 if u[prefix] and u[prefix][self] then
flickerstreak@1 1116 return true
flickerstreak@1 1117 end
flickerstreak@1 1118 end
flickerstreak@1 1119 else
flickerstreak@1 1120 if v[prefix] and v[prefix][self] then
flickerstreak@1 1121 return true
flickerstreak@1 1122 end
flickerstreak@1 1123 end
flickerstreak@1 1124 end
flickerstreak@1 1125 return false
flickerstreak@1 1126 elseif distribution == "CUSTOM" and not customChannel then
flickerstreak@1 1127 if not registry[distribution] then
flickerstreak@1 1128 return false
flickerstreak@1 1129 end
flickerstreak@1 1130 for l,u in pairs(registry[distribution]) do
flickerstreak@1 1131 if u[prefix] and u[prefix][self] then
flickerstreak@1 1132 return true
flickerstreak@1 1133 end
flickerstreak@1 1134 end
flickerstreak@1 1135 return false
flickerstreak@1 1136 elseif distribution == "CUSTOM" then
flickerstreak@1 1137 customChannel = "AceComm" .. customChannel
flickerstreak@1 1138 return registry[distribution] and registry[distribution][customChannel] and registry[distribution][customChannel][prefix] and registry[distribution][customChannel][prefix][self] and true or false
flickerstreak@1 1139 end
flickerstreak@1 1140 return registry[distribution] and registry[distribution][prefix] and registry[distribution][prefix][self] and true or false
flickerstreak@1 1141 end
flickerstreak@1 1142
flickerstreak@1 1143 function AceComm:OnEmbedDisable(target)
flickerstreak@1 1144 self.UnregisterAllComms(target)
flickerstreak@1 1145 end
flickerstreak@1 1146
flickerstreak@1 1147 local id = byte_Z
flickerstreak@1 1148
flickerstreak@1 1149 local function encodedChar(x)
flickerstreak@1 1150 if x == 10 then
flickerstreak@1 1151 return "°\011"
flickerstreak@1 1152 elseif x == 0 then
flickerstreak@1 1153 return "\255"
flickerstreak@1 1154 elseif x == 255 then
flickerstreak@1 1155 return "°\254"
flickerstreak@1 1156 elseif x == 124 then
flickerstreak@1 1157 return "°\125"
flickerstreak@1 1158 elseif x == byte_s then
flickerstreak@1 1159 return "\015"
flickerstreak@1 1160 elseif x == byte_S then
flickerstreak@1 1161 return "\020"
flickerstreak@1 1162 elseif x == 15 then
flickerstreak@1 1163 return "°\016"
flickerstreak@1 1164 elseif x == 20 then
flickerstreak@1 1165 return "°\021"
flickerstreak@1 1166 elseif x == byte_deg then
flickerstreak@1 1167 return "°±"
flickerstreak@1 1168 elseif x == 37 then
flickerstreak@1 1169 return "°\038"
flickerstreak@1 1170 end
flickerstreak@1 1171 return string_char(x)
flickerstreak@1 1172 end
flickerstreak@1 1173
flickerstreak@1 1174 local function soberEncodedChar(x)
flickerstreak@1 1175 if x == 10 then
flickerstreak@1 1176 return "°\011"
flickerstreak@1 1177 elseif x == 0 then
flickerstreak@1 1178 return "\255"
flickerstreak@1 1179 elseif x == 255 then
flickerstreak@1 1180 return "°\254"
flickerstreak@1 1181 elseif x == 124 then
flickerstreak@1 1182 return "°\125"
flickerstreak@1 1183 elseif x == byte_deg then
flickerstreak@1 1184 return "°±"
flickerstreak@1 1185 elseif x == 37 then
flickerstreak@1 1186 return "°\038"
flickerstreak@1 1187 end
flickerstreak@1 1188 return string_char(x)
flickerstreak@1 1189 end
flickerstreak@1 1190
flickerstreak@1 1191 local function SendMessage(prefix, priority, distribution, person, message, textToHash)
flickerstreak@1 1192 if distribution == "CUSTOM" then
flickerstreak@1 1193 person = "AceComm" .. person
flickerstreak@1 1194 end
flickerstreak@1 1195 if not IsInDistribution(distribution, person) then
flickerstreak@1 1196 return false
flickerstreak@1 1197 end
flickerstreak@1 1198 if distribution == "GROUP" then
flickerstreak@1 1199 distribution = GetCurrentGroupDistribution()
flickerstreak@1 1200 if not distribution then
flickerstreak@1 1201 return false
flickerstreak@1 1202 end
flickerstreak@1 1203 end
flickerstreak@1 1204 if id == byte_Z then
flickerstreak@1 1205 id = byte_a
flickerstreak@1 1206 elseif id == byte_z then
flickerstreak@1 1207 id = byte_A
flickerstreak@1 1208 else
flickerstreak@1 1209 id = id + 1
flickerstreak@1 1210 end
flickerstreak@1 1211 if id == byte_s or id == byte_S then
flickerstreak@1 1212 id = id + 1
flickerstreak@1 1213 end
flickerstreak@1 1214 local id = string_char(id)
flickerstreak@1 1215 local drunk = distribution == "GLOBAL" or distribution == "WHISPER" or distribution == "ZONE" or distribution == "CUSTOM"
flickerstreak@1 1216 prefix = Encode(prefix, drunk)
flickerstreak@1 1217 message = Serialize(message, textToHash)
flickerstreak@1 1218 message = Encode(message, drunk)
flickerstreak@1 1219 local headerLen = string_len(prefix) + 6
flickerstreak@1 1220 local messageLen = string_len(message)
flickerstreak@1 1221 if distribution == "WHISPER" then
flickerstreak@1 1222 AceComm.recentWhispers[string.lower(person)] = GetTime()
flickerstreak@1 1223 end
flickerstreak@1 1224 local max = math_floor(messageLen / (250 - headerLen) + 1)
flickerstreak@1 1225 if max > 1 then
flickerstreak@1 1226 local segment = math_floor(messageLen / max + 0.5)
flickerstreak@1 1227 local last = 0
flickerstreak@1 1228 local good = true
flickerstreak@1 1229 for i = 1, max do
flickerstreak@1 1230 local bit
flickerstreak@1 1231 if i == max then
flickerstreak@1 1232 bit = string_sub(message, last + 1)
flickerstreak@1 1233 else
flickerstreak@1 1234 local next = segment * i
flickerstreak@1 1235 if string_byte(message, next) == byte_deg then
flickerstreak@1 1236 next = next + 1
flickerstreak@1 1237 end
flickerstreak@1 1238 bit = string_sub(message, last + 1, next)
flickerstreak@1 1239 last = next
flickerstreak@1 1240 end
flickerstreak@1 1241 if distribution == "WHISPER" then
flickerstreak@1 1242 bit = "/" .. prefix .. "\t" .. id .. encodedChar(i) .. encodedChar(max) .. "\t" .. bit .. "°"
flickerstreak@1 1243 ChatThrottleLib:SendChatMessage(priority, prefix, bit, "WHISPER", nil, person)
flickerstreak@1 1244 elseif distribution == "GLOBAL" or distribution == "ZONE" or distribution == "CUSTOM" then
flickerstreak@1 1245 bit = prefix .. "\t" .. id .. encodedChar(i) .. encodedChar(max) .. "\t" .. bit .. "°"
flickerstreak@1 1246 local channel
flickerstreak@1 1247 if distribution == "GLOBAL" then
flickerstreak@1 1248 channel = "AceComm"
flickerstreak@1 1249 elseif distribution == "ZONE" then
flickerstreak@1 1250 channel = GetCurrentZoneChannel()
flickerstreak@1 1251 elseif distribution == "CUSTOM" then
flickerstreak@1 1252 channel = person
flickerstreak@1 1253 end
flickerstreak@1 1254 local index = GetChannelName(channel)
flickerstreak@1 1255 if index and index > 0 then
flickerstreak@1 1256 ChatThrottleLib:SendChatMessage(priority, prefix, bit, "CHANNEL", nil, index)
flickerstreak@1 1257 else
flickerstreak@1 1258 good = false
flickerstreak@1 1259 end
flickerstreak@1 1260 else
flickerstreak@1 1261 bit = id .. soberEncodedChar(i) .. soberEncodedChar(max) .. "\t" .. bit
flickerstreak@1 1262 ChatThrottleLib:SendAddonMessage(priority, prefix, bit, distribution)
flickerstreak@1 1263 end
flickerstreak@1 1264 end
flickerstreak@1 1265 return good
flickerstreak@1 1266 else
flickerstreak@1 1267 if distribution == "WHISPER" then
flickerstreak@1 1268 message = "/" .. prefix .. "\t" .. id .. string_char(1) .. string_char(1) .. "\t" .. message .. "°"
flickerstreak@1 1269 ChatThrottleLib:SendChatMessage(priority, prefix, message, "WHISPER", nil, person)
flickerstreak@1 1270 return true
flickerstreak@1 1271 elseif distribution == "GLOBAL" or distribution == "ZONE" or distribution == "CUSTOM" then
flickerstreak@1 1272 message = prefix .. "\t" .. id .. string_char(1) .. string_char(1) .. "\t" .. message .. "°"
flickerstreak@1 1273 local channel
flickerstreak@1 1274 if distribution == "GLOBAL" then
flickerstreak@1 1275 channel = "AceComm"
flickerstreak@1 1276 elseif distribution == "ZONE" then
flickerstreak@1 1277 channel = GetCurrentZoneChannel()
flickerstreak@1 1278 elseif distribution == "CUSTOM" then
flickerstreak@1 1279 channel = person
flickerstreak@1 1280 end
flickerstreak@1 1281 local index = GetChannelName(channel)
flickerstreak@1 1282 if index and index > 0 then
flickerstreak@1 1283 ChatThrottleLib:SendChatMessage(priority, prefix, message, "CHANNEL", nil, index)
flickerstreak@1 1284 return true
flickerstreak@1 1285 end
flickerstreak@1 1286 else
flickerstreak@1 1287 message = id .. string_char(1) .. string_char(1) .. "\t" .. message
flickerstreak@1 1288 ChatThrottleLib:SendAddonMessage(priority, prefix, message, distribution)
flickerstreak@1 1289 return true
flickerstreak@1 1290 end
flickerstreak@1 1291 end
flickerstreak@1 1292 return false
flickerstreak@1 1293 end
flickerstreak@1 1294
flickerstreak@1 1295 local tmp = {}
flickerstreak@1 1296 function AceComm:SendPrioritizedCommMessage(priority, distribution, person, ...)
flickerstreak@1 1297 AceComm:argCheck(priority, 2, "string")
flickerstreak@1 1298 if priority ~= "NORMAL" and priority ~= "BULK" and priority ~= "ALERT" then
flickerstreak@1 1299 AceComm:error('Argument #2 to `SendPrioritizedCommMessage\' must be either "NORMAL", "BULK", or "ALERT"')
flickerstreak@1 1300 end
flickerstreak@1 1301 AceComm:argCheck(distribution, 3, "string")
flickerstreak@1 1302 local includePerson = true
flickerstreak@1 1303 if distribution == "WHISPER" or distribution == "CUSTOM" then
flickerstreak@1 1304 includePerson = false
flickerstreak@1 1305 AceComm:argCheck(person, 4, "string")
flickerstreak@1 1306 if string_len(person) == 0 then
flickerstreak@1 1307 AceComm:error("Argument #4 to `SendPrioritizedCommMessage' must be a non-zero-length string")
flickerstreak@1 1308 end
flickerstreak@1 1309 end
flickerstreak@1 1310 if self == AceComm then
flickerstreak@1 1311 AceComm:error("Cannot send a comm message from AceComm directly.")
flickerstreak@1 1312 end
flickerstreak@1 1313 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 1314 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 1315 end
flickerstreak@1 1316
flickerstreak@1 1317 local prefix = self.commPrefix
flickerstreak@1 1318 if type(prefix) ~= "string" then
flickerstreak@1 1319 AceComm:error("`SetCommPrefix' must be called before sending a message.")
flickerstreak@1 1320 end
flickerstreak@1 1321
flickerstreak@1 1322 local message
flickerstreak@1 1323
flickerstreak@1 1324 if includePerson and select('#', ...) == 0 and type(person) ~= "table" then
flickerstreak@1 1325 message = person
flickerstreak@1 1326 elseif not includePerson and select('#', ...) == 1 and type((...)) ~= "table" then
flickerstreak@1 1327 message = ...
flickerstreak@1 1328 else
flickerstreak@1 1329 message = tmp
flickerstreak@1 1330 local n = 1
flickerstreak@1 1331 if includePerson then
flickerstreak@1 1332 tmp[1] = person
flickerstreak@1 1333 n = 2
flickerstreak@1 1334 end
flickerstreak@1 1335 for i = 1, select('#', ...) do
flickerstreak@1 1336 tmp[n] = select(i, ...)
flickerstreak@1 1337 n = n + 1
flickerstreak@1 1338 end
flickerstreak@1 1339 end
flickerstreak@1 1340
flickerstreak@1 1341 local ret = SendMessage(AceComm.prefixTextToHash[prefix], priority, distribution, person, message, self.commMemoTextToHash)
flickerstreak@1 1342
flickerstreak@1 1343 if message == tmp then
flickerstreak@1 1344 local n = #tmp
flickerstreak@1 1345 for i = 1, n do
flickerstreak@1 1346 tmp[i] = nil
flickerstreak@1 1347 end
flickerstreak@1 1348 end
flickerstreak@1 1349
flickerstreak@1 1350 return ret
flickerstreak@1 1351 end
flickerstreak@1 1352
flickerstreak@1 1353 function AceComm:SendCommMessage(distribution, person, ...)
flickerstreak@1 1354 AceComm:argCheck(distribution, 2, "string")
flickerstreak@1 1355 local includePerson = true
flickerstreak@1 1356 if distribution == "WHISPER" or distribution == "CUSTOM" then
flickerstreak@1 1357 includePerson = false
flickerstreak@1 1358 AceComm:argCheck(person, 3, "string")
flickerstreak@1 1359 if string_len(person) == 0 then
flickerstreak@1 1360 AceComm:error("Argument #3 to `SendCommMessage' must be a non-zero-length string")
flickerstreak@1 1361 end
flickerstreak@1 1362 end
flickerstreak@1 1363 if self == AceComm then
flickerstreak@1 1364 AceComm:error("Cannot send a comm message from AceComm directly.")
flickerstreak@1 1365 end
flickerstreak@1 1366 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 1367 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 1368 end
flickerstreak@1 1369
flickerstreak@1 1370 local prefix = self.commPrefix
flickerstreak@1 1371 if type(prefix) ~= "string" then
flickerstreak@1 1372 AceComm:error("`SetCommPrefix' must be called before sending a message.")
flickerstreak@1 1373 end
flickerstreak@1 1374
flickerstreak@1 1375 if includePerson and select('#', ...) == 0 and type(person) ~= "table" then
flickerstreak@1 1376 message = person
flickerstreak@1 1377 elseif not includePerson and select('#', ...) == 1 and type((...)) ~= "table" then
flickerstreak@1 1378 message = ...
flickerstreak@1 1379 else
flickerstreak@1 1380 message = tmp
flickerstreak@1 1381 local n = 1
flickerstreak@1 1382 if includePerson then
flickerstreak@1 1383 tmp[1] = person
flickerstreak@1 1384 n = 2
flickerstreak@1 1385 end
flickerstreak@1 1386 for i = 1, select('#', ...) do
flickerstreak@1 1387 tmp[n] = select(i, ...)
flickerstreak@1 1388 n = n + 1
flickerstreak@1 1389 end
flickerstreak@1 1390 end
flickerstreak@1 1391
flickerstreak@1 1392 local priority = self.commPriority or "NORMAL"
flickerstreak@1 1393
flickerstreak@1 1394 local ret = SendMessage(AceComm.prefixTextToHash[prefix], priority, distribution, person, message, self.commMemoTextToHash)
flickerstreak@1 1395
flickerstreak@1 1396 if message == tmp then
flickerstreak@1 1397 local n = #tmp
flickerstreak@1 1398 for i = 1, n do
flickerstreak@1 1399 tmp[i] = nil
flickerstreak@1 1400 end
flickerstreak@1 1401 end
flickerstreak@1 1402
flickerstreak@1 1403 return ret
flickerstreak@1 1404 end
flickerstreak@1 1405
flickerstreak@1 1406 function AceComm:SetDefaultCommPriority(priority)
flickerstreak@1 1407 AceComm:argCheck(priority, 2, "string")
flickerstreak@1 1408 if priority ~= "NORMAL" and priority ~= "BULK" and priority ~= "ALERT" then
flickerstreak@1 1409 AceComm:error('Argument #2 must be either "NORMAL", "BULK", or "ALERT"')
flickerstreak@1 1410 end
flickerstreak@1 1411
flickerstreak@1 1412 if self.commPriority then
flickerstreak@1 1413 AceComm:error("Cannot call `SetDefaultCommPriority' more than once")
flickerstreak@1 1414 end
flickerstreak@1 1415
flickerstreak@1 1416 self.commPriority = priority
flickerstreak@1 1417 end
flickerstreak@1 1418
flickerstreak@1 1419 function AceComm:SetCommPrefix(prefix)
flickerstreak@1 1420 AceComm:argCheck(prefix, 2, "string")
flickerstreak@1 1421
flickerstreak@1 1422 if self.commPrefix then
flickerstreak@1 1423 AceComm:error("Cannot call `SetCommPrefix' more than once.")
flickerstreak@1 1424 end
flickerstreak@1 1425
flickerstreak@1 1426 if AceComm.prefixes[prefix] then
flickerstreak@1 1427 AceComm:error("Cannot set prefix to %q, it is already in use.", prefix)
flickerstreak@1 1428 end
flickerstreak@1 1429
flickerstreak@1 1430 local hash = TailoredBinaryCheckSum(prefix)
flickerstreak@1 1431 if AceComm.prefixHashToText[hash] then
flickerstreak@1 1432 AceComm:error("Cannot set prefix to %q, its hash is used by another prefix: %q", prefix, AceComm.prefixHashToText[hash])
flickerstreak@1 1433 end
flickerstreak@1 1434
flickerstreak@1 1435 AceComm.prefixes[prefix] = true
flickerstreak@1 1436 self.commPrefix = prefix
flickerstreak@1 1437 AceComm.prefixHashToText[hash] = prefix
flickerstreak@1 1438 AceComm.prefixTextToHash[prefix] = hash
flickerstreak@1 1439 end
flickerstreak@1 1440
flickerstreak@1 1441 function AceComm:RegisterMemoizations(values)
flickerstreak@1 1442 AceComm:argCheck(values, 2, "table")
flickerstreak@1 1443 for k,v in pairs(values) do
flickerstreak@1 1444 if type(k) ~= "number" then
flickerstreak@1 1445 AceComm:error("Bad argument #2 to `RegisterMemoizations'. All keys must be numbers")
flickerstreak@1 1446 elseif type(v) ~= "string" then
flickerstreak@1 1447 AceComm:error("Bad argument #2 to `RegisterMemoizations'. All values must be strings")
flickerstreak@1 1448 end
flickerstreak@1 1449 end
flickerstreak@1 1450 if self.commMemoHashToText or self.commMemoTextToHash then
flickerstreak@1 1451 AceComm:error("You can only call `RegisterMemoizations' once.")
flickerstreak@1 1452 elseif not self.commPrefix then
flickerstreak@1 1453 AceComm:error("You can only call `RegisterCommPrefix' before calling `RegisterMemoizations'.")
flickerstreak@1 1454 elseif AceComm.prefixMemoizations[self.commPrefix] then
flickerstreak@1 1455 AceComm:error("Another addon with prefix %q has already registered memoizations.", self.commPrefix)
flickerstreak@1 1456 end
flickerstreak@1 1457 local hashToText = {}
flickerstreak@1 1458 local textToHash = {}
flickerstreak@1 1459 for _,text in ipairs(values) do
flickerstreak@1 1460 local hash = TailoredNumericCheckSum(text)
flickerstreak@1 1461 if hashToText[hash] then
flickerstreak@1 1462 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 1463 else
flickerstreak@1 1464 textToHash[text] = hash
flickerstreak@1 1465 hashToText[hash] = text
flickerstreak@1 1466 end
flickerstreak@1 1467 end
flickerstreak@1 1468 values = nil
flickerstreak@1 1469 self.commMemoHashToText = hashToText
flickerstreak@1 1470 self.commMemoTextToHash = textToHash
flickerstreak@1 1471 AceComm.prefixMemoizations[self.commPrefix] = hashToText
flickerstreak@1 1472 end
flickerstreak@1 1473
flickerstreak@1 1474 local lastCheck = GetTime()
flickerstreak@1 1475 local function CheckRefix()
flickerstreak@1 1476 if GetTime() - lastCheck >= 120 then
flickerstreak@1 1477 lastCheck = GetTime()
flickerstreak@1 1478 RefixAceCommChannelsAndEvents()
flickerstreak@1 1479 end
flickerstreak@1 1480 end
flickerstreak@1 1481
flickerstreak@1 1482 local stack = setmetatable({}, {__mode='k'})
flickerstreak@1 1483 local function HandleMessage(prefix, message, distribution, sender, customChannel)
flickerstreak@1 1484 local isGroup = GetCurrentGroupDistribution() == distribution
flickerstreak@1 1485 local isCustom = distribution == "CUSTOM"
flickerstreak@1 1486 if (not AceComm_registry[distribution] and (not isGroup or not AceComm_registry.GROUP)) or (isCustom and not AceComm_registry.CUSTOM[customChannel]) then
flickerstreak@1 1487 return CheckRefix()
flickerstreak@1 1488 end
flickerstreak@1 1489 local _, id, current, max
flickerstreak@1 1490 if not message then
flickerstreak@1 1491 if distribution == "WHISPER" then
flickerstreak@1 1492 _,_, prefix, id, current, max, message = string_find(prefix, "^/(...)\t(.)(.)(.)\t(.*)$")
flickerstreak@1 1493 else
flickerstreak@1 1494 _,_, prefix, id, current, max, message = string_find(prefix, "^(...)\t(.)(.)(.)\t(.*)$")
flickerstreak@1 1495 end
flickerstreak@1 1496 prefix = AceComm.prefixHashToText[prefix]
flickerstreak@1 1497 if not prefix then
flickerstreak@1 1498 return CheckRefix()
flickerstreak@1 1499 end
flickerstreak@1 1500 if isCustom then
flickerstreak@1 1501 if not AceComm_registry.CUSTOM[customChannel][prefix] then
flickerstreak@1 1502 return CheckRefix()
flickerstreak@1 1503 end
flickerstreak@1 1504 else
flickerstreak@1 1505 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 1506 return CheckRefix()
flickerstreak@1 1507 end
flickerstreak@1 1508 end
flickerstreak@1 1509 else
flickerstreak@1 1510 _,_, id, current, max, message = string_find(message, "^(.)(.)(.)\t(.*)$")
flickerstreak@1 1511 end
flickerstreak@1 1512 if not message then
flickerstreak@1 1513 return
flickerstreak@1 1514 end
flickerstreak@1 1515 local smallCustomChannel = customChannel and string_sub(customChannel, 8)
flickerstreak@1 1516 current = string_byte(current)
flickerstreak@1 1517 max = string_byte(max)
flickerstreak@1 1518 if max > 1 then
flickerstreak@1 1519 local queue = AceComm.recvQueue
flickerstreak@1 1520 local x
flickerstreak@1 1521 if distribution == "CUSTOM" then
flickerstreak@1 1522 x = prefix .. ":" .. sender .. distribution .. customChannel .. id
flickerstreak@1 1523 else
flickerstreak@1 1524 x = prefix .. ":" .. sender .. distribution .. id
flickerstreak@1 1525 end
flickerstreak@1 1526 if not queue[x] then
flickerstreak@1 1527 if current ~= 1 then
flickerstreak@1 1528 return
flickerstreak@1 1529 end
flickerstreak@1 1530 local t = next(stack) or {}
flickerstreak@1 1531 stack[t] = nil
flickerstreak@1 1532 queue[x] = t
flickerstreak@1 1533 end
flickerstreak@1 1534 local chunk = queue[x]
flickerstreak@1 1535 chunk.time = GetTime()
flickerstreak@1 1536 chunk[current] = message
flickerstreak@1 1537 if current == max then
flickerstreak@1 1538 message = table_concat(chunk)
flickerstreak@1 1539 local t = queue[x]
flickerstreak@1 1540 queue[x] = nil
flickerstreak@1 1541 for k in pairs(t) do
flickerstreak@1 1542 t[k] = nil
flickerstreak@1 1543 end
flickerstreak@1 1544 stack[t] = true
flickerstreak@1 1545 else
flickerstreak@1 1546 return
flickerstreak@1 1547 end
flickerstreak@1 1548 end
flickerstreak@1 1549 message = Deserialize(message, AceComm.prefixMemoizations[prefix])
flickerstreak@1 1550 local isTable = type(message) == "table"
flickerstreak@1 1551 local n
flickerstreak@1 1552 if isTable then
flickerstreak@1 1553 n = #message * 4
flickerstreak@1 1554 if n < 40 then
flickerstreak@1 1555 n = 40
flickerstreak@1 1556 end
flickerstreak@1 1557 while message[n] == nil do
flickerstreak@1 1558 n = n - 1
flickerstreak@1 1559 end
flickerstreak@1 1560 end
flickerstreak@1 1561 if AceComm_registry[distribution] then
flickerstreak@1 1562 if isTable then
flickerstreak@1 1563 if isCustom then
flickerstreak@1 1564 if AceComm_registry.CUSTOM[customChannel][prefix] then
flickerstreak@1 1565 for k,v in pairs(AceComm_registry.CUSTOM[customChannel][prefix]) do
flickerstreak@1 1566 local type_v = type(v)
flickerstreak@1 1567 if type_v == "string" then
flickerstreak@1 1568 local f = k[v]
flickerstreak@1 1569 if type(f) == "table" then
flickerstreak@1 1570 local i = 1
flickerstreak@1 1571 local g = f[message[i]]
flickerstreak@1 1572 while g do
flickerstreak@1 1573 if type(g) ~= "table" then -- function
flickerstreak@1 1574 g(k, prefix, sender, distribution, smallCustomChannel, unpack(message, i+1, n))
flickerstreak@1 1575 break
flickerstreak@1 1576 else
flickerstreak@1 1577 i = i + 1
flickerstreak@1 1578 g = g[message[i]]
flickerstreak@1 1579 end
flickerstreak@1 1580 end
flickerstreak@1 1581 else -- function
flickerstreak@1 1582 f(k, prefix, sender, distribution, smallCustomChannel, unpack(message, 1, n))
flickerstreak@1 1583 end
flickerstreak@1 1584 elseif type_v == "table" then
flickerstreak@1 1585 local i = 1
flickerstreak@1 1586 local g = v[message[i]]
flickerstreak@1 1587 while g do
flickerstreak@1 1588 if type(g) ~= "table" then -- function
flickerstreak@1 1589 g(prefix, sender, distribution, smallCustomChannel, unpack(message, i+1, n))
flickerstreak@1 1590 break
flickerstreak@1 1591 else
flickerstreak@1 1592 i = i + 1
flickerstreak@1 1593 g = g[message[i]]
flickerstreak@1 1594 end
flickerstreak@1 1595 end
flickerstreak@1 1596 else -- function
flickerstreak@1 1597 v(prefix, sender, distribution, smallCustomChannel, unpack(message, 1, n))
flickerstreak@1 1598 end
flickerstreak@1 1599 end
flickerstreak@1 1600 end
flickerstreak@1 1601 else
flickerstreak@1 1602 if AceComm_registry[distribution][prefix] then
flickerstreak@1 1603 for k,v in pairs(AceComm_registry[distribution][prefix]) do
flickerstreak@1 1604 local type_v = type(v)
flickerstreak@1 1605 if type_v == "string" then
flickerstreak@1 1606 local f = k[v]
flickerstreak@1 1607 if type(f) == "table" then
flickerstreak@1 1608 local i = 1
flickerstreak@1 1609 local g = f[message[i]]
flickerstreak@1 1610 while g do
flickerstreak@1 1611 if type(g) ~= "table" then -- function
flickerstreak@1 1612 g(k, prefix, sender, distribution, unpack(message, i+1, n))
flickerstreak@1 1613 break
flickerstreak@1 1614 else
flickerstreak@1 1615 i = i + 1
flickerstreak@1 1616 g = g[message[i]]
flickerstreak@1 1617 end
flickerstreak@1 1618 end
flickerstreak@1 1619 else -- function
flickerstreak@1 1620 f(k, prefix, sender, distribution, unpack(message, 1, n))
flickerstreak@1 1621 end
flickerstreak@1 1622 elseif type_v == "table" then
flickerstreak@1 1623 local i = 1
flickerstreak@1 1624 local g = v[message[i]]
flickerstreak@1 1625 while g do
flickerstreak@1 1626 if type(g) ~= "table" then -- function
flickerstreak@1 1627 g(prefix, sender, distribution, unpack(message, i+1, n))
flickerstreak@1 1628 break
flickerstreak@1 1629 else
flickerstreak@1 1630 i = i + 1
flickerstreak@1 1631 g = g[message[i]]
flickerstreak@1 1632 end
flickerstreak@1 1633 end
flickerstreak@1 1634 else -- function
flickerstreak@1 1635 v(prefix, sender, distribution, unpack(message, 1, n))
flickerstreak@1 1636 end
flickerstreak@1 1637 end
flickerstreak@1 1638 end
flickerstreak@1 1639 end
flickerstreak@1 1640 else
flickerstreak@1 1641 if isCustom then
flickerstreak@1 1642 if AceComm_registry.CUSTOM[customChannel][prefix] then
flickerstreak@1 1643 for k,v in pairs(AceComm_registry.CUSTOM[customChannel][prefix]) do
flickerstreak@1 1644 local type_v = type(v)
flickerstreak@1 1645 if type_v == "string" then
flickerstreak@1 1646 local f = k[v]
flickerstreak@1 1647 if type(f) == "table" then
flickerstreak@1 1648 local g = f[message]
flickerstreak@1 1649 if g and type(g) == "function" then
flickerstreak@1 1650 g(k, prefix, sender, distribution, smallCustomChannel)
flickerstreak@1 1651 end
flickerstreak@1 1652 else -- function
flickerstreak@1 1653 f(k, prefix, sender, distribution, smallCustomChannel, message)
flickerstreak@1 1654 end
flickerstreak@1 1655 elseif type_v == "table" then
flickerstreak@1 1656 local g = v[message]
flickerstreak@1 1657 if g and type(g) == "function" then
flickerstreak@1 1658 g(k, prefix, sender, distribution, smallCustomChannel)
flickerstreak@1 1659 end
flickerstreak@1 1660 else -- function
flickerstreak@1 1661 v(prefix, sender, distribution, smallCustomChannel, message)
flickerstreak@1 1662 end
flickerstreak@1 1663 end
flickerstreak@1 1664 end
flickerstreak@1 1665 else
flickerstreak@1 1666 if AceComm_registry[distribution][prefix] then
flickerstreak@1 1667 for k,v in pairs(AceComm_registry[distribution][prefix]) do
flickerstreak@1 1668 local type_v = type(v)
flickerstreak@1 1669 if type_v == "string" then
flickerstreak@1 1670 local f = k[v]
flickerstreak@1 1671 if type(f) == "table" then
flickerstreak@1 1672 local g = f[message]
flickerstreak@1 1673 if g and type(g) == "function" then
flickerstreak@1 1674 g(k, prefix, sender, distribution)
flickerstreak@1 1675 end
flickerstreak@1 1676 else -- function
flickerstreak@1 1677 f(k, prefix, sender, distribution, message)
flickerstreak@1 1678 end
flickerstreak@1 1679 elseif type_v == "table" then
flickerstreak@1 1680 local g = v[message]
flickerstreak@1 1681 if g and type(g) == "function" then
flickerstreak@1 1682 g(k, prefix, sender, distribution)
flickerstreak@1 1683 end
flickerstreak@1 1684 else -- function
flickerstreak@1 1685 v(prefix, sender, distribution, message)
flickerstreak@1 1686 end
flickerstreak@1 1687 end
flickerstreak@1 1688 end
flickerstreak@1 1689 end
flickerstreak@1 1690 end
flickerstreak@1 1691 end
flickerstreak@1 1692 if isGroup and AceComm_registry.GROUP and AceComm_registry.GROUP[prefix] then
flickerstreak@1 1693 if isTable then
flickerstreak@1 1694 for k,v in pairs(AceComm_registry.GROUP[prefix]) do
flickerstreak@1 1695 local type_v = type(v)
flickerstreak@1 1696 if type_v == "string" then
flickerstreak@1 1697 local f = k[v]
flickerstreak@1 1698 if type(f) == "table" then
flickerstreak@1 1699 local i = 1
flickerstreak@1 1700 local g = f[message[i]]
flickerstreak@1 1701 while g do
flickerstreak@1 1702 if type(g) ~= "table" then -- function
flickerstreak@1 1703 g(k, prefix, sender, "GROUP", unpack(message, i+1, n))
flickerstreak@1 1704 break
flickerstreak@1 1705 else
flickerstreak@1 1706 i = i + 1
flickerstreak@1 1707 g = g[message[i]]
flickerstreak@1 1708 end
flickerstreak@1 1709 end
flickerstreak@1 1710 else -- function
flickerstreak@1 1711 f(k, prefix, sender, "GROUP", unpack(message, 1, n))
flickerstreak@1 1712 end
flickerstreak@1 1713 elseif type_v == "table" then
flickerstreak@1 1714 local i = 1
flickerstreak@1 1715 local g = v[message[i]]
flickerstreak@1 1716 while g do
flickerstreak@1 1717 if type(g) ~= "table" then -- function
flickerstreak@1 1718 g(prefix, sender, "GROUP", unpack(message, i+1, n))
flickerstreak@1 1719 break
flickerstreak@1 1720 else
flickerstreak@1 1721 i = i + 1
flickerstreak@1 1722 g = g[message[i]]
flickerstreak@1 1723 end
flickerstreak@1 1724 end
flickerstreak@1 1725 else -- function
flickerstreak@1 1726 v(prefix, sender, "GROUP", unpack(message, 1, n))
flickerstreak@1 1727 end
flickerstreak@1 1728 end
flickerstreak@1 1729 else
flickerstreak@1 1730 for k,v in pairs(AceComm_registry.GROUP[prefix]) do
flickerstreak@1 1731 local type_v = type(v)
flickerstreak@1 1732 if type_v == "string" then
flickerstreak@1 1733 local f = k[v]
flickerstreak@1 1734 if type(f) == "table" then
flickerstreak@1 1735 local g = f[message]
flickerstreak@1 1736 if g and type(g) == "function" then
flickerstreak@1 1737 g(k, prefix, sender, "GROUP")
flickerstreak@1 1738 end
flickerstreak@1 1739 else -- function
flickerstreak@1 1740 f(k, prefix, sender, "GROUP", message)
flickerstreak@1 1741 end
flickerstreak@1 1742 elseif type_v == "table" then
flickerstreak@1 1743 local g = v[message]
flickerstreak@1 1744 if g and type(g) == "function" then
flickerstreak@1 1745 g(k, prefix, sender, "GROUP")
flickerstreak@1 1746 end
flickerstreak@1 1747 else -- function
flickerstreak@1 1748 v(prefix, sender, "GROUP", message)
flickerstreak@1 1749 end
flickerstreak@1 1750 end
flickerstreak@1 1751 end
flickerstreak@1 1752 end
flickerstreak@1 1753 end
flickerstreak@1 1754
flickerstreak@1 1755 function AceComm:CHAT_MSG_ADDON(prefix, message, distribution, sender)
flickerstreak@1 1756 if sender == player then
flickerstreak@1 1757 return
flickerstreak@1 1758 end
flickerstreak@1 1759 prefix = self.prefixHashToText[prefix]
flickerstreak@1 1760 if not prefix then
flickerstreak@1 1761 return CheckRefix()
flickerstreak@1 1762 end
flickerstreak@1 1763 local isGroup = GetCurrentGroupDistribution() == distribution
flickerstreak@1 1764 if not AceComm_registry[distribution] and (not isGroup or not AceComm_registry.GROUP) then
flickerstreak@1 1765 return CheckRefix()
flickerstreak@1 1766 end
flickerstreak@1 1767 prefix = Decode(prefix)
flickerstreak@1 1768 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 1769 return CheckRefix()
flickerstreak@1 1770 end
flickerstreak@1 1771 message = Decode(message)
flickerstreak@1 1772 return HandleMessage(prefix, message, distribution, sender)
flickerstreak@1 1773 end
flickerstreak@1 1774
flickerstreak@1 1775 function AceComm:CHAT_MSG_WHISPER(text, sender)
flickerstreak@1 1776 if not string_find(text, "^/") then
flickerstreak@1 1777 return
flickerstreak@1 1778 end
flickerstreak@1 1779 text = Decode(text, true)
flickerstreak@1 1780 return HandleMessage(text, nil, "WHISPER", sender)
flickerstreak@1 1781 end
flickerstreak@1 1782
flickerstreak@1 1783 function AceComm:CHAT_MSG_CHANNEL(text, sender, _, _, _, _, _, _, channel)
flickerstreak@1 1784 if sender == player or not string_find(channel, "^AceComm") then
flickerstreak@1 1785 return
flickerstreak@1 1786 end
flickerstreak@1 1787 text = Decode(text, true)
flickerstreak@1 1788 local distribution
flickerstreak@1 1789 local customChannel
flickerstreak@1 1790 if channel == "AceComm" then
flickerstreak@1 1791 distribution = "GLOBAL"
flickerstreak@1 1792 elseif channel == GetCurrentZoneChannel() then
flickerstreak@1 1793 distribution = "ZONE"
flickerstreak@1 1794 else
flickerstreak@1 1795 distribution = "CUSTOM"
flickerstreak@1 1796 customChannel = channel
flickerstreak@1 1797 end
flickerstreak@1 1798 return HandleMessage(text, nil, distribution, sender, customChannel)
flickerstreak@1 1799 end
flickerstreak@1 1800
flickerstreak@1 1801 function AceComm:IsUserInChannel(userName, distribution, customChannel)
flickerstreak@1 1802 AceComm:argCheck(userName, 2, "string", "nil")
flickerstreak@1 1803 if not userName then
flickerstreak@1 1804 userName = player
flickerstreak@1 1805 end
flickerstreak@1 1806 AceComm:argCheck(distribution, 3, "string")
flickerstreak@1 1807 local channel
flickerstreak@1 1808 if distribution == "GLOBAL" then
flickerstreak@1 1809 channel = "AceComm"
flickerstreak@1 1810 elseif distribution == "ZONE" then
flickerstreak@1 1811 channel = GetCurrentZoneChannel()
flickerstreak@1 1812 elseif distribution == "CUSTOM" then
flickerstreak@1 1813 AceComm:argCheck(customChannel, 4, "string")
flickerstreak@1 1814 channel = "AceComm" .. customChannel
flickerstreak@1 1815 else
flickerstreak@1 1816 AceComm:error('Argument #3 to `IsUserInChannel\' must be "GLOBAL", "CUSTOM", or "ZONE"')
flickerstreak@1 1817 end
flickerstreak@1 1818
flickerstreak@1 1819 return AceComm.userRegistry[channel] and AceComm.userRegistry[channel][userName] or false
flickerstreak@1 1820 end
flickerstreak@1 1821
flickerstreak@1 1822 function AceComm:CHAT_MSG_CHANNEL_LIST(text, _, _, _, _, _, _, _, channel)
flickerstreak@1 1823 if not string_find(channel, "^AceComm") then
flickerstreak@1 1824 return
flickerstreak@1 1825 end
flickerstreak@1 1826
flickerstreak@1 1827 if not AceComm.userRegistry[channel] then
flickerstreak@1 1828 AceComm.userRegistry[channel] = {}
flickerstreak@1 1829 end
flickerstreak@1 1830 local t = AceComm.userRegistry[channel]
flickerstreak@1 1831 for k in string_gmatch(text, "[^, @%*#]+") do
flickerstreak@1 1832 t[k] = true
flickerstreak@1 1833 end
flickerstreak@1 1834 end
flickerstreak@1 1835
flickerstreak@1 1836 function AceComm:CHAT_MSG_CHANNEL_JOIN(_, user, _, _, _, _, _, _, channel)
flickerstreak@1 1837 if not string_find(channel, "^AceComm") then
flickerstreak@1 1838 return
flickerstreak@1 1839 end
flickerstreak@1 1840
flickerstreak@1 1841 if not AceComm.userRegistry[channel] then
flickerstreak@1 1842 AceComm.userRegistry[channel] = {}
flickerstreak@1 1843 end
flickerstreak@1 1844 local t = AceComm.userRegistry[channel]
flickerstreak@1 1845 t[user] = true
flickerstreak@1 1846 end
flickerstreak@1 1847
flickerstreak@1 1848 function AceComm:CHAT_MSG_CHANNEL_LEAVE(_, user, _, _, _, _, _, _, channel)
flickerstreak@1 1849 if not string_find(channel, "^AceComm") then
flickerstreak@1 1850 return
flickerstreak@1 1851 end
flickerstreak@1 1852
flickerstreak@1 1853 if not AceComm.userRegistry[channel] then
flickerstreak@1 1854 AceComm.userRegistry[channel] = {}
flickerstreak@1 1855 end
flickerstreak@1 1856 local t = AceComm.userRegistry[channel]
flickerstreak@1 1857 if t[user] then
flickerstreak@1 1858 t[user] = nil
flickerstreak@1 1859 end
flickerstreak@1 1860 end
flickerstreak@1 1861
flickerstreak@1 1862 function AceComm:AceEvent_FullyInitialized()
flickerstreak@1 1863 RefixAceCommChannelsAndEvents()
flickerstreak@1 1864 end
flickerstreak@1 1865
flickerstreak@1 1866 function AceComm:PLAYER_LOGOUT()
flickerstreak@1 1867 LeaveAceCommChannels(true)
flickerstreak@1 1868 end
flickerstreak@1 1869
flickerstreak@1 1870 function AceComm:ZONE_CHANGED_NEW_AREA()
flickerstreak@1 1871 local lastZone = zoneCache
flickerstreak@1 1872 zoneCache = nil
flickerstreak@1 1873 local newZone = GetCurrentZoneChannel()
flickerstreak@1 1874 if self.registry.ZONE and next(self.registry.ZONE) then
flickerstreak@1 1875 if lastZone then
flickerstreak@1 1876 SwitchChannel(lastZone, newZone)
flickerstreak@1 1877 else
flickerstreak@1 1878 JoinChannel(newZone)
flickerstreak@1 1879 end
flickerstreak@1 1880 end
flickerstreak@1 1881 end
flickerstreak@1 1882
flickerstreak@1 1883 function AceComm:embed(target)
flickerstreak@1 1884 self.super.embed(self, target)
flickerstreak@1 1885 if not AceEvent then
flickerstreak@1 1886 AceComm:error(MAJOR_VERSION .. " requires AceEvent-2.0")
flickerstreak@1 1887 end
flickerstreak@1 1888 end
flickerstreak@1 1889
flickerstreak@1 1890 local recentNotSeen = {}
flickerstreak@1 1891 local notSeenString = '^' .. string_gsub(string_gsub(ERR_CHAT_PLAYER_NOT_FOUND_S, "%%s", "(.-)"), "%%1%$s", "(.-)") .. '$'
flickerstreak@1 1892 local ambiguousString = '^' .. string_gsub(string_gsub(ERR_CHAT_PLAYER_AMBIGUOUS_S, "%%s", "(.-)"), "%%1%$s", "(.-)") .. '$'
flickerstreak@1 1893 function AceComm.hooks:ChatFrame_MessageEventHandler(orig, event)
flickerstreak@1 1894 if event == "CHAT_MSG_WHISPER" or event == "CHAT_MSG_WHISPER_INFORM" then
flickerstreak@1 1895 if string_find(arg1, "^/") then
flickerstreak@1 1896 return
flickerstreak@1 1897 end
flickerstreak@1 1898 elseif event == "CHAT_MSG_AFK" or event == "CHAT_MSG_DND" then
flickerstreak@1 1899 local t = self.recentWhispers[string.lower(arg2)]
flickerstreak@1 1900 if t and GetTime() - t <= 15 then
flickerstreak@1 1901 return
flickerstreak@1 1902 end
flickerstreak@1 1903 elseif event == "CHAT_MSG_CHANNEL" or event == "CHAT_MSG_CHANNEL_LIST" then
flickerstreak@1 1904 if string_find(arg9, "^AceComm") then
flickerstreak@1 1905 return
flickerstreak@1 1906 end
flickerstreak@1 1907 elseif event == "CHAT_MSG_SYSTEM" then
flickerstreak@1 1908 local _,_,player = string_find(arg1, notSeenString)
flickerstreak@1 1909 if not player then
flickerstreak@1 1910 _,_,player = string_find(arg1, ambiguousString)
flickerstreak@1 1911 end
flickerstreak@1 1912 if player then
flickerstreak@1 1913 local t = GetTime()
flickerstreak@1 1914 if recentNotSeen[player] and recentNotSeen[player] > t then
flickerstreak@1 1915 recentNotSeen[player] = t + 10
flickerstreak@1 1916 return
flickerstreak@1 1917 else
flickerstreak@1 1918 recentNotSeen[player] = t + 10
flickerstreak@1 1919 end
flickerstreak@1 1920 end
flickerstreak@1 1921 end
flickerstreak@1 1922 return orig(event)
flickerstreak@1 1923 end
flickerstreak@1 1924
flickerstreak@1 1925 local id, loggingOut
flickerstreak@1 1926 function AceComm.hooks:Logout(orig)
flickerstreak@1 1927 if IsResting() then
flickerstreak@1 1928 LeaveAceCommChannels(true)
flickerstreak@1 1929 else
flickerstreak@1 1930 id = self:ScheduleEvent(LeaveAceCommChannels, 15, true)
flickerstreak@1 1931 end
flickerstreak@1 1932 loggingOut = true
flickerstreak@1 1933 return orig()
flickerstreak@1 1934 end
flickerstreak@1 1935
flickerstreak@1 1936 function AceComm.hooks:CancelLogout(orig)
flickerstreak@1 1937 shutdown = false
flickerstreak@1 1938 if id then
flickerstreak@1 1939 self:CancelScheduledEvent(id)
flickerstreak@1 1940 id = nil
flickerstreak@1 1941 end
flickerstreak@1 1942 RefixAceCommChannelsAndEvents()
flickerstreak@1 1943 loggingOut = false
flickerstreak@1 1944 return orig()
flickerstreak@1 1945 end
flickerstreak@1 1946
flickerstreak@1 1947 function AceComm.hooks:Quit(orig)
flickerstreak@1 1948 if IsResting() then
flickerstreak@1 1949 LeaveAceCommChannels(true)
flickerstreak@1 1950 else
flickerstreak@1 1951 id = self:ScheduleEvent(LeaveAceCommChannels, 15, true)
flickerstreak@1 1952 end
flickerstreak@1 1953 loggingOut = true
flickerstreak@1 1954 return orig()
flickerstreak@1 1955 end
flickerstreak@1 1956
flickerstreak@1 1957 function AceComm.hooks:FCFDropDown_LoadChannels(orig, ...)
flickerstreak@1 1958 local arg = { ... }
flickerstreak@1 1959 for i = 1, #arg, 2 do
flickerstreak@1 1960 if not arg[i] then
flickerstreak@1 1961 break
flickerstreak@1 1962 end
flickerstreak@1 1963 if type(arg[i + 1]) == "string" and string_find(arg[i + 1], "^AceComm") then
flickerstreak@1 1964 table.remove(arg, i + 1)
flickerstreak@1 1965 table.remove(arg, i)
flickerstreak@1 1966 i = i - 2
flickerstreak@1 1967 end
flickerstreak@1 1968 end
flickerstreak@1 1969 return orig(unpack(arg))
flickerstreak@1 1970 end
flickerstreak@1 1971
flickerstreak@1 1972 function AceComm:CHAT_MSG_SYSTEM(text)
flickerstreak@1 1973 if text ~= ERR_TOO_MANY_CHAT_CHANNELS then
flickerstreak@1 1974 return
flickerstreak@1 1975 end
flickerstreak@1 1976
flickerstreak@1 1977 local chan = lastChannelJoined
flickerstreak@1 1978 if not chan then
flickerstreak@1 1979 return
flickerstreak@1 1980 end
flickerstreak@1 1981 if not string_find(lastChannelJoined, "^AceComm") then
flickerstreak@1 1982 return
flickerstreak@1 1983 end
flickerstreak@1 1984
flickerstreak@1 1985 local text
flickerstreak@1 1986 if chan == "AceComm" then
flickerstreak@1 1987 local addon = self.registry.GLOBAL and next(AceComm_registry.GLOBAL)
flickerstreak@1 1988 if not addon then
flickerstreak@1 1989 return
flickerstreak@1 1990 end
flickerstreak@1 1991 addon = tostring(addon)
flickerstreak@1 1992 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 1993 elseif chan == GetCurrentZoneChannel() then
flickerstreak@1 1994 local addon = AceComm_registry.ZONE and next(AceComm_registry.ZONE)
flickerstreak@1 1995 if not addon then
flickerstreak@1 1996 return
flickerstreak@1 1997 end
flickerstreak@1 1998 addon = tostring(addon)
flickerstreak@1 1999 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 2000 else
flickerstreak@1 2001 local addon = AceComm_registry.CUSTOM and AceComm_registry.CUSTOM[chan] and next(AceComm_registry.CUSTOM[chan])
flickerstreak@1 2002 if not addon then
flickerstreak@1 2003 return
flickerstreak@1 2004 end
flickerstreak@1 2005 addon = tostring(addon)
flickerstreak@1 2006 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 2007 end
flickerstreak@1 2008
flickerstreak@1 2009 StaticPopupDialogs["ACECOMM_TOO_MANY_CHANNELS"] = {
flickerstreak@1 2010 text = text,
flickerstreak@1 2011 button1 = CLOSE,
flickerstreak@1 2012 timeout = 0,
flickerstreak@1 2013 whileDead = 1,
flickerstreak@1 2014 hideOnEscape = 1,
flickerstreak@1 2015 }
flickerstreak@1 2016 StaticPopup_Show("ACECOMM_TOO_MANY_CHANNELS")
flickerstreak@1 2017 end
flickerstreak@1 2018
flickerstreak@1 2019 local function activate(self, oldLib, oldDeactivate)
flickerstreak@1 2020 AceComm = self
flickerstreak@1 2021
flickerstreak@1 2022 if oldLib then
flickerstreak@1 2023 self.frame = oldLib.frame
flickerstreak@1 2024 self.frame:UnregisterAllEvents()
flickerstreak@1 2025 self.recvQueue = oldLib.recvQueue
flickerstreak@1 2026 self.registry = oldLib.registry
flickerstreak@1 2027 self.channels = oldLib.channels
flickerstreak@1 2028 self.prefixes = oldLib.prefixes
flickerstreak@1 2029 self.classes = oldLib.classes
flickerstreak@1 2030 self.prefixMemoizations = oldLib.prefixMemoizations
flickerstreak@1 2031 self.prefixHashToText = oldLib.prefixHashToText
flickerstreak@1 2032 self.prefixTextToHash = oldLib.prefixTextToHash
flickerstreak@1 2033 self.recentWhispers = oldLib.recentWhispers
flickerstreak@1 2034 self.userRegistry = oldLib.userRegistry
flickerstreak@1 2035 else
flickerstreak@1 2036 local old_ChatFrame_MessageEventHandler = ChatFrame_MessageEventHandler
flickerstreak@1 2037 function ChatFrame_MessageEventHandler(event)
flickerstreak@1 2038 if self.hooks.ChatFrame_MessageEventHandler then
flickerstreak@1 2039 return self.hooks.ChatFrame_MessageEventHandler(self, old_ChatFrame_MessageEventHandler, event)
flickerstreak@1 2040 else
flickerstreak@1 2041 return old_ChatFrame_MessageEventHandler(event)
flickerstreak@1 2042 end
flickerstreak@1 2043 end
flickerstreak@1 2044 local id
flickerstreak@1 2045 local loggingOut = false
flickerstreak@1 2046 local old_Logout = Logout
flickerstreak@1 2047 function Logout()
flickerstreak@1 2048 if self.hooks.Logout then
flickerstreak@1 2049 return self.hooks.Logout(self, old_Logout)
flickerstreak@1 2050 else
flickerstreak@1 2051 return old_Logout()
flickerstreak@1 2052 end
flickerstreak@1 2053 end
flickerstreak@1 2054 local old_CancelLogout = CancelLogout
flickerstreak@1 2055 function CancelLogout()
flickerstreak@1 2056 if self.hooks.CancelLogout then
flickerstreak@1 2057 return self.hooks.CancelLogout(self, old_CancelLogout)
flickerstreak@1 2058 else
flickerstreak@1 2059 return old_CancelLogout()
flickerstreak@1 2060 end
flickerstreak@1 2061 end
flickerstreak@1 2062 local old_Quit = Quit
flickerstreak@1 2063 function Quit()
flickerstreak@1 2064 if self.hooks.Quit then
flickerstreak@1 2065 return self.hooks.Quit(self, old_Quit)
flickerstreak@1 2066 else
flickerstreak@1 2067 return old_Quit()
flickerstreak@1 2068 end
flickerstreak@1 2069 end
flickerstreak@1 2070 local old_FCFDropDown_LoadChannels = FCFDropDown_LoadChannels
flickerstreak@1 2071 function FCFDropDown_LoadChannels(...)
flickerstreak@1 2072 if self.hooks.FCFDropDown_LoadChannels then
flickerstreak@1 2073 return self.hooks.FCFDropDown_LoadChannels(self, old_FCFDropDown_LoadChannels, ...)
flickerstreak@1 2074 else
flickerstreak@1 2075 return old_FCFDropDown_LoadChannels(...)
flickerstreak@1 2076 end
flickerstreak@1 2077 end
flickerstreak@1 2078 local old_JoinChannelByName = JoinChannelByName
flickerstreak@1 2079 function JoinChannelByName(a,b,c,d,e,f,g,h,i,j)
flickerstreak@1 2080 if self.hooks.JoinChannelByName then
flickerstreak@1 2081 return self.hooks.JoinChannelByName(self, old_JoinChannelByName, a,b,c,d,e,f,g,h,i,j)
flickerstreak@1 2082 else
flickerstreak@1 2083 return old_JoinChannelByName(a,b,c,d,e,f,g,h,i,j)
flickerstreak@1 2084 end
flickerstreak@1 2085 end
flickerstreak@1 2086 end
flickerstreak@1 2087
flickerstreak@1 2088 if not self.recvQueue then
flickerstreak@1 2089 self.recvQueue = {}
flickerstreak@1 2090 end
flickerstreak@1 2091 if not self.registry then
flickerstreak@1 2092 self.registry = {}
flickerstreak@1 2093 end
flickerstreak@1 2094 AceComm_registry = self.registry
flickerstreak@1 2095 if not self.prefixes then
flickerstreak@1 2096 self.prefixes = {}
flickerstreak@1 2097 end
flickerstreak@1 2098 if not self.classes then
flickerstreak@1 2099 self.classes = {}
flickerstreak@1 2100 else
flickerstreak@1 2101 for k in pairs(self.classes) do
flickerstreak@1 2102 self.classes[k] = nil
flickerstreak@1 2103 end
flickerstreak@1 2104 end
flickerstreak@1 2105 if not self.prefixMemoizations then
flickerstreak@1 2106 self.prefixMemoizations = {}
flickerstreak@1 2107 end
flickerstreak@1 2108 if not self.prefixHashToText then
flickerstreak@1 2109 self.prefixHashToText = {}
flickerstreak@1 2110 end
flickerstreak@1 2111 if not self.prefixTextToHash then
flickerstreak@1 2112 self.prefixTextToHash = {}
flickerstreak@1 2113 end
flickerstreak@1 2114 if not self.recentWhispers then
flickerstreak@1 2115 self.recentWhispers = {}
flickerstreak@1 2116 end
flickerstreak@1 2117 if not self.userRegistry then
flickerstreak@1 2118 self.userRegistry = {}
flickerstreak@1 2119 end
flickerstreak@1 2120
flickerstreak@1 2121 SetCVar("spamFilter", 0)
flickerstreak@1 2122
flickerstreak@1 2123 self:activate(oldLib, oldDeactivate)
flickerstreak@1 2124
flickerstreak@1 2125 if oldDeactivate then
flickerstreak@1 2126 oldDeactivate(oldLib)
flickerstreak@1 2127 end
flickerstreak@1 2128 end
flickerstreak@1 2129
flickerstreak@1 2130 local function external(self, major, instance)
flickerstreak@1 2131 if major == "AceEvent-2.0" then
flickerstreak@1 2132 AceEvent = instance
flickerstreak@1 2133
flickerstreak@1 2134 AceEvent:embed(AceComm)
flickerstreak@1 2135
flickerstreak@1 2136 self:UnregisterAllEvents()
flickerstreak@1 2137 self:CancelAllScheduledEvents()
flickerstreak@1 2138
flickerstreak@1 2139 if AceEvent:IsFullyInitialized() then
flickerstreak@1 2140 self:AceEvent_FullyInitialized()
flickerstreak@1 2141 else
flickerstreak@1 2142 self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true)
flickerstreak@1 2143 end
flickerstreak@1 2144
flickerstreak@1 2145 self:RegisterEvent("PLAYER_LOGOUT")
flickerstreak@1 2146 self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
flickerstreak@1 2147 self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE")
flickerstreak@1 2148 self:RegisterEvent("CHAT_MSG_SYSTEM")
flickerstreak@1 2149 else
flickerstreak@1 2150 if AceOO.inherits(instance, AceOO.Class) and not instance.class then
flickerstreak@1 2151 self.classes[TailoredNumericCheckSum(major)] = instance
flickerstreak@1 2152 end
flickerstreak@1 2153 end
flickerstreak@1 2154 end
flickerstreak@1 2155
flickerstreak@1 2156 AceLibrary:Register(AceComm, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
flickerstreak@1 2157
flickerstreak@1 2158
flickerstreak@1 2159
flickerstreak@1 2160
flickerstreak@1 2161
flickerstreak@1 2162 --
flickerstreak@1 2163 -- ChatThrottleLib by Mikk
flickerstreak@1 2164 --
flickerstreak@1 2165 -- Manages AddOn chat output to keep player from getting kicked off.
flickerstreak@1 2166 --
flickerstreak@1 2167 -- ChatThrottleLib.SendChatMessage/.SendAddonMessage functions that accept
flickerstreak@1 2168 -- a Priority ("BULK", "NORMAL", "ALERT") as well as prefix for SendChatMessage.
flickerstreak@1 2169 --
flickerstreak@1 2170 -- Priorities get an equal share of available bandwidth when fully loaded.
flickerstreak@1 2171 -- Communication channels are separated on extension+chattype+destination and
flickerstreak@1 2172 -- get round-robinned. (Destination only matters for whispers and channels,
flickerstreak@1 2173 -- obviously)
flickerstreak@1 2174 --
flickerstreak@1 2175 -- Will install hooks for SendChatMessage and SendAdd[Oo]nMessage to measure
flickerstreak@1 2176 -- bandwidth bypassing the library and use less bandwidth itself.
flickerstreak@1 2177 --
flickerstreak@1 2178 --
flickerstreak@1 2179 -- Fully embeddable library. Just copy this file into your addon directory,
flickerstreak@1 2180 -- add it to the .toc, and it's done.
flickerstreak@1 2181 --
flickerstreak@1 2182 -- Can run as a standalone addon also, but, really, just embed it! :-)
flickerstreak@1 2183 --
flickerstreak@1 2184
flickerstreak@1 2185 local CTL_VERSION = 13
flickerstreak@1 2186
flickerstreak@1 2187 local MAX_CPS = 800 -- 2000 seems to be safe if NOTHING ELSE is happening. let's call it 800.
flickerstreak@1 2188 local MSG_OVERHEAD = 40 -- Guesstimate overhead for sending a message; source+dest+chattype+protocolstuff
flickerstreak@1 2189
flickerstreak@1 2190 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 2191
flickerstreak@1 2192 local MIN_FPS = 20 -- Reduce output CPS to half (and don't burst) if FPS drops below this value
flickerstreak@1 2193
flickerstreak@1 2194 if(ChatThrottleLib and ChatThrottleLib.version>=CTL_VERSION) then
flickerstreak@1 2195 -- There's already a newer (or same) version loaded. Buh-bye.
flickerstreak@1 2196 return;
flickerstreak@1 2197 end
flickerstreak@1 2198
flickerstreak@1 2199
flickerstreak@1 2200
flickerstreak@1 2201 if(not ChatThrottleLib) then
flickerstreak@1 2202 ChatThrottleLib = {}
flickerstreak@1 2203 end
flickerstreak@1 2204
flickerstreak@1 2205 local ChatThrottleLib = ChatThrottleLib
flickerstreak@1 2206 local strlen = strlen
flickerstreak@1 2207 local setmetatable = setmetatable
flickerstreak@1 2208 local getn = getn
flickerstreak@1 2209 local tremove = tremove
flickerstreak@1 2210 local tinsert = tinsert
flickerstreak@1 2211 local tostring = tostring
flickerstreak@1 2212 local GetTime = GetTime
flickerstreak@1 2213 local format = format
flickerstreak@1 2214
flickerstreak@1 2215 ChatThrottleLib.version=CTL_VERSION;
flickerstreak@1 2216
flickerstreak@1 2217
flickerstreak@1 2218 -----------------------------------------------------------------------
flickerstreak@1 2219 -- Double-linked ring implementation
flickerstreak@1 2220
flickerstreak@1 2221 local Ring = {}
flickerstreak@1 2222 local RingMeta = { __index=Ring }
flickerstreak@1 2223
flickerstreak@1 2224 function Ring:New()
flickerstreak@1 2225 local ret = {}
flickerstreak@1 2226 setmetatable(ret, RingMeta)
flickerstreak@1 2227 return ret;
flickerstreak@1 2228 end
flickerstreak@1 2229
flickerstreak@1 2230 function Ring:Add(obj) -- Append at the "far end" of the ring (aka just before the current position)
flickerstreak@1 2231 if(self.pos) then
flickerstreak@1 2232 obj.prev = self.pos.prev;
flickerstreak@1 2233 obj.prev.next = obj;
flickerstreak@1 2234 obj.next = self.pos;
flickerstreak@1 2235 obj.next.prev = obj;
flickerstreak@1 2236 else
flickerstreak@1 2237 obj.next = obj;
flickerstreak@1 2238 obj.prev = obj;
flickerstreak@1 2239 self.pos = obj;
flickerstreak@1 2240 end
flickerstreak@1 2241 end
flickerstreak@1 2242
flickerstreak@1 2243 function Ring:Remove(obj)
flickerstreak@1 2244 obj.next.prev = obj.prev;
flickerstreak@1 2245 obj.prev.next = obj.next;
flickerstreak@1 2246 if(self.pos == obj) then
flickerstreak@1 2247 self.pos = obj.next;
flickerstreak@1 2248 if(self.pos == obj) then
flickerstreak@1 2249 self.pos = nil;
flickerstreak@1 2250 end
flickerstreak@1 2251 end
flickerstreak@1 2252 end
flickerstreak@1 2253
flickerstreak@1 2254
flickerstreak@1 2255
flickerstreak@1 2256 -----------------------------------------------------------------------
flickerstreak@1 2257 -- Recycling bin for pipes (kept in a linked list because that's
flickerstreak@1 2258 -- how they're worked with in the rotating rings; just reusing members)
flickerstreak@1 2259
flickerstreak@1 2260 ChatThrottleLib.PipeBin = { count=0 }
flickerstreak@1 2261
flickerstreak@1 2262 function ChatThrottleLib.PipeBin:Put(pipe)
flickerstreak@1 2263 for i=getn(pipe),1,-1 do
flickerstreak@1 2264 tremove(pipe, i);
flickerstreak@1 2265 end
flickerstreak@1 2266 pipe.prev = nil;
flickerstreak@1 2267 pipe.next = self.list;
flickerstreak@1 2268 self.list = pipe;
flickerstreak@1 2269 self.count = self.count+1;
flickerstreak@1 2270 end
flickerstreak@1 2271
flickerstreak@1 2272 function ChatThrottleLib.PipeBin:Get()
flickerstreak@1 2273 if(self.list) then
flickerstreak@1 2274 local ret = self.list;
flickerstreak@1 2275 self.list = ret.next;
flickerstreak@1 2276 ret.next=nil;
flickerstreak@1 2277 self.count = self.count - 1;
flickerstreak@1 2278 return ret;
flickerstreak@1 2279 end
flickerstreak@1 2280 return {};
flickerstreak@1 2281 end
flickerstreak@1 2282
flickerstreak@1 2283 function ChatThrottleLib.PipeBin:Tidy()
flickerstreak@1 2284 if(self.count < 25) then
flickerstreak@1 2285 return;
flickerstreak@1 2286 end
flickerstreak@1 2287
flickerstreak@1 2288 if(self.count > 100) then
flickerstreak@1 2289 n=self.count-90;
flickerstreak@1 2290 else
flickerstreak@1 2291 n=10;
flickerstreak@1 2292 end
flickerstreak@1 2293 for i=2,n do
flickerstreak@1 2294 self.list = self.list.next;
flickerstreak@1 2295 end
flickerstreak@1 2296 local delme = self.list;
flickerstreak@1 2297 self.list = self.list.next;
flickerstreak@1 2298 delme.next = nil;
flickerstreak@1 2299 end
flickerstreak@1 2300
flickerstreak@1 2301
flickerstreak@1 2302
flickerstreak@1 2303
flickerstreak@1 2304 -----------------------------------------------------------------------
flickerstreak@1 2305 -- Recycling bin for messages
flickerstreak@1 2306
flickerstreak@1 2307 ChatThrottleLib.MsgBin = {}
flickerstreak@1 2308
flickerstreak@1 2309 function ChatThrottleLib.MsgBin:Put(msg)
flickerstreak@1 2310 msg.text = nil;
flickerstreak@1 2311 tinsert(self, msg);
flickerstreak@1 2312 end
flickerstreak@1 2313
flickerstreak@1 2314 function ChatThrottleLib.MsgBin:Get()
flickerstreak@1 2315 local ret = tremove(self, getn(self));
flickerstreak@1 2316 if(ret) then return ret; end
flickerstreak@1 2317 return {};
flickerstreak@1 2318 end
flickerstreak@1 2319
flickerstreak@1 2320 function ChatThrottleLib.MsgBin:Tidy()
flickerstreak@1 2321 if(getn(self)<50) then
flickerstreak@1 2322 return;
flickerstreak@1 2323 end
flickerstreak@1 2324 if(getn(self)>150) then -- "can't happen" but ...
flickerstreak@1 2325 for n=getn(self),120,-1 do
flickerstreak@1 2326 tremove(self,n);
flickerstreak@1 2327 end
flickerstreak@1 2328 else
flickerstreak@1 2329 for n=getn(self),getn(self)-20,-1 do
flickerstreak@1 2330 tremove(self,n);
flickerstreak@1 2331 end
flickerstreak@1 2332 end
flickerstreak@1 2333 end
flickerstreak@1 2334
flickerstreak@1 2335
flickerstreak@1 2336 -----------------------------------------------------------------------
flickerstreak@1 2337 -- ChatThrottleLib:Init
flickerstreak@1 2338 -- Initialize queues, set up frame for OnUpdate, etc
flickerstreak@1 2339
flickerstreak@1 2340
flickerstreak@1 2341 function ChatThrottleLib:Init()
flickerstreak@1 2342
flickerstreak@1 2343 -- Set up queues
flickerstreak@1 2344 if(not self.Prio) then
flickerstreak@1 2345 self.Prio = {}
flickerstreak@1 2346 self.Prio["ALERT"] = { ByName={}, Ring = Ring:New(), avail=0 };
flickerstreak@1 2347 self.Prio["NORMAL"] = { ByName={}, Ring = Ring:New(), avail=0 };
flickerstreak@1 2348 self.Prio["BULK"] = { ByName={}, Ring = Ring:New(), avail=0 };
flickerstreak@1 2349 end
flickerstreak@1 2350
flickerstreak@1 2351 -- v4: total send counters per priority
flickerstreak@1 2352 for _,Prio in pairs(self.Prio) do
flickerstreak@1 2353 Prio.nTotalSent = Prio.nTotalSent or 0;
flickerstreak@1 2354 end
flickerstreak@1 2355
flickerstreak@1 2356 self.avail = self.avail or 0; -- v5
flickerstreak@1 2357 self.nTotalSent = self.nTotalSent or 0; -- v5
flickerstreak@1 2358
flickerstreak@1 2359
flickerstreak@1 2360 -- Set up a frame to get OnUpdate events
flickerstreak@1 2361 if(not self.Frame) then
flickerstreak@1 2362 self.Frame = CreateFrame("Frame");
flickerstreak@1 2363 self.Frame:Hide();
flickerstreak@1 2364 end
flickerstreak@1 2365 self.Frame.Show = self.Frame.Show; -- cache for speed
flickerstreak@1 2366 self.Frame.Hide = self.Frame.Hide; -- cache for speed
flickerstreak@1 2367 self.Frame:SetScript("OnUpdate", self.OnUpdate);
flickerstreak@1 2368 self.Frame:SetScript("OnEvent", self.OnEvent); -- v11: Monitor P_E_W so we can throttle hard for a few seconds
flickerstreak@1 2369 self.Frame:RegisterEvent("PLAYER_ENTERING_WORLD");
flickerstreak@1 2370 self.OnUpdateDelay=0;
flickerstreak@1 2371 self.LastAvailUpdate=GetTime();
flickerstreak@1 2372 self.HardThrottlingBeginTime=GetTime(); -- v11: Throttle hard for a few seconds after startup
flickerstreak@1 2373
flickerstreak@1 2374 -- Hook SendChatMessage and SendAddonMessage so we can measure unpiped traffic and avoid overloads (v7)
flickerstreak@1 2375 if(not self.ORIG_SendChatMessage) then
flickerstreak@1 2376 --SendChatMessage
flickerstreak@1 2377 self.ORIG_SendChatMessage = SendChatMessage;
flickerstreak@1 2378 SendChatMessage = function(a1,a2,a3,a4) return ChatThrottleLib.Hook_SendChatMessage(a1,a2,a3,a4); end
flickerstreak@1 2379 --SendAdd[Oo]nMessage
flickerstreak@1 2380 if(SendAddonMessage or SendAddOnMessage) then -- v10: don't pretend like it doesn't exist if it doesn't!
flickerstreak@1 2381 self.ORIG_SendAddonMessage = SendAddonMessage or SendAddOnMessage;
flickerstreak@1 2382 SendAddonMessage = function(a1,a2,a3) return ChatThrottleLib.Hook_SendAddonMessage(a1,a2,a3); end
flickerstreak@1 2383 if(SendAddOnMessage) then -- in case Slouken changes his mind...
flickerstreak@1 2384 SendAddOnMessage = SendAddonMessage;
flickerstreak@1 2385 end
flickerstreak@1 2386 end
flickerstreak@1 2387 end
flickerstreak@1 2388 self.nBypass = 0;
flickerstreak@1 2389 end
flickerstreak@1 2390
flickerstreak@1 2391
flickerstreak@1 2392 -----------------------------------------------------------------------
flickerstreak@1 2393 -- ChatThrottleLib.Hook_SendChatMessage / .Hook_SendAddonMessage
flickerstreak@1 2394 function ChatThrottleLib.Hook_SendChatMessage(text, chattype, language, destination)
flickerstreak@1 2395 local self = ChatThrottleLib;
flickerstreak@1 2396 local size = strlen(tostring(text or "")) + strlen(tostring(chattype or "")) + strlen(tostring(destination or "")) + 40;
flickerstreak@1 2397 self.avail = self.avail - size;
flickerstreak@1 2398 self.nBypass = self.nBypass + size;
flickerstreak@1 2399 return self.ORIG_SendChatMessage(text, chattype, language, destination);
flickerstreak@1 2400 end
flickerstreak@1 2401 function ChatThrottleLib.Hook_SendAddonMessage(prefix, text, chattype)
flickerstreak@1 2402 local self = ChatThrottleLib;
flickerstreak@1 2403 local size = strlen(tostring(text or "")) + strlen(tostring(chattype or "")) + strlen(tostring(prefix or "")) + 40;
flickerstreak@1 2404 self.avail = self.avail - size;
flickerstreak@1 2405 self.nBypass = self.nBypass + size;
flickerstreak@1 2406 return self.ORIG_SendAddonMessage(prefix, text, chattype);
flickerstreak@1 2407 end
flickerstreak@1 2408
flickerstreak@1 2409
flickerstreak@1 2410
flickerstreak@1 2411 -----------------------------------------------------------------------
flickerstreak@1 2412 -- ChatThrottleLib:UpdateAvail
flickerstreak@1 2413 -- Update self.avail with how much bandwidth is currently available
flickerstreak@1 2414
flickerstreak@1 2415 function ChatThrottleLib:UpdateAvail()
flickerstreak@1 2416 local now = GetTime();
flickerstreak@1 2417 local newavail = MAX_CPS * (now-self.LastAvailUpdate);
flickerstreak@1 2418
flickerstreak@1 2419 if(now - self.HardThrottlingBeginTime < 5) then
flickerstreak@1 2420 -- First 5 seconds after startup/zoning: VERY hard clamping to avoid irritating the server rate limiter, it seems very cranky then
flickerstreak@1 2421 self.avail = min(self.avail + (newavail*0.1), MAX_CPS*0.5);
flickerstreak@1 2422 elseif(GetFramerate()<MIN_FPS) then -- GetFrameRate call takes ~0.002 secs
flickerstreak@1 2423 newavail = newavail * 0.5;
flickerstreak@1 2424 self.avail = min(MAX_CPS, self.avail + newavail);
flickerstreak@1 2425 self.bChoking = true; -- just for stats
flickerstreak@1 2426 else
flickerstreak@1 2427 self.avail = min(BURST, self.avail + newavail);
flickerstreak@1 2428 self.bChoking = false;
flickerstreak@1 2429 end
flickerstreak@1 2430
flickerstreak@1 2431 self.avail = max(self.avail, 0-(MAX_CPS*2)); -- Can go negative when someone is eating bandwidth past the lib. but we refuse to stay silent for more than 2 seconds; if they can do it, we can.
flickerstreak@1 2432 self.LastAvailUpdate = now;
flickerstreak@1 2433
flickerstreak@1 2434 return self.avail;
flickerstreak@1 2435 end
flickerstreak@1 2436
flickerstreak@1 2437
flickerstreak@1 2438 -----------------------------------------------------------------------
flickerstreak@1 2439 -- Despooling logic
flickerstreak@1 2440
flickerstreak@1 2441 function ChatThrottleLib:Despool(Prio)
flickerstreak@1 2442 local ring = Prio.Ring;
flickerstreak@1 2443 while(ring.pos and Prio.avail>ring.pos[1].nSize) do
flickerstreak@1 2444 local msg = tremove(Prio.Ring.pos, 1);
flickerstreak@1 2445 if(not Prio.Ring.pos[1]) then
flickerstreak@1 2446 local pipe = Prio.Ring.pos;
flickerstreak@1 2447 Prio.Ring:Remove(pipe);
flickerstreak@1 2448 Prio.ByName[pipe.name] = nil;
flickerstreak@1 2449 self.PipeBin:Put(pipe);
flickerstreak@1 2450 else
flickerstreak@1 2451 Prio.Ring.pos = Prio.Ring.pos.next;
flickerstreak@1 2452 end
flickerstreak@1 2453 Prio.avail = Prio.avail - msg.nSize;
flickerstreak@1 2454 msg.f(msg[1], msg[2], msg[3], msg[4]);
flickerstreak@1 2455 Prio.nTotalSent = Prio.nTotalSent + msg.nSize;
flickerstreak@1 2456 self.MsgBin:Put(msg);
flickerstreak@1 2457 end
flickerstreak@1 2458 end
flickerstreak@1 2459
flickerstreak@1 2460
flickerstreak@1 2461 function ChatThrottleLib.OnEvent()
flickerstreak@1 2462 -- v11: We know that the rate limiter is touchy after login. Assume that it's touch after zoning, too.
flickerstreak@1 2463 self = ChatThrottleLib;
flickerstreak@1 2464 if(event == "PLAYER_ENTERING_WORLD") then
flickerstreak@1 2465 self.HardThrottlingBeginTime=GetTime(); -- Throttle hard for a few seconds after zoning
flickerstreak@1 2466 self.avail = 0;
flickerstreak@1 2467 end
flickerstreak@1 2468 end
flickerstreak@1 2469
flickerstreak@1 2470
flickerstreak@1 2471 function ChatThrottleLib.OnUpdate()
flickerstreak@1 2472 self = ChatThrottleLib;
flickerstreak@1 2473
flickerstreak@1 2474 self.OnUpdateDelay = self.OnUpdateDelay + arg1;
flickerstreak@1 2475 if(self.OnUpdateDelay < 0.08) then
flickerstreak@1 2476 return;
flickerstreak@1 2477 end
flickerstreak@1 2478 self.OnUpdateDelay = 0;
flickerstreak@1 2479
flickerstreak@1 2480 self:UpdateAvail();
flickerstreak@1 2481
flickerstreak@1 2482 if(self.avail<0) then
flickerstreak@1 2483 return; -- argh. some bastard is spewing stuff past the lib. just bail early to save cpu.
flickerstreak@1 2484 end
flickerstreak@1 2485
flickerstreak@1 2486 -- See how many of or priorities have queued messages
flickerstreak@1 2487 local n=0;
flickerstreak@1 2488 for prioname,Prio in pairs(self.Prio) do
flickerstreak@1 2489 if(Prio.Ring.pos or Prio.avail<0) then
flickerstreak@1 2490 n=n+1;
flickerstreak@1 2491 end
flickerstreak@1 2492 end
flickerstreak@1 2493
flickerstreak@1 2494 -- Anything queued still?
flickerstreak@1 2495 if(n<1) then
flickerstreak@1 2496 -- Nope. Move spillover bandwidth to global availability gauge and clear self.bQueueing
flickerstreak@1 2497 for prioname,Prio in pairs(self.Prio) do
flickerstreak@1 2498 self.avail = self.avail + Prio.avail;
flickerstreak@1 2499 Prio.avail = 0;
flickerstreak@1 2500 end
flickerstreak@1 2501 self.bQueueing = false;
flickerstreak@1 2502 self.Frame:Hide();
flickerstreak@1 2503 return;
flickerstreak@1 2504 end
flickerstreak@1 2505
flickerstreak@1 2506 -- There's stuff queued. Hand out available bandwidth to priorities as needed and despool their queues
flickerstreak@1 2507 local avail= self.avail/n;
flickerstreak@1 2508 self.avail = 0;
flickerstreak@1 2509
flickerstreak@1 2510 for prioname,Prio in pairs(self.Prio) do
flickerstreak@1 2511 if(Prio.Ring.pos or Prio.avail<0) then
flickerstreak@1 2512 Prio.avail = Prio.avail + avail;
flickerstreak@1 2513 if(Prio.Ring.pos and Prio.avail>Prio.Ring.pos[1].nSize) then
flickerstreak@1 2514 self:Despool(Prio);
flickerstreak@1 2515 end
flickerstreak@1 2516 end
flickerstreak@1 2517 end
flickerstreak@1 2518
flickerstreak@1 2519 -- Expire recycled tables if needed
flickerstreak@1 2520 self.MsgBin:Tidy();
flickerstreak@1 2521 self.PipeBin:Tidy();
flickerstreak@1 2522 end
flickerstreak@1 2523
flickerstreak@1 2524
flickerstreak@1 2525
flickerstreak@1 2526
flickerstreak@1 2527 -----------------------------------------------------------------------
flickerstreak@1 2528 -- Spooling logic
flickerstreak@1 2529
flickerstreak@1 2530
flickerstreak@1 2531 function ChatThrottleLib:Enqueue(prioname, pipename, msg)
flickerstreak@1 2532 local Prio = self.Prio[prioname];
flickerstreak@1 2533 local pipe = Prio.ByName[pipename];
flickerstreak@1 2534 if(not pipe) then
flickerstreak@1 2535 self.Frame:Show();
flickerstreak@1 2536 pipe = self.PipeBin:Get();
flickerstreak@1 2537 pipe.name = pipename;
flickerstreak@1 2538 Prio.ByName[pipename] = pipe;
flickerstreak@1 2539 Prio.Ring:Add(pipe);
flickerstreak@1 2540 end
flickerstreak@1 2541
flickerstreak@1 2542 tinsert(pipe, msg);
flickerstreak@1 2543
flickerstreak@1 2544 self.bQueueing = true;
flickerstreak@1 2545 end
flickerstreak@1 2546
flickerstreak@1 2547
flickerstreak@1 2548
flickerstreak@1 2549 function ChatThrottleLib:SendChatMessage(prio, prefix, text, chattype, language, destination)
flickerstreak@1 2550 if(not (self and prio and text and self.Prio[prio] ) ) then
flickerstreak@1 2551 error('Usage: ChatThrottleLib:SendChatMessage("{BULK||NORMAL||ALERT}", "prefix" or nil, "text"[, "chattype"[, "language"[, "destination"]]]', 2);
flickerstreak@1 2552 end
flickerstreak@1 2553
flickerstreak@1 2554 prefix = prefix or tostring(this); -- each frame gets its own queue if prefix is not given
flickerstreak@1 2555
flickerstreak@1 2556 local nSize = strlen(text) + MSG_OVERHEAD;
flickerstreak@1 2557
flickerstreak@1 2558 -- Check if there's room in the global available bandwidth gauge to send directly
flickerstreak@1 2559 if(not self.bQueueing and nSize < self:UpdateAvail()) then
flickerstreak@1 2560 self.avail = self.avail - nSize;
flickerstreak@1 2561 self.ORIG_SendChatMessage(text, chattype, language, destination);
flickerstreak@1 2562 self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize;
flickerstreak@1 2563 return;
flickerstreak@1 2564 end
flickerstreak@1 2565
flickerstreak@1 2566 -- Message needs to be queued
flickerstreak@1 2567 msg=self.MsgBin:Get();
flickerstreak@1 2568 msg.f=self.ORIG_SendChatMessage
flickerstreak@1 2569 msg[1]=text;
flickerstreak@1 2570 msg[2]=chattype or "SAY";
flickerstreak@1 2571 msg[3]=language;
flickerstreak@1 2572 msg[4]=destination;
flickerstreak@1 2573 msg.n = 4
flickerstreak@1 2574 msg.nSize = nSize;
flickerstreak@1 2575
flickerstreak@1 2576 self:Enqueue(prio, format("%s/%s/%s", prefix, chattype, destination or ""), msg);
flickerstreak@1 2577 end
flickerstreak@1 2578
flickerstreak@1 2579
flickerstreak@1 2580 function ChatThrottleLib:SendAddonMessage(prio, prefix, text, chattype)
flickerstreak@1 2581 if(not (self and prio and prefix and text and chattype and self.Prio[prio] ) ) then
flickerstreak@1 2582 error('Usage: ChatThrottleLib:SendAddonMessage("{BULK||NORMAL||ALERT}", "prefix", "text", "chattype")', 0);
flickerstreak@1 2583 end
flickerstreak@1 2584
flickerstreak@1 2585 local nSize = strlen(prefix) + 1 + strlen(text) + MSG_OVERHEAD;
flickerstreak@1 2586
flickerstreak@1 2587 -- Check if there's room in the global available bandwidth gauge to send directly
flickerstreak@1 2588 if(not self.bQueueing and nSize < self:UpdateAvail()) then
flickerstreak@1 2589 self.avail = self.avail - nSize;
flickerstreak@1 2590 self.ORIG_SendAddonMessage(prefix, text, chattype);
flickerstreak@1 2591 self.Prio[prio].nTotalSent = self.Prio[prio].nTotalSent + nSize;
flickerstreak@1 2592 return;
flickerstreak@1 2593 end
flickerstreak@1 2594
flickerstreak@1 2595 -- Message needs to be queued
flickerstreak@1 2596 msg=self.MsgBin:Get();
flickerstreak@1 2597 msg.f=self.ORIG_SendAddonMessage;
flickerstreak@1 2598 msg[1]=prefix;
flickerstreak@1 2599 msg[2]=text;
flickerstreak@1 2600 msg[3]=chattype;
flickerstreak@1 2601 msg.n = 3
flickerstreak@1 2602 msg.nSize = nSize;
flickerstreak@1 2603
flickerstreak@1 2604 self:Enqueue(prio, format("%s/%s", prefix, chattype), msg);
flickerstreak@1 2605 end
flickerstreak@1 2606
flickerstreak@1 2607
flickerstreak@1 2608
flickerstreak@1 2609
flickerstreak@1 2610 -----------------------------------------------------------------------
flickerstreak@1 2611 -- Get the ball rolling!
flickerstreak@1 2612
flickerstreak@1 2613 ChatThrottleLib:Init();
flickerstreak@1 2614
flickerstreak@1 2615 --[[ WoWBench debugging snippet
flickerstreak@1 2616 if(WOWB_VER) then
flickerstreak@1 2617 local function SayTimer()
flickerstreak@1 2618 print("SAY: "..GetTime().." "..arg1);
flickerstreak@1 2619 end
flickerstreak@1 2620 ChatThrottleLib.Frame:SetScript("OnEvent", SayTimer);
flickerstreak@1 2621 ChatThrottleLib.Frame:RegisterEvent("CHAT_MSG_SAY");
flickerstreak@1 2622 end
flickerstreak@1 2623 ]]