-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from okdecm/instances-hotfix
Instances hotfix
- Loading branch information
Showing
31 changed files
with
7,912 additions
and
157 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
--- **AceComm-3.0** allows you to send messages of unlimited length over the addon comm channels. | ||
-- It'll automatically split the messages into multiple parts and rebuild them on the receiving end.\\ | ||
-- **ChatThrottleLib** is of course being used to avoid being disconnected by the server. | ||
-- | ||
-- **AceComm-3.0** can be embeded into your addon, either explicitly by calling AceComm:Embed(MyAddon) or by | ||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object | ||
-- and can be accessed directly, without having to explicitly call AceComm itself.\\ | ||
-- It is recommended to embed AceComm, otherwise you'll have to specify a custom `self` on all calls you | ||
-- make into AceComm. | ||
-- @class file | ||
-- @name AceComm-3.0 | ||
-- @release $Id: AceComm-3.0.lua 1202 2019-05-15 23:11:22Z nevcairiel $ | ||
|
||
--[[ AceComm-3.0 | ||
TODO: Time out old data rotting around from dead senders? Not a HUGE deal since the number of possible sender names is somewhat limited. | ||
]] | ||
|
||
local CallbackHandler = LibStub("CallbackHandler-1.0") | ||
local CTL = assert(ChatThrottleLib, "AceComm-3.0 requires ChatThrottleLib") | ||
|
||
local MAJOR, MINOR = "AceComm-3.0", 12 | ||
local AceComm,oldminor = LibStub:NewLibrary(MAJOR, MINOR) | ||
|
||
if not AceComm then return end | ||
|
||
-- Lua APIs | ||
local type, next, pairs, tostring = type, next, pairs, tostring | ||
local strsub, strfind = string.sub, string.find | ||
local match = string.match | ||
local tinsert, tconcat = table.insert, table.concat | ||
local error, assert = error, assert | ||
|
||
-- WoW APIs | ||
local Ambiguate = Ambiguate | ||
|
||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded | ||
-- List them here for Mikk's FindGlobals script | ||
-- GLOBALS: LibStub, DEFAULT_CHAT_FRAME, geterrorhandler, RegisterAddonMessagePrefix | ||
|
||
AceComm.embeds = AceComm.embeds or {} | ||
|
||
-- for my sanity and yours, let's give the message type bytes some names | ||
local MSG_MULTI_FIRST = "\001" | ||
local MSG_MULTI_NEXT = "\002" | ||
local MSG_MULTI_LAST = "\003" | ||
local MSG_ESCAPE = "\004" | ||
|
||
-- remove old structures (pre WoW 4.0) | ||
AceComm.multipart_origprefixes = nil | ||
AceComm.multipart_reassemblers = nil | ||
|
||
-- the multipart message spool: indexed by a combination of sender+distribution+ | ||
AceComm.multipart_spool = AceComm.multipart_spool or {} | ||
|
||
--- Register for Addon Traffic on a specified prefix | ||
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent), max 16 characters | ||
-- @param method Callback to call on message reception: Function reference, or method name (string) to call on self. Defaults to "OnCommReceived" | ||
function AceComm:RegisterComm(prefix, method) | ||
if method == nil then | ||
method = "OnCommReceived" | ||
end | ||
|
||
if #prefix > 16 then -- TODO: 15? | ||
error("AceComm:RegisterComm(prefix,method): prefix length is limited to 16 characters") | ||
end | ||
if C_ChatInfo then | ||
C_ChatInfo.RegisterAddonMessagePrefix(prefix) | ||
else | ||
RegisterAddonMessagePrefix(prefix) | ||
end | ||
|
||
return AceComm._RegisterComm(self, prefix, method) -- created by CallbackHandler | ||
end | ||
|
||
local warnedPrefix=false | ||
|
||
--- Send a message over the Addon Channel | ||
-- @param prefix A printable character (\032-\255) classification of the message (typically AddonName or AddonNameEvent) | ||
-- @param text Data to send, nils (\000) not allowed. Any length. | ||
-- @param distribution Addon channel, e.g. "RAID", "GUILD", etc; see SendAddonMessage API | ||
-- @param target Destination for some distributions; see SendAddonMessage API | ||
-- @param prio OPTIONAL: ChatThrottleLib priority, "BULK", "NORMAL" or "ALERT". Defaults to "NORMAL". | ||
-- @param callbackFn OPTIONAL: callback function to be called as each chunk is sent. receives 3 args: the user supplied arg (see next), the number of bytes sent so far, and the number of bytes total to send. | ||
-- @param callbackArg: OPTIONAL: first arg to the callback function. nil will be passed if not specified. | ||
function AceComm:SendCommMessage(prefix, text, distribution, target, prio, callbackFn, callbackArg) | ||
prio = prio or "NORMAL" -- pasta's reference implementation had different prio for singlepart and multipart, but that's a very bad idea since that can easily lead to out-of-sequence delivery! | ||
if not( type(prefix)=="string" and | ||
type(text)=="string" and | ||
type(distribution)=="string" and | ||
(target==nil or type(target)=="string" or type(target)=="number") and | ||
(prio=="BULK" or prio=="NORMAL" or prio=="ALERT") | ||
) then | ||
error('Usage: SendCommMessage(addon, "prefix", "text", "distribution"[, "target"[, "prio"[, callbackFn, callbackarg]]])', 2) | ||
end | ||
|
||
local textlen = #text | ||
local maxtextlen = 255 -- Yes, the max is 255 even if the dev post said 256. I tested. Char 256+ get silently truncated. /Mikk, 20110327 | ||
local queueName = prefix..distribution..(target or "") | ||
|
||
local ctlCallback = nil | ||
if callbackFn then | ||
ctlCallback = function(sent) | ||
return callbackFn(callbackArg, sent, textlen) | ||
end | ||
end | ||
|
||
local forceMultipart | ||
if match(text, "^[\001-\009]") then -- 4.1+: see if the first character is a control character | ||
-- we need to escape the first character with a \004 | ||
if textlen+1 > maxtextlen then -- would we go over the size limit? | ||
forceMultipart = true -- just make it multipart, no escape problems then | ||
else | ||
text = "\004" .. text | ||
end | ||
end | ||
|
||
if not forceMultipart and textlen <= maxtextlen then | ||
-- fits all in one message | ||
CTL:SendAddonMessage(prio, prefix, text, distribution, target, queueName, ctlCallback, textlen) | ||
else | ||
maxtextlen = maxtextlen - 1 -- 1 extra byte for part indicator in prefix(4.0)/start of message(4.1) | ||
|
||
-- first part | ||
local chunk = strsub(text, 1, maxtextlen) | ||
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_FIRST..chunk, distribution, target, queueName, ctlCallback, maxtextlen) | ||
|
||
-- continuation | ||
local pos = 1+maxtextlen | ||
|
||
while pos+maxtextlen <= textlen do | ||
chunk = strsub(text, pos, pos+maxtextlen-1) | ||
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_NEXT..chunk, distribution, target, queueName, ctlCallback, pos+maxtextlen-1) | ||
pos = pos + maxtextlen | ||
end | ||
|
||
-- final part | ||
chunk = strsub(text, pos) | ||
CTL:SendAddonMessage(prio, prefix, MSG_MULTI_LAST..chunk, distribution, target, queueName, ctlCallback, textlen) | ||
end | ||
end | ||
|
||
|
||
---------------------------------------- | ||
-- Message receiving | ||
---------------------------------------- | ||
|
||
do | ||
local compost = setmetatable({}, {__mode = "k"}) | ||
local function new() | ||
local t = next(compost) | ||
if t then | ||
compost[t]=nil | ||
for i=#t,3,-1 do -- faster than pairs loop. don't even nil out 1/2 since they'll be overwritten | ||
t[i]=nil | ||
end | ||
return t | ||
end | ||
|
||
return {} | ||
end | ||
|
||
local function lostdatawarning(prefix,sender,where) | ||
DEFAULT_CHAT_FRAME:AddMessage(MAJOR..": Warning: lost network data regarding '"..tostring(prefix).."' from '"..tostring(sender).."' (in "..where..")") | ||
end | ||
|
||
function AceComm:OnReceiveMultipartFirst(prefix, message, distribution, sender) | ||
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender | ||
local spool = AceComm.multipart_spool | ||
|
||
--[[ | ||
if spool[key] then | ||
lostdatawarning(prefix,sender,"First") | ||
-- continue and overwrite | ||
end | ||
--]] | ||
|
||
spool[key] = message -- plain string for now | ||
end | ||
|
||
function AceComm:OnReceiveMultipartNext(prefix, message, distribution, sender) | ||
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender | ||
local spool = AceComm.multipart_spool | ||
local olddata = spool[key] | ||
|
||
if not olddata then | ||
--lostdatawarning(prefix,sender,"Next") | ||
return | ||
end | ||
|
||
if type(olddata)~="table" then | ||
-- ... but what we have is not a table. So make it one. (Pull a composted one if available) | ||
local t = new() | ||
t[1] = olddata -- add old data as first string | ||
t[2] = message -- and new message as second string | ||
spool[key] = t -- and put the table in the spool instead of the old string | ||
else | ||
tinsert(olddata, message) | ||
end | ||
end | ||
|
||
function AceComm:OnReceiveMultipartLast(prefix, message, distribution, sender) | ||
local key = prefix.."\t"..distribution.."\t"..sender -- a unique stream is defined by the prefix + distribution + sender | ||
local spool = AceComm.multipart_spool | ||
local olddata = spool[key] | ||
|
||
if not olddata then | ||
--lostdatawarning(prefix,sender,"End") | ||
return | ||
end | ||
|
||
spool[key] = nil | ||
|
||
if type(olddata) == "table" then | ||
-- if we've received a "next", the spooled data will be a table for rapid & garbage-free tconcat | ||
tinsert(olddata, message) | ||
AceComm.callbacks:Fire(prefix, tconcat(olddata, ""), distribution, sender) | ||
compost[olddata] = true | ||
else | ||
-- if we've only received a "first", the spooled data will still only be a string | ||
AceComm.callbacks:Fire(prefix, olddata..message, distribution, sender) | ||
end | ||
end | ||
end | ||
|
||
|
||
|
||
|
||
|
||
|
||
---------------------------------------- | ||
-- Embed CallbackHandler | ||
---------------------------------------- | ||
|
||
if not AceComm.callbacks then | ||
AceComm.callbacks = CallbackHandler:New(AceComm, | ||
"_RegisterComm", | ||
"UnregisterComm", | ||
"UnregisterAllComm") | ||
end | ||
|
||
AceComm.callbacks.OnUsed = nil | ||
AceComm.callbacks.OnUnused = nil | ||
|
||
local function OnEvent(self, event, prefix, message, distribution, sender) | ||
if event == "CHAT_MSG_ADDON" then | ||
sender = Ambiguate(sender, "none") | ||
local control, rest = match(message, "^([\001-\009])(.*)") | ||
if control then | ||
if control==MSG_MULTI_FIRST then | ||
AceComm:OnReceiveMultipartFirst(prefix, rest, distribution, sender) | ||
elseif control==MSG_MULTI_NEXT then | ||
AceComm:OnReceiveMultipartNext(prefix, rest, distribution, sender) | ||
elseif control==MSG_MULTI_LAST then | ||
AceComm:OnReceiveMultipartLast(prefix, rest, distribution, sender) | ||
elseif control==MSG_ESCAPE then | ||
AceComm.callbacks:Fire(prefix, rest, distribution, sender) | ||
else | ||
-- unknown control character, ignore SILENTLY (dont warn unnecessarily about future extensions!) | ||
end | ||
else | ||
-- single part: fire it off immediately and let CallbackHandler decide if it's registered or not | ||
AceComm.callbacks:Fire(prefix, message, distribution, sender) | ||
end | ||
else | ||
assert(false, "Received "..tostring(event).." event?!") | ||
end | ||
end | ||
|
||
AceComm.frame = AceComm.frame or CreateFrame("Frame", "AceComm30Frame") | ||
AceComm.frame:SetScript("OnEvent", OnEvent) | ||
AceComm.frame:UnregisterAllEvents() | ||
AceComm.frame:RegisterEvent("CHAT_MSG_ADDON") | ||
|
||
|
||
---------------------------------------- | ||
-- Base library stuff | ||
---------------------------------------- | ||
|
||
local mixins = { | ||
"RegisterComm", | ||
"UnregisterComm", | ||
"UnregisterAllComm", | ||
"SendCommMessage", | ||
} | ||
|
||
-- Embeds AceComm-3.0 into the target object making the functions from the mixins list available on target:.. | ||
-- @param target target object to embed AceComm-3.0 in | ||
function AceComm:Embed(target) | ||
for k, v in pairs(mixins) do | ||
target[v] = self[v] | ||
end | ||
self.embeds[target] = true | ||
return target | ||
end | ||
|
||
function AceComm:OnEmbedDisable(target) | ||
target:UnregisterAllComm() | ||
end | ||
|
||
-- Update embeds | ||
for target, v in pairs(AceComm.embeds) do | ||
AceComm:Embed(target) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/ | ||
..\FrameXML\UI.xsd"> | ||
<Script file="ChatThrottleLib.lua"/> | ||
<Script file="AceComm-3.0.lua"/> | ||
</Ui> |
Oops, something went wrong.