AceComm-2.0 Tutorial

From WowAce Wiki
Jump to: navigation, search

The AceComm library allows your addon to communicate across several channels. This Tutorial is meant to give you a basic idea how to set up your addon to use AceComm and how to send and process messages.

This tutorial assumes that you have a basic understanding of Lua, the WoW API and the Ace2 library structure. If this is not the case, I recommend reading this Ace2 tutorial by PProvost first.

Basic Setup

First of all, you'll have to download AceComm (preferrably from the SVN), put it in your addon's folder and include the line

Libs\AceComm-2.0\AceComm-2.0.lua

in your toc-file. Remember to put this line after the inclusion of AceLibrary and AceOO, but before any of your own Lua files.

AceComm is a mixin library, so you have to tell AceLibrary that AceComm should be included in your addon (let's call it "ChitChat":

 ChitChat = AceLibrary("AceAddon-2.0"):new("AceComm-2.0", ...)

"..." stands for any other libraries you may need.

Every message has a (hopefully) unique prefix specific to the addon. It is handy to save this prefix in a variable so you can use it throughout your addon but can easily change it whenever you want. In most cases, your addon's name will suffice. If it is rather long, don't mind, it will be shortened automatically. We will come to this shortening-mechanism called "Memoization" a bit later on.

 ChitChat:SetCommPrefix("ChitChat")

Registering for channels

Next, you should decide which channel(s) your addon has to use. For reference which channels are available with AceComm, read the Distribution Section in the API. When you have decided which one to use, you have to tell your addon to listen to this channel by invoking the function RegisterComm(prefix, distribution). It is a good idea to do this in your OnEnable function.

Example:

function ChitChat:OnEnable()
    self:RegisterComm(self.commPrefix, "GROUP", "ReceiveMessage") -- note the use of the previously defined commPrefix here
end

Now, whenever your game client receives a message in the "GROUP" distribution with the prefix "ChitChat", the function "ChitChat:ReceiveMessage" is invoked. You may omit the name of the method, then AceComm will call the function ""ChitChat:OnCommReceive". If you do this, remember to define the function or else you will get a 'nil' error ingame. Keep in mind that the "GROUP" distribution is not always the same. When you don't have a group, you don't send or receive messages from it. When you are in a battleground, you will actually send and receive in the distribution "BATTLEGROUND". When you are in a raid, it's the distribution "RAID", and finally "PARTY", when you are in a PARTY. If your addon only makes sense in a battleground, raid or small party, by all means, use the specific distributions for these to cut down chat traffic.

Note that you can unregister for channels to save ressources, but you don't have to do this in you OnDisable function because AceComm will automatically unregister all channels for you when you disable your addon.

In order to recieve WHISPER you will need to register a comm also. Use the "WHISPER" for distribution method.

Sending messages

Now, whenever you need to send a message, you can invoke

 self:SendCommMessage("GROUP", chit, chat, morechitchat)

this command will send the values of the variables chit, chat and morechitchat to the "GROUP" distribution. If you want to be sure that the message was successfully sent, you may be happy to hear that it returns a boolean: "true", if everything went ok and "false" if something bad happened (probably you forgot to register the distribution).

You may send up to 20 values, let it be strings, tables, booleans, numbers, anything. Be aware though, that every argument after the first "nil" (starting with this "nil") is not sent. Use "false" instead.

It may also save you some frustration to know that the sender of a message in the "GROUP" distribution does not receive this message himself. If you need to receive your own messages, combine the "GROUP" broadcast with a whisper to the player:

self:SendCommMessage("WHISPER", UnitName("player"), chit, chat, morechitchat)

If you send messages in several locations in your code, I recommend setting up a wrapper function to combine the "GROUP" and "WHISPER":

function ChitChat:SendMessage(arg1, arg2, arg3, ...)
   local groupSuccess = self:SendCommMessage("GROUP", arg1, arg2, arg3, ...)
   local whisperSuccess = self:SendCommMessage("WHISPER", UnitName("player"), arg1, arg2, arg3, ...)
   return (groupSuccess and whisperSuccess)
end

Replace "..." with as many arguments you need. By using this wrappperfunction, you a) save yourself some lines of code and b) won't forget a "WHISPER" that would cost you a while figuring out where the bug is.

Receiving Messages

Now you have sent some messages, but nothing happens - yet. Remember the "ReceiveMessage" argument to self:RegisterComm? This is the function that gets called whenever the client receives a message with the right prefix ("ChitChat"):

function ChitChat:ReceiveMessage(prefix, sender, distribution, arg1, arg2, arg3, ...)
  --do some chitchatting
end

It's not hard to figure out what the arguments of this function mean: "prefix" is in most cases unimportant to you. "sender", the originator of the message and "distribution", self-explanatory. The other arguments are a copy of the values sent by SendCommMessage. So, basically, whenever you call

self:SendCommMessage("GROUP", "someString", 3.1415, aTable)

you call

self:ReceiveMessage("ChitChat", "yourName", "GROUP", "somestring", 3.1415, aTable)

on every other client that receives the message. Easy, huh?

By separating your messages into single arguments, you don't even have to parse the string you receive. You just use these arguments as you like.

Advanced Usage

If you work on a communication-heavy addon, there are a few useful things you should know:

Memoization

One goal of every addon should be to use as little memory, bandwidth and CPU time as possible. In the case of communication, this means cutting down the length of the sent messages to save bandwidth. Fortunately, AceComm has a mechanism for this called "Memoization". I mentioned it above when we defined our prefix. I didn't write AceComm, so I can just guess how this works, but you can imagine it like abbreveations. Instead of "ChitChat", only "ch" is sent. Another client receives this "ch", doesn't know immediately what to do with it and looks it up in some table provided by AceComm: ["ch"] = "ChitChat" is found, et voila! The client gets the idea and calls ReceiveMessage("ChitChat", ...).

But memoization is not limited to the prefix. AceComm defines the function RegisterMemoizations. It takes a table of strings as argument and memoizes them. Whenever you send one of these strings, it will be automatically abbrevieated before sending and reconstructed at receive:

self:RegisterMemoizations {"shortenMe", "shortenMeEvenMore", "makeMeTiny"}

Note that you may only call this once, before you have sent any messages. So it is not suitable to dynamically shorten strings such as playernames you learn while the addon is running. But it is a good idea to memoize everything that you define as some kind of constant, or the name of the player himself. Everything that doesn't change.

Consistent Argument List to avoid TypeChecking and Guessing

It's generally a good idea to always use the same argument-pattern throughout your addon. Let's take my addon Druidcom for example. It's a tool for druids that displays the innervate and rebirth cooldowns of every other druid in the raid. It also let's the druid leader (the one promoted by the leader) send requests to other druid such like "Please innervate Nimbal". The argument list for ReceiveMessage looks like this:

Druidcom:ReceiveMessage(prefix, sender, distri, innervateCD, rebirthCD, command, recipient, target)

innervateCD and rebirthCD are the respective cooldowns in seconds. Command is either "refresh", so everyone is sending an update on their cooldowns and the list stays in sync, or "innervate" / "rebirth". If one of the last ones, the druid named in the argument recipient is notified that he should innervate/resurrect the player named in the argument target. However, when the druid leader sends a request, it is not necessary that he sends his cooldowns at the same time. But instead of calling

Druidcom:SendCommMessage("GROUP", command, recipient, target)

where I'd have to typecheck the first argument in ReceiveMessage (if string, it's a command - if number, it's a cooldown), I rather call

Druidcom:SendCommMessage("GROUP", false, false, command, recipient, target)

This way, I can safely use all arguments in ReceiveMessage without worrying which is which.

In short: give your arguments in the OnCommReceive function a meaningful name (not just arg1, arg2, arg3 and so forth), keep the argument list consistent throughout your addon and if you tend to forget things (like me), write the name and the ordering of your arguments down and keep that note visible on your desk. By doing this, you don't have to scroll through hundreds of lines of code to find your OnCommReceive everytime you want to send a message.

Priority Messages

The WoW client allows only that amount of chat traffic, therefore it is necessary to throttle the communication. AceComm does that for you, so don't worry. But what if you have that bulky long novel your addon sends to another player that takes several seconds? It blocks the channel for this time, which is bad if there is an important message (low health, for example) waiting to be sent. AceComm has a solution for this called "Prioritizing". You may send a message with either one of three priorities: "BULK" (can wait), "NORMAL" (default) and "ALERT" (life and death). Use SetDefaultCommPriority(priority) to change the priority for every message you send. If some messages need another priority than the default one, use SendPrioritizedCommMessage(priority, ...) with "..." being the same arguments as provided for SendCommMessage.