8.8 Tutorials: SmartFoxTris PRO (p.2)
The source FLA of this
example is found under the Examples/mx2004/pro_smartFoxTris folder. |
» 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 luck!