[SmartFox Pro] SmartFoxTris PRO
[ July 29, 2005 ] by Marco Lapi, a.k.a Lapo
Article 8: follow the development of a complete turn-based game (tic-tac-toe) using server side extensions


(continues from page 1)

SENDING MOVES

As we said in the introduction, all the game logic is now moved on the server side while the client will only take care of the visualization. The central part of the client code is the extension response handler:

smartfox.onExtensionResponse = function(resObj:Object, type:String)
{
        var cmd:String = resObj._cmd
        
        switch(cmd)
        {
                case "start":
                _global.whoseTurn = resObj.t
                player1Id = resObj.p1i
                player2Id = resObj.p2i
                player1Name = resObj.p1n
                player2Name = resObj.p2n
                startGame()
                break
                
                case "stop":
                _global.gameStarted = false
                gamePaused(resObj.n + " left the game" + newline)
                break
                
                case "move":
                moveReceived(resObj)
                break
                
                case "specStatus":
                setSpectatorBoard(resObj)
                break
                
                case "tie":
                _global.gameStarted = false
                var win:MovieClip = showWindow("gameEnd")
                win.message_txt.text = "Tie!"
                break
                
                case "win":
                setWinner(resObj)
                break
        }
}

For each command coming from the extension we perform the appropriate action, for example if a "start" message is received we'll save the player names, ids and turn locally and call the startGame() function. If the "stop" action is received will halt the game and show a dialog window and so on...
The _global.whoseTurn variable will keep track of the player turn during the game, so that each player will know if it's his turn or not.

This is what we do when the game starts:

function startGame()
{
        resetGameBoard()
        
        hideWindow("gameEnd")
        
        if (!iAmSpectator)
        	hideWindow("gameMessage")
        else
        	hideWindow("gameSpecMessage")
        
        _root["player1"].name.text = player1Name
        _root["player2"].name.text = player2Name
        
        setTurn()
        
        _global.gameStarted = true
}

Once we have performed these very simple actions, the players will be able to click on the game board and send moves.
The code we use for sending the player move is this:

function moveDone(tile:MovieClip)
{
	var x:Number = Number(tile._name.substr(3,1))
        var y:Number = Number(tile._name.substr(5,1))
        
        var obj:Object = {}
        obj.x = x
        obj.y = y
        
        smartfox.sendXtMessage(extensionName, "move", obj)
}

If you remember, from the previous tutorials about this same game, we called each square tile of the board like this: sq_x_y, where x and y are their relative tile position on the X and Y axis. In order to know which board square was clicked, we take the 3rd and 5th character of the tile name and convert them to numbers. Finally we send this information to the server.

This is how the extension handles the "move" action:

function handleMove(prms, u)
{
        if (gameStarted)
        {
                if (whoseTurn == u.getPlayerIndex())
                {
                        var px = prms.x
                        var py = prms.y
                        
                        if (board[py][px] == ".")
                        {
                                board[py][px] = String(u.getPlayerIndex())
                                
                                whoseTurn = (whoseTurn == 1) ? 2 : 1
                                
                                var o = {}
                                o._cmd = "move"
                                o.x = px
                                o.y = py
                                o.t = u.getPlayerIndex()
                                
                                _server.sendResponse(o, currentRoomId, null, users)
                                
                                moveCount++
                                
                                checkBoard()
                        }
                }
        }
}

We have added some extra validations to avoid cheating: first we check that the game is really started, if not we'll refuse the request. Then we verify if the player who sent the move was allowed to do so, in other words if it's his turn. Once these two checks are passed we can finally store the move in the board array, using the playerId as value.

In the next lines we set the turn for the other player and send the move data and turn id to both clients. Also we keep track of the number of moves by incrementing the moveCount variable and we call the checkBoard() method to see if there's a winner or a tie.

The checkBoard() method is the same we've used in the past examples of this game: you may want to go back to those articles if something is not clear about how it works. The only difference you will find is that, if a winner or tie is found, a message will be sent to all clients:

var response = {}

// TIE !!!
if (winner == null && moveCount == 9)
{
        gameStarted = false
        
        response._cmd = "tie"
        _server.sendResponse(response, currentRoomId, null, users)
        
        endGameResponse = response
}
else if (winner != null)
{
        // There is a winner !
        gameStarted = false
        
        response._cmd = "win"
        response.w = winner
        _server.sendResponse(response, currentRoomId, null, users)
        
        endGameResponse = response
}


Receiving moves and client updates

As we've already seen the moves are received in the onExtensionResponse(). In this version of the game the method that handles the move update is called moveReceived():

function moveReceived(res:Object)
{
        _global.whoseTurn = (res.t == 1) ? 2 : 1
        
        setTurn()
        
        if (res.t != _global.myID)
        {
                var tile:String = "sq_" + res.x + "_" + res.y
                var color:String = (res.t == 1) ? "green" : "red"
                
                setTile(tile, color)
        }
}

This code is very similar to the one we've used in the past examples: we dynamically create a string with the name of the tile to set and pass it to the setTile() method. Also we set the _global.whoseTurn variable to the new turn id sent by the server side extension.


Conclusions

We've seen how to split the game logic from the game view by using a server side extension as the "game controller". There are many advantages in using this approach: better game code organization, better game security, better integration with external data, etc.

We suggest to analyze both client and server code to fully understand how they work and experiment with your own variations and ideas. Good work!


                      
 
 
Name: Marco Lapi, a.k.a Lapo
Location: Fossano, Italy
Age: 34
Flash experience: started out with Flash 4 back in 1999
Job: web designer/developer
Website: http://www.gotoandplay.it/
 
 
| Homepage | News | Games | Articles | Multiplayer Central | Reviews | Spotlight | Forums | Info | Links | Contact us | Advertise | Credits |

| www.smartfoxserver.com | www.gotoandplay.biz | www.openspace-engine.com |

gotoAndPlay() v 3.0.0 -- (c)2003-2008 gotoAndPlay() Team -- P.IVA 03121770048