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:

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

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:

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)
theViewer = new TTTC2PViewer (stageMovieClip); 
keyMove= 0;
}// 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.

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);

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;

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);
var squareNew = new Square();
squareNew.myBoardIndex= i;
squareNew.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
arrayOfSquares[i] = 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 = as Square;
if ((arrayOfSquares[clicked.myBoardIndex].currentFrame)==EMPTYFRAME){
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){

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) ) 

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();
theViewer.squareClickedIndex = theViewer.NOCLICKEDSQUARE;

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); }
// CHECK if the game is over
var winnerIndex:int = theViewer.winnerPlayer(); 
if (winnerIndex>-1) { 

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(){
if (theViewer.currentturnIndexPlayer >= allPlayerIds.length)
theViewer.currentturnIndexPlayer = 0;

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);
playerMatchOver= PlayerMatchOver.create(allPlayerIds[i],0,0);

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. »

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers