/branches/option/Aloft/Libs/AceComm-3.0
-- Can run as a standalone addon also, but, really, just embed it! :-) |
-- |
local CTL_VERSION = 21 |
local CTL_VERSION = 22 |
local _G = _G |
error('ChatThrottleLib:SendAddonMessage(): callbackFn: expected function, got '..type(callbackFn), 2) |
end |
local nSize = prefix:len() + 1 + text:len(); |
local nSize = text:len(); |
if nSize>255 then |
error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2) |
if RegisterAddonMessagePrefix then |
if nSize>255 then |
error("ChatThrottleLib:SendAddonMessage(): message length cannot exceed 255 bytes", 2) |
end |
else |
nSize = nSize + prefix:len() + 1 |
if nSize>255 then |
error("ChatThrottleLib:SendAddonMessage(): prefix + message length cannot exceed 254 bytes", 2) |
end |
end |
nSize = nSize + self.MSG_OVERHEAD; |
-- make into AceComm. |
-- @class file |
-- @name AceComm-3.0 |
-- @release $Id: AceComm-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $ |
-- @release $Id: AceComm-3.0.lua 1019 2011-03-27 12:08:33Z mikk $ |
--[[ AceComm-3.0 |
]] |
local MAJOR, MINOR = "AceComm-3.0", 6 |
local MAJOR, MINOR = "AceComm-3.0", 7 |
local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
-- Lua APIs |
local type, next, pairs, tostring = type, next, pairs, tostring |
local strsub, strfind = string.sub, string.find |
local match = string.match |
local tinsert, tconcat = table.insert, table.concat |
local error, assert = error, assert |
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler |
-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix |
AceComm.embeds = AceComm.embeds or {} |
local MSG_MULTI_FIRST = "\001" |
local MSG_MULTI_NEXT = "\002" |
local MSG_MULTI_LAST = "\003" |
local MSG_ESCAPE = "\004" |
AceComm.multipart_origprefixes = AceComm.multipart_origprefixes or {} -- e.g. "Prefix\001"="Prefix", "Prefix\002"="Prefix" |
AceComm.multipart_reassemblers = AceComm.multipart_reassemblers or {} -- e.g. "Prefix\001"="OnReceiveMultipartFirst" |
if not RegisterAddonMessagePrefix then |
AceComm.multipart_origprefixes = AceComm.multipart_origprefixes or {} -- e.g. "Prefix\001"="Prefix", "Prefix\002"="Prefix" |
AceComm.multipart_reassemblers = AceComm.multipart_reassemblers or {} -- e.g. "Prefix\001"="OnReceiveMultipartFirst" |
else |
AceComm.multipart_origprefixes = nil |
AceComm.multipart_reassemblers = nil |
end |
-- the multipart message spool: indexed by a combination of sender+distribution+ |
AceComm.multipart_spool = AceComm.multipart_spool or {} |
--- Register for Addon Traffic on a specified prefix |
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent) |
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters |
-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived" |
function AceComm:RegisterComm(prefix, method) |
if method == nil then |
method = "OnCommReceived" |
end |
if RegisterAddonMessagePrefix then |
if #prefix>16 then -- TODO: 15? |
error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters") |
end |
RegisterAddonMessagePrefix(prefix) |
end |
return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler |
end |
error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2) |
end |
if strfind(prefix, "[\001-\009]") then |
if strfind(prefix, "[\001-\003]") then |
error("SendCommMessage: Characters \\001--\\003 in prefix are reserved for AceComm metadata", 2) |
elseif not warnedPrefix then |
-- I have some ideas about future extensions that require more control characters /mikk, 20090808 |
geterrorhandler()("SendCommMessage: Heads-up developers: Characters \\004--\\009 in prefix are reserved for AceComm future extension") |
warnedPrefix = true |
if not RegisterAddonMessagePrefix then |
if strfind(prefix, "[\001-\009]") then |
if strfind(prefix, "[\001-\003]") then |
error("SendCommMessage: Characters \\001--\\003 in prefix are reserved for AceComm metadata", 2) |
elseif not warnedPrefix then |
-- I have some ideas about future extensions that require more control characters /mikk, 20090808 |
geterrorhandler()("SendCommMessage: Heads-up developers: Characters \\004--\\009 in prefix are reserved for AceComm future extension") |
warnedPrefix = true |
end |
end |
end |
local textlen = #text |
local maxtextlen = 254 - #prefix -- 254 is the max length of prefix + text that can be sent in one message |
local maxtextlen; |
if not RegisterAddonMessagePrefix then |
maxtextlen = 254 - #prefix -- 254 is the max length of prefix + text that can be sent in one message (there's an internal separator char) |
else |
maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327 |
end |
local queueName = prefix..distribution..(target or "") |
local ctlCallback = nil |
return callbackFn(callbackArg, sent, textlen) |
end |
end |
local forceMultipart |
if RegisterAddonMessagePrefix and match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character |
-- we need to escape the first character with a \004 |
if textlen+1 > maxtextlen then -- would we go over the size limit? |
forceMultipart = true -- just make it multipart, no escape problems then |
else |
text = "\004" .. text |
end |
end |
if textlen <= maxtextlen then |
if not forceMultipart and textlen <= maxtextlen then |
-- fits all in one message |
CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen) |
else |
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix |
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1) |
-- first part |
local chunk = strsub(text, 1, maxtextlen) |
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_FIRST, chunk, distribution, target, queueName, ctlCallback, maxtextlen) |
if not RegisterAddonMessagePrefix then |
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_FIRST, chunk, distribution, target, queueName, ctlCallback, maxtextlen) |
else |
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen) |
end |
-- continuation |
local pos = 1+maxtextlen |
local prefix2 = prefix..MSG_MULTI_NEXT |
while pos+maxtextlen <= textlen do |
chunk = strsub(text, pos, pos+maxtextlen-1) |
CTL:SendAddonMessage(prio, prefix2, chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1) |
if not RegisterAddonMessagePrefix then |
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_NEXT, chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1) |
else |
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1) |
end |
pos = pos + maxtextlen |
end |
-- final part |
chunk = strsub(text, pos) |
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_LAST, chunk, distribution, target, queueName, ctlCallback, textlen) |
if not RegisterAddonMessagePrefix then |
CTL:SendAddonMessage(prio, prefix..MSG_MULTI_LAST, chunk, distribution, target, queueName, ctlCallback, textlen) |
else |
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen) |
end |
end |
end |
---------------------------------------- |
if not AceComm.callbacks then |
-- ensure that 'prefix to watch' table is consistent with registered |
-- callbacks |
AceComm.__prefixes = {} |
AceComm.callbacks = CallbackHandler:New(AceComm, |
"_RegisterComm", |
"UnregisterComm", |
"UnregisterAllComm") |
end |
function AceComm.callbacks:OnUsed(target, prefix) |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = "OnReceiveMultipartFirst" |
local OnEvent |
if not RegisterAddonMessagePrefix then -- 4.0: per-prefix callbacks per part type |
function AceComm.callbacks:OnUsed(target, prefix) |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = "OnReceiveMultipartFirst" |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = "OnReceiveMultipartNext" |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = "OnReceiveMultipartLast" |
end |
function AceComm.callbacks:OnUnused(target, prefix) |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = nil |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = nil |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = nil |
end |
function OnEvent(this, event, ...) |
if event == "CHAT_MSG_ADDON" then |
local prefix,message,distribution,sender = ... |
local reassemblername = AceComm.multipart_reassemblers[prefix] |
if reassemblername then |
-- multipart: reassemble |
local aceCommReassemblerFunc = AceComm[reassemblername] |
local origprefix = AceComm.multipart_origprefixes[prefix] |
aceCommReassemblerFunc(AceComm, origprefix, message, distribution, sender) |
else |
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not |
AceComm.callbacks:Fire(prefix, message, distribution, sender) |
end |
else |
assert(false, "Received "..tostring(event).." event?!") |
end |
end |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = "OnReceiveMultipartNext" |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = prefix |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = "OnReceiveMultipartLast" |
end |
else -- 4.1+: only one prefix for all |
function AceComm.callbacks:OnUnused(target, prefix) |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_FIRST] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_FIRST] = nil |
AceComm.callbacks.OnUsed = nil |
AceComm.callbacks.OnUnused = nil |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_NEXT] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_NEXT] = nil |
AceComm.multipart_origprefixes[prefix..MSG_MULTI_LAST] = nil |
AceComm.multipart_reassemblers[prefix..MSG_MULTI_LAST] = nil |
end |
local function OnEvent(this, event, ...) |
if event == "CHAT_MSG_ADDON" then |
local prefix,message,distribution,sender = ... |
local reassemblername = AceComm.multipart_reassemblers[prefix] |
if reassemblername then |
-- multipart: reassemble |
local aceCommReassemblerFunc = AceComm[reassemblername] |
local origprefix = AceComm.multipart_origprefixes[prefix] |
aceCommReassemblerFunc(AceComm, origprefix, message, distribution, sender) |
function OnEvent(this, event, ...) |
if event == "CHAT_MSG_ADDON" then |
local prefix,message,distribution,sender = ... |
local control, rest = match(message, "^([\001-\009])(.*)") |
if control then |
if control==MSG_MULTI_FIRST then |
AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender) |
elseif control==MSG_MULTI_NEXT then |
AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender) |
elseif control==MSG_MULTI_LAST then |
AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender) |
elseif control==MSG_ESCAPE then |
AceComm.callbacks:Fire(prefix, rest, distribution, sender) |
else |
-- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!) |
end |
else |
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not |
AceComm.callbacks:Fire(prefix, message, distribution, sender) |
end |
else |
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not |
AceComm.callbacks:Fire(prefix, message, distribution, sender) |
assert(false, "Received "..tostring(event).." event?!") |
end |
else |
assert(false, "Received "..tostring(event).." event?!") |
end |
end |
AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame") |