Come2Play multiplayer API tutorial

Today I want to introduce you a Come2Play multiplayer API tutorial written by Emanuele Ornella from Mind the Move.

Emanuele is a Flash game developer and a board game designer too.

« After the development of my first two games Tokyo Train and Haunted house I am interested in development of a multiplayer game. For this reason I started to study the Come2Play API.

Links to the project: http://www.come2play.com/developer.asp

The goal of the tutorial is to show how to build a Tic Tac Toe game using these API.

The project can be downloaded from
www.mindthemove.com/blog/projects/TicTacToeC2P.zip

These API are basically used to have a communication between one or more clients (in Flash AS3) and the Come2Play server to allow a multiplayer game.

There are 2 type of functions: operations and callbacks.

I am going to use two classes.

- MainTTTC2P that extends ClientGameAPI. This is the main class the acts as controller

- TTTC2PViewer that is the viewer class. In some way, because the game is simple and it is for tutorial purpose, this will work as model as well.

I am not using an event driven approach as in the tutorials from come2play project because I found this confusing the main focus that is to learn how to use the basic API by come2play.

For any reference to the API this is the link: http://come2play.com/API_inner.asp?f=1&newsid=357

Let’s start from MainTTTC2P class.

This class is created in the first frame of the FLA file:

var game:MainTTTC2P = new MainTTTC2P(this)

The class is declared as:

public class MainTTTC2P extends ClientGameAPI

In the constructor I am going to get the calling movieclip and calling the super constructor (the constructor of the basic class ClientGameAPI.

Moreover I pass this movieclip to my viewer class that is created here.

The call to doRegisterOnServer is the way for the client to enter the lobby for this game. The call to this function should be done using AS3_vs_AS2.waitForStage function.

public function MainTTTC2P(stageMovieClip:MovieClip)
{
super(stageMovieClip);
theViewer = new TTTC2PViewer (stageMovieClip); 
keyMove= 0;
AS3_vs_As2.waitForStage(this,constructGame);
}// constructor
private function constructGame(){
doRegisterOnServer();//registers your game on the server
}

The first callback function that is called is gotCustomInfo. You need to remember that the callback functions inherited by the basic class ClientGameAPI must be declared as override.

1
2
3
4
override public function gotCustomInfo (infoEntries:Array):void
{
this.myUserId = T.custom(CUSTOM_INFO_KEY_myUserId,null) as int; 
}

On this function you get back some custom information. Calling the helper class T you can get some information back from the server. One simple example is to use the constant string CUSTOM_INFO_KEY_myUserId to get your user id. Because this is a important information it’s useful to get this stored.

Remember that each player will receive from the server a unique id to be identified. It’s critical for the client flash game to know the user id. In some how this is telling the program who is it.

The second callback function the client will receive is gotMatchStarted. This is triggered as soon as the game is started.

In this function the most important information is the list of the user ids for all the players. Again this is critical to know so this is stored in an internal variable.

I found useful as well store immediately where in this array the client is. This is done using the already stored information about my id.

Let’s have an example. If myUserId =41 and the array received is allPlayerIds=[41,42], this client has index 0 in the array while my opponent will have index 1. Therefore myUserIndex=0; This is another way to know who the client is, in terms of index in the array of all players.

This is the time where to start the new board using theViewer.startNewGame. The viewer class should also know what is myUserIndex, therefore I am going to store this on that class.

override public function gotMatchStarted (allPlayerIds:Array/*int*/, finishedPlayerIds:Array/*int*/, serverEntries:Array/*ServerEntry*/):void
{
this.allPlayerIds = allPlayerIds;
this.myUserIndex = allPlayerIds.indexOf(myUserId);
theViewer.startNewGame(this.myUserIndex);
addEventListener(Event.ENTER_FRAME,timerHandler);
setupFirstPlayer();
}

In this function I also start a loop using the ENTER_FRAME event. This is a function that will be called periodically with the fps set up in the FLA. This is a way to check if the viewer class has set up any new moves. This is an alternative to the event driven solution found in the come2play tutorials.

We will discuss this later.

The last function is used to define the first player. Because the game is in turns, only one player can make a move. Therefore this information must be set up.

A simple way is to define as starting player the first player in the index of all user ids. This information is stored in the currentturnIndexPlayer in the viewer class.

You need to think to the 2 clients that received the same array of the player ids (allPlayerIds). Therefore both clients have the user ids in order in this array: the first element of the array (allPlayerIds[0]) will be the same for both clients.

The serve must be notified by all clients about the current player. To do this each client must call the doAllSetTurn function. This is the way the server can be sure that all clients agreed to have one current player.

private function setupFirstPlayer() {
var firstPlayerIndex: int = 0;
theViewer.currentturnIndexPlayer = firstPlayerIndex;
doAllSetTurn(allPlayerIds[firstPlayerIndex],-1);
}

It is time now to see the viewer class.

In the constructor the movieclip is stored.

Then the board is created. I found useful to have a linear array of 9 elements instead of a double array 3×3. The index of the linear array will be:

0 1 2
3 4 5
6 7 8

In order to display an empty square or a square filled with the “x” or with the “o” I have created a Movie Clip symbol called Square and I have exported for Actionscript.

In the frame=1 there is an empty square. In frame=2 there is the “x”, while in frame=3 there is the “o”.

To create a new symbol it’s just enough to create a new instance of the class Square:

var squareNew = new Square();

Then I set up the Square movieclip in the right position and to the right frame (squareNew.gotoAndStop(EMPTYFRAME);). Then I add this new symbol to the stage and to an arrayOfSquares that I use to keep track of the clicked squares.

To be noted I have used a dynamic property called “myBoardIndex” to store the index of the movie clip in the board. For example the first movie clip (on the left top corner) will have myBoardIndex=0. The one in the center of the board will have myBoardIndex=4, etc

public function TTTC2PViewer (stageMovieClip:MovieClip)
{
this.stageMovieClip = stageMovieClip;
arrayOfSquares = new Array(9);
for(i=0;i<9;i++){
var squareNew = new Square();
squareNew.x=SQUAREPOSITION[i].x;
squareNew.y=SQUAREPOSITION[i].y;
squareNew.myBoardIndex= i;
squareNew.gotoAndStop(EMPTYFRAME);
squareNew.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
arrayOfSquares[i] = squareNew;
stageMovieClip.addChild(squareNew);
squareClickedIndex = NOCLICKEDSQUARE;
}
}// constructor

Each movieclip is registered with a MOUSE_DOWN event. The called function is clickHandler.

public function clickHandler(event:MouseEvent){
if (!isMyTurn()) return;
var clicked:Square = event.target as Square;
if ((arrayOfSquares[clicked.myBoardIndex].currentFrame)==EMPTYFRAME){
setSquare(myUserIndex,clicked.myBoardIndex);
squareClickedIndex = clicked.myBoardIndex;
} 
}

This function is immediately exit if it is not the turn of this client. This is done comparing the myUserIndex value with the currentturnIndexPlayer value.

public function isMyTurn():Boolean{
return (this.currentturnIndexPlayer==this.myUserIndex);
}

Then the target of the event is used to know which square was clicked. Only if the square is empty (that is checked using the currentFrame of the MovieClip then the square is changed calling the function setSquare (this function will be called to set the square also to set an opponent move). This is done simply using the procedure gotoAndStop to the playerIndex value (added by 2 because the first symbol “x” is to the frame 2 as you can see in the timeline for Square).

public function setSquare(playerIndex:int, squareIndex:int){
arrayOfSquares[squareIndex].gotoAndStop(playerIndex+OFFSETFIRSTSYMBOL);
}

The last action on the clickHandler function is to set the variable squareClickedIndex to the index of the board (value from 0 to 8). Normally this variable is set to -1 to indicate that no click is yet made. As soon as this variable is set to a different value, this indicates the place in the board where the click was performed.

This is the signal that the main class will check in the polling cycle inside the timerHandler. Actually there are 2 condition must be fulfilled: must be my turn and the theViewer.squareClickedIndex must be different than -1 (=theViewer.NOCLICKEDSQUARE).

public function timerHandler(event:Event):void {
if ( theViewer.isMyTurn() && (theViewer.squareClickedIndex!=theViewer.NOCLICKEDSQUARE) ) 
sendMoveToServer();
}

If so the sendMoveToServer is called. The main purpose of this function is to send to the server the message that a move was made by the current player using the function doStoreState.

public function sendMoveToServer(){
var myKey = this.myUserId+"_"+this.keyMove;
var userEntry:UserEntry = UserEntry.create(myKey,theViewer.squareClickedIndex,false);
var userEntries:Array = new Array();
userEntries.push(userEntry);
doStoreState(userEntries);
theViewer.squareClickedIndex = theViewer.NOCLICKEDSQUARE;
keyMove++;
}

The doStoreState function needs only one parameter that must be an Array of type UserEntry. Actually the array can contain only one element.

To create a variable of type UserEntry the static function create is used:

UserEntry.create(theKey,theValue,isSecret) where:

- theKey must be unique if you want to store a new message. If you use a key already present this will override the previous one.
- theValue is the actual message that you want to send
- isSecret indicates if the other clients will be allowed to see the message sent

Both theKey and theValue can be any Object. There are different way to choose how to create the key and the value. My choise was the following:

For the key I created a String of this type myUserId_keyMove where keyMove is incremented at each move (at each click). So for example with player with id=41 the first key sent will be 41_0, the second 41_1 etc… While for the opponent (with id=42 for example) will be 42_0, 42_1, etc…

The value will be simply the index of the board where the click was performed. This is the critical information to send to the opponent so that the opponent can display the square with the right symbol.

After sending the message to the server , the incremental keyMove variable is updated. And the theViewer.squareClickedIndex variable is set to -1 so that next time (and when it will be again my turn) it will be possible to click again.

Now that the message is sent let’s suppose to move to the opponent client. This client must receive the message and display the opponent’s symbol on the board on the right position (board index).

This is done in the gotStateChanged function where the server sends to the client the serverEntries array containing the moves in terms of ServerEntry elements.

Because we sent only 1 message, only the first element serverEntries[0] will be used.

In this variable the property storedByUserId is the id of the client who sent this message. Because the client who actually sent the message is also receiving this information, we need to check if the message was sent by the client itself. Only if this was sent by the opponent the function theViewer.setSquare is called to display the opponent symbol.

While the client who sent the message is not doing this simply because we already set this on the clickHandler function. That was a choice. An alternative would have been to set the symbol for the client itself after he received back the gotStateChange event from the server.

override public function gotStateChanged(serverEntries:Array):void
{
var serverEntry:ServerEntry = serverEntries[0];
if (serverEntry.storedByUserId != this.myUserId){ theViewer.setSquare(allPlayerIds.indexOf(serverEntry.storedByUserId),serverEntry.value); }
setNextPlayerTurn();
// CHECK if the game is over
var winnerIndex:int = theViewer.winnerPlayer(); 
if (winnerIndex>-1) { 
setupWinner(winnerIndex);
}
}

In any case the setNextPlayerTurn is called. First thing the currentIndexPlayer is updated with the next player. This is simply done adding 1 and set back to 0 the index in case it’s greater than the allPlayerIds array length.

private function setNextPlayerTurn(){
theViewer.currentturnIndexPlayer++;
if (theViewer.currentturnIndexPlayer >= allPlayerIds.length)
theViewer.currentturnIndexPlayer = 0;
doAllSetTurn(allPlayerIds[theViewer.currentturnIndexPlayer],-1); 
}

Then the doAllSetTurn is sent to the server with the id of the new active player. It is also possible to set a maximum number of millisecond for the move (or -1 to ignore this).

This soAllSetTurn function must be called by all the clients to have any effect. And this is the reason why the gotStateChange function this is called from both clients. This is also useful to have both clients updated in terms of current player index.

The last action in the gotStateChange is to check if the game is over. If so the setupWinner function is called.

The main purpose of this function is to call the doAllEndMatch function to inform the server the game is ended. Again, as all the doAll…. functions, this must be called by all the players.

private function setupWinner(winnerIndex:int){
var finishedPlayers:Array = new Array();
var playerMatchOver:PlayerMatchOver;
for (var i:int=0; i
if ( i==winnerIndex ){
playerMatchOver= PlayerMatchOver.create(allPlayerIds[i],100,100);
}else{
playerMatchOver= PlayerMatchOver.create(allPlayerIds[i],0,0);
}
finishedPlayers.push(playerMatchOver);
}
doAllEndMatch(finishedPlayers);
}

The parameter sent to the server must be an array of type PlayerMatchOver. Each element must contain the id of all players that have finished the game. In our case both players end the game simultaneously.

Therefore there is a loop in the allPlayeIds array to create a variable of type PlayerMatchOver. Again the static method “create” is used with 3 parameters:

- userId
- theScore. Set to 100 if the player wins and 0 if the player looses.
- thePot. The pot is used if the game allows players to bet during the game. In our case this will be 100% if the player wins, and 0% if the player loses. »

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (9 votes, average: 5.00 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 11 comments

  1. Monkios

    on January 14, 2010 at 8:14 pm

    Nice tut.

    What is a Callbakx ?

  2. Anthony

    on January 17, 2010 at 4:46 pm

    A callback is a method that is performed after the calling method is completed. Correct me if I am wrong. I am guessing the gotCustomInfo() method is a method that is called within the api once the code is finished. I am guessing gotCustomInfo() was meant to be overridden so the programmer can do what they want. Ive never heard of as2_vs_as3 function before. I am guessing these are all methods included in the api

  3. Kurak

    on January 19, 2010 at 12:59 pm

    link problem (come2play.com[..]) – just to let you know

  4. Games that Challenge the World Come2Play contest – $8,000 in prizes : Emanuele Feronato - italian geek and PROgrammer

    on January 25, 2010 at 2:30 pm

    [...] Do you want to make something interesting out of the Come2Play multiplayer API tutorial? [...]

  5. Come2Play multiplayer API tutorial « Dobos Bence

    on January 26, 2010 at 6:07 pm

    [...] by dobosbence in Uncategorized. Tagged: Come2Play, multiplayer, tutorial. Leave a Comment Come2Play multiplayer API tutorial at [...]

  6. Dnx

    on February 10, 2010 at 3:48 am

    Very Nice! Amazing :) thanks

  7. Monetize your Flash games with GamesChart : Emanuele Feronato - italian geek and PROgrammer

    on February 23, 2010 at 12:23 am

    [...] you remember Emanuele Ornella from Mind the Move? It’s the guy behind the Come2Play multiplayer API tutorial posted about a month [...]

  8. pisces_eyes

    on March 26, 2010 at 5:31 am

    I think callback functions are triggered/called automatically whenever the server sends data to clients.

  9. Sparky

    on May 10, 2010 at 10:04 am

    Great tutorial! Thank you!

  10. Upcoming Flash Game contests | blog.sokay.net | flash game development discussion

    on February 7, 2011 at 7:56 am

    [...] You can check out the documentation to get started and Emanuele Ornella wrote a tutorial for implementing the Multiplayer Api that might be worth checking out as [...]

  11. hicham

    on January 19, 2012 at 10:51 am

    General question, can we use this for a fast paced gamed that requires a low latency? I am looking for a way to do that in flash , I am thinking of using sockets and ended up here.