This article was written and submitted by an external developer. The Google Desktop Team thanks Jeremy Glassenberg for his time and expertise.
The Communication API is a little known API feature that really sets Google Desktop Gadgets apart from other desktop widget providers. Through this system, developers can easily create gadgets that enable users to interact with each other. A very popular use for this is in creation of multi-player games. Moreover, it's a great platform for learning how to create multi-player games since the API is so easy to use.
Besides being fun and interactive, any gadget that uses the Communication API is arguably at a better position for growth through the use of viral marketing. Users of such a gadget could share the gadget with their friends, further spreading its adoption.
For this article, you may want to look at the code for the multi-player Tic Tac Toe gadget. This is a simple example of a fully working multi-player game and can be a good reference when you create your own Communication API gadget.
Gadgets that use the Communication API require not only the Google Desktop Sidebar, but also the Google Talk client (GTalk) to be running in the background. In short, these are the requirements:
Keep this list in mind, not only for the purposes of getting your gadget running, but for error-handling as well. In particular, your gadget should not be throwing errors when Google Talk isn't running.
In order to use the Communication API, have the gadget run the following line upon loading:
// Set up Google Talk data handler googleTalk.onReceiveTalkData = yourOnTalkFunction;
yourOnTalkFunction
is the function
that is called whenever data is received. For more information on
how this function works, see the Receiving Data section
later in this article.
In order to send data, your gadget must first identify a recipient. The best way to do this is by retrieving a list of GTalk contacts (friends) who are currently online. We make a call like this:
// Get an array of friends var friends = googleTalk.friends.toArray();
The above code obtains a list of Google Talk contacts.
These friends
are everyone in the user's contact list who are
currently online. However, this also includes friends who do not have the GD Sidebar and
therefore can't use our
gadget.
These contacts can still receive regular text messages sent by our gadget, but obviously can't participate in our game.
Let's look at the properties belonging to the
friend
object:
user_id
and has_sidebar
, but is still very useful in order to give your users the
best experience.
See the
official
API reference for more information about the friend
object.
A very important thing to note is that you cannot directly determine who already has the gadget running. More details on this issue are available in the Remaining Unobtrusive section.
With this knowledge, you can now filter the list of contacts. This can be accomplished with a for-loop similar to the one below:
for (var i = 0; i < friends.length; i++) { if (friends[i].has_sidebar){ //You found a contact who can use your gadget. Add friend[i] to a list of potential contacts } }
The above code can be extended to display a selectable list of friends in the gadget's UI.
NOTE: The Tic Tac Toe gadget has an example of a simple contact display list that shows the usernames and statuses. However, the Tic Tac Toe gadget will only display a limited number of contacts and is not scrollable if the list is long. You may want to check out the article on lists and scrollbars for further advice on this issue.
Once a friend is selected, it is very helpful to store
friend.user_id
for future use.
The other entities may help for display purposes, but are not
necessary once a contact is selected. Also, keep in mind that some values such
as friend.status
will change over time.
Now that a particular contact is selected, the gadget can interact across two users. In order to accomplish this, communication gadgets rely on a simple three step process for handling data transmission - sending data, receiving data, and managing the received data.
Communication is initiated when one client sends data to another. This can be done fairly simply, with the following code:
googleTalk.sendTalkData(friend_id, data)
The first parameter, friend_id
, is the
id of the user to whom you are sending data. These ids can be obtained
from the list of friends
. See the
Finding a
Contact section for more information. The second parameter is the
data being sent.
This function can be called at any point in your code. Multi-player games send these calls to accomplish many things: to invite a player, signal the end of a turn, or when someone exits prematurely. However, be prepared for various errors such as network disconnections. I recommend using a try-catch clause similar to the following:
try { googleTalk.sendTalkData(friend_user_id, messageStartGame); } catch (e) { alert(strConnectionError); //define an error message for this //Call a function to go somewhere else. }
Please be aware that the above method of sendTalkData
may be an inconvenience
if you send data to a user who does not already have the gadget. For
more information on this issue, check out the section on
Remaining
Unobtrusive.
Once data is sent to the other user, the receiving gadget calls
the handler function for
googleTalk.onReceiveTalkData
defined
earlier in your gadget. The handler function definition should look
something like this:
function OnReceiveTalkData(friend, data) { }
The first parameter is a
friend
object, to let you know who sent the data. The second parameter is
the actual data transmitted.
NOTE: The data
parameter can be of any
variable type (string, int, bool, object, etc), and because all received data
messages have to go through the same handler function, you may receive
different variable types for the second parameter. More information on
this is available in the next section.
So, someone sent data to a friend's gadget, and
onReceiveTalkData
was called.
What can you do with this?
You'll want to know who sent you the data, what is contained in the data, and then decide what to do with that information.
First, it is important to find out who sent this data. To figure that out,
you can obtain friend.user_id
or other
friend
properties. Perhaps this gadget is a game, and someone is inviting you to
play. In that case, the gadget can prompt the user to play the game with
this friend.
However, the user may already be playing another instance of the game with someone else, in which
case the gadget should confirm whether the friend.user_id
of the person who just
sent data to this user is actually the person who is playing on the other
end. If you would like helpful advice on what different situations to
consider, go to Additional Advice - setting gadget states.
The data
variable should be used to
determine the next function call. All data will be sent to the same
receiving function, so this function would be best organized to figure out the
data content, and decide where to go from there. Perhaps the data was an
invitation to play a new game, or if the game is in play, a message indicating that the
player has forfeited. For these cases, the messages can be strings with
certain pre-defined messages such as "start?" or "quit". Just remember to
create a conditional (if statement) for each type of message you can
receive.
function OnReceiveTalkData(friend, data) { if (data == "start?"){ opponent = friend.user_id; startGame(); } else if (data == "nothanks"){ goToInviteFriend(); } else if (data == "quit"){ setGameOver(); goToHomePage(); } }The data may also be numerical or custom objects. For example, many multi-player board games indicate when an opponent moved a piece from one board location to another. It is for this reason that you may want to familiarize yourself with the JavaScript
typeof
function. You can first check
if the data is a string, indicating something like a "quit" message, or a number
indicating the new coordinates of a board piece.
Gadgets that use the Communication API are exceptionally difficult to test. The need to test these gadgets across multiple computers alone makes testing complicated. Moreover, the Google Gadget Designer currently does not function well with gadgets that use this API. Therefore, the following process may be necessary to test and debug any communication errors.
First, because the Google Gadget Designer cannot work with this API, you will need to fully build your gadget, install it onto Google Desktop, and run it from there in order to perform tests across computers. That is, rather than use the "Preview" tab in the Designer, go to Project -> Build Package, and install the package onto Google Desktop.
You will also need a second computer with Google Desktop, Google Talk, and your test gadget. In addition, you will want to have an extra Google account to log into Google Talk on your second computer. The accounts of these two test users must be in each other's contact lists so that Google Talk can communicate between them easily.
Now that the environment is setup, you're ready to perform all the test cases.
to verify that the data is sent and handled.
I recommend adding some alert statements or other
break-points within your onReceiveTalkData
function, and also near any sendData
functions to verify that the
data is sent and handled.
Test out all cases that you can think of. For example, multi-player games must be able to handle the acceptance of a friend to a challenge, movement of pieces, and graceful exits for all cases (be it different ways of one user winning or a user forfeiting a game). Many other issues must be considered, such as users rejecting challenges to a game or interruptions by other contacts.
You'll also want to test network disconnections during any potential communication.
For this, it is worth noting that sendData
can give an error when the sender is not connected on Google Talk, but does not raise an error when the receiver is disconnected.
As you continue to build on your gadget, you will run into an unexpectedly long list of messages and exception cases to deal with. This section helps identify many common issues before they arise.
Remaining unobtrusive when sending data
The original version of the Communication API only allowed for one means of sending data:
sendTalkData(friend_id, data)
The problem with this function is that you were able to send data to anyone in your Google Talk contact list, whether or not they have the gadget. Users who don't have the gadget are prompted to install it. This feature is a nice way of gaining new users by having friends encourage friends to join in, but it can also be an inconvenience. You certainly don't want to encourage friends to sign up if you accidentally invite them to a gadget that you are still testing!
In November 2006, this function was updated and takes a third parameter:
sendTalkDataEx(friend_id, data, flags)
This allows developers to customize how GD handles received data. By
adding gddSendDataFlagSilent
as a flag, users who do not have the gadget
will not be prompted to add it. The data will simply be ignored.
An important factor to deal with in Communication gadgets, especially as seen in multi-player games, is that the gadget will have to respond to received data differently depending on what the user is doing. For example, a user who is currently playing a game against another friend may not want to receive an invitation to play against someone else until the game is over.
These situations can best be managed by creating a global variable to store the "state" of the gadget. For a game, the gadget may have a "playing" state. Then, if the gadget state is set to "playing" and an invitation comes in, the gadget should send a message back indicating that the user cannot play another game.
The Tic Tac Toe gadget has such a variable to help determine when a user is playing, or is on the main menu, etc. However, it does not take into full consideration how these states affect received data. Tic Tac Toe is a simple example, so many such issues are not addressed. I therefore recommend checking out multiplayer.js within the code for Strategy Chess, available here.
Handling responses when inviting a friend to play a game
Multiplayer games require users to invite friends to play at the initial setup. Getting two players to agree to play may take more messages than expected. This section should help game creators get this system set up faster.
The first type of message would be an invitation to play. Once a message is sent, the user should wait for a reply. In the simplest form, this can be "accept" or "decline" as done in Tic Tac Toe. Other games include "can't play" which occurs when the user invited someone who is currently playing a game.
Once a friend is invited, the gadget should go into a "waiting" state where the gadget is waiting for the friend to reply. I recommend setting this state so that the inviter cannot be contacted while inviting another friend and can only receive a reply from that friend. This can prevent many other problems, such as friends all contacting each other to play, then getting confused about who is playing against who.
This state should also allow for the user to cancel this invitation request. Remember, when a message is sent, it is possible that the other user will just never respond, so we must allow a graceful exit. If canceled, the gadget should also notify the other player of the cancellation. Code to handle these sorts of cases can be found in the multiplayer.js file of the Strategy Chess gadget code.
This article is just a summary of the Communication API — a simple, yet powerful way to allow "talking" between gadgets. Just set up Google Talk in your code, find a friend, and send/receive data. These instructions should help you get a basic idea of how all these functions work together and get a Communication Gadget up and running in no time.
I am currently an MBA student at Carnegie Mellon University. I have a B.S. Degree in Computer Science from UIUC. Prior to grad school, I worked as an intern at Salesforce.com, Microsoft, Northwestern University, and Technion. More recently, I worked for web startups in Pittsburgh: mSpoke and GeoNexxus.
Favorite Quote: "Come on, guys. If we don't try now, we'll never know how horribly they can beat us. That's the sort of thing that can haunt you." -Homer J. Simpson
This work is licensed under a
Creative Commons Attribution 3.0 United States License.