Introduction
If you have written a nice multiplayer game, which can be played only by users on the same computer (as you never had time to even start thinking on making it online) – this article is for you. It presents a simple approach for all those “networking” tasks, and comes with a simple library implementing it. So make your game online in five minutes (well, probably two hours).
Contents
Making Online Games
For this discussion, I want to divide this task to two separate parts:
- Create desktop game, and
- Allow users to play online
The first part is interesting and difficult by itself, but is totally out of the scope of the current article. For example, for chess game, it includes UI and game engine that will allow two people to play chess on the same computer. For my demo application, I use great chess board control, created by Gregory Prentice. In addition to the nice UI, it has a fully functional chess engine which validates the moves, fires events both for UI changes (move) and game notifications (Mate!), and so on. You can read its description here, or download the most recent version from here.
Let’s look deeper into the second task – going online. Here, we should look separately at:
- connecting users, and
- supplying additional online services
Opening connection between two users:
- server-less. Connect directly to other user’s IP? Ugly and won’t work in many cases, where the user does not have external IP address (you can’t play at work!). And this is just for finding the opponent IP, take into account the firewalls, and you will see that this solution is useless in many cases.
- with dedicated server. Obvious advantages: all users connect to known gaming server, they can see who is online, etc. Building such server from scratch is very problematic and time-consuming task. Even if you are using some existing code (for example, I saw online games based on IRC protocol), you need to manage an online server with known IP address – not something each one wants to do.
Additional services include chat window, which is a must for a game, and optionally other communication channels – voice over IP, video, etc.
Well, currently this all seems very complicated.
Using Skype as Infrastructure
As you can already guess, this paragraph will explain how Skype can ease all those tasks.
Skype
I started using Skype a couple of years ago for VOIP – talking to my friends and family in different countries in great sound quality. Over time, they added chat and video functionality, including conference calls.
Similar to other programs of this type, you create Skype account, manage list of contacts, see when each of your friends is online, etc.
Skype API
In addition to those features, there’s a Skype Public API, which allows developing new application based on Skype capabilities. The API gives full access to all basic functionality: contacts, chats, voice calls, etc. The current solution is mostly based “Application Streams” – data channels which can be created between 3rd-party applications.
The next section describes Skype
API in more detail. But the concept should already be clear – we will use those data streams to exchange game-specific info between players which will be all connected to Skype. This approach solves most of the difficulties in making online games:
- All users are logged in to Skype servers and you don’t need to worry about the network layer but can use reliable connection to exchange data
- You always know who is connected to Skype at any moment
- Chat window, voice conversations and even video calls are available in Skype
Using Skype API
This section describes basic Skype API and a COM API, when special attention is given to topics related to “Application Streams”. The API is well documented, and can be found here. You can also use developers' forum to find answers on subjects that are not explained well in the documentation.
In the next section, you can find description of SkypeGameInfra
library, which wraps all relevant functionality in a convenient way. You can even skip the current section for now, and go back to it only when you need to get more details, or make changes.
Skype API for Windows
Application exchanges data with Skype using WM_COPYDATA
Windows messages. The format of the messages is called “Skype Protocol”, which defines set of commands that can be sent to Skype. The full description of commands can be found here.
Skype4COM
In addition to Skype API, which isn’t very friendly to use, there’s a COM wrapper which represents the Skype API as COM objects with properties and events. Again, the full description can be found here. I will describe the parts relevant for creating “Application Streams”.
- Skype is the top-level object. To create an instance:
SKYPE4COMLib.Skype skype = new SKYPE4COMLib.Skype();
This operation will pop-up a message in Skype, saying to the user that new application is attaching. The user should allow this operation (it’s recommended to use the “never ask again” option).
Note: If, by mistake, you’ve disabled your application from using Skype, you can go to C:\Documents and Settings\WINDOWS USER NAME\Application Data\Skype\SKYPE USER NAME, and find your application in config.xml, UI/AccessControlList/ClientX node – just delete this whole node (make sure the numbering of Client nodes remains correct).
- In order to check whether you could connect to Skype, test
AttachmentStatus
property:
SKYPE4COMLib.ISkype iskype = skype as SKYPE4COMLib.ISkype;
if (iskype.AttachmentStatus ==
SKYPE4COMLib.TAttachmentStatus.apiAttachNotAvailable)
{ }
- Next, you have to register your application. Each “application” connected to Skype has a unique name, by which it can be identified.
SKYPE4COMLib.Application app =
skype.get_Application(APPLICATION_NAME);
if (app != null) {
app.Create();
}
- And we also need to register on two events:
ApplicationStreams
and ApplicationReceiving
.
skype.ApplicationStreams += new
SKYPE4COMLib._ISkypeEvents_ApplicationStreamsEventHandler(
skype_ApplicationStreams);
skype.ApplicationReceiving += new
SKYPE4COMLib._ISkypeEvents_ApplicationReceivingEventHandler(
skype_ApplicationReceiving);
- When you are ready to open connection to another Skype user, just call:
app.Connect(skypeHandle, false);
Note: The connection is received by application registered with the same APPLICATION_NAME
as your application. If no such application was registered, no connection will be opened.
- When the connection is finally established, you will receive
ApplicationStreams
notification, and from now on, you can use the stream to send and receive messages.
void skype_ApplicationStreams(SKYPE4COMLib.Application pApp,
SKYPE4COMLib.ApplicationStreamCollection pStreams)
{
SKYPE4COMLib.ApplicationStream stream = null;
foreach (SKYPE4COMLib.ApplicationStream
astream in pStreams)
if (stream == null)
stream = astream;
}
- To send a message, just call:
stream.Write("My message");
- And, finally, messages are received through
ApplicationReceiving
events:
void skype_ApplicationReceiving(SKYPE4COMLib.Application pApp,
SKYPE4COMLib.ApplicationStreamCollection pStreams)
{
foreach (SKYPE4COMLib.ApplicationStream
astream in pStreams) {
string msg = astream.Read();
}
}
As an example, see the GameCommunications
class in the SkypeGameInfra
project.
Skype Contact List Control
In addition to those APIs, you can use one of two ActiveX controls to automatically get list of contacts defined in Skype: “SkypeContactList
” and “SkypeContactCombo
”. The use of those controls is pretty self-explanatory – just add one of them to your Form
.
They can be downloaded from here - “Skype ActiveX Tools”.
SkypeGameInfra Wrapper
It’s .NET wrapper on top of Skype COM API which encapsulates all functionality needed for a game between 2 players.
DemoSkypeChess
is an example project which uses this API.
- Creating new instance:
SkypeGameInfra.GameCommunication skypeWrapper =
new SkypeGameInfra.GameCommunication("DemoChess");
Note: Replace “DemoChess
” with your unique name.
- Let’s say the user is connected as Alex in Skype. To invite other Skype users to a game, call:
string username = skypeWrapper.SelectUser();
if (username.Length > 0)
skypeWrapper.InviteOpponent(username);
SelectUser
opens a dialog with list of all contacts, and the user should select one of them. InviteOpponent
opens “Application Stream”, and sends an invitation to selected contact.
- At this point, your application on the remote computer (let’s say, Bob’s), which has also performed step 1 of creating
GameCommunication
object with the same name, will receive InvitationReceived
event. In the application level you will, probably, want to show a nice dialog to the user, asking to accept or decline the game. - Based on Bob’s action in step 3
AcceptGame
of DeclineGame
should be called, and Alex will receive OpponentAccepted
or OpponentDeclined
event. Starting from this point, the communication channel is established: Alex plays white, and Bob – black. - Each player in his turn should make a move, resulting in the following call:
skypeWrapper.Move(position);
position
parameter is arbitrary game-specific string
, which will be received by the other side in OpponentMove
event.
- Finally, the game ends when one of the parties is invoking
StopGame
function. This will release the “Application Steam” channel for both users, and bring the system back to the state it was after step 1.
Links
Chess control by Gregory Prentice:
Skype links:
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.