TGB/Tutorials/NetworkTicTacToe
From TDN
Network TicTacToe Tutorial
This is an extension of the simple tutorial for TicTacToe, this shows how to accomplish it over a network connection.
1) First, please do the TicTacToe Tutorial to understand the basics of it, and also for the images to utilize.
2) Start a new project, and if possible have a debug version or two computers available for testing.
For the sake of keeping it simple, I used all of the images from the TicTacToe Tutorial.
Also, since I'm not very familiar with make wiki pages, I'm just going to show the code parts.
Let's start with the first thing to do for network games. Go to your game directory, and under common\gamescripts\common.cs add a line under UsesCommonVersion:
$Game::UsesNetwork = true;
This tells TGB to utilize the network commands. Very important for network games!!
Ok, now I'm just going to tell you my folder structure, and put the code for each file. Hopefully I'll be able to make this a little prettier some other time (or someone else can).
File: *gameName*\game\gamescripts\game.cs
function startGame(%level)
{
exec("./exec.cs"); //add this line
exec("./onStartUp.cs"); //add this line
Canvas.setContent(mainScreenGui);
Canvas.setCursor(DefaultCursor);
new ActionMap(moveMap);
moveMap.push();
$enableDirectInput = true;
activateDirectInput();
//enableJoystick(); //I never use a joystick, so I comment this out
sceneWindow2D.loadLevel(%level);
onStartUp(); //Also add this line
}
File: *gameName*\game\gamescripts\exec.cs
exec("./mouse.cs");
exec("./server/initServer.cs");
exec("./server/serverTTT.cs");
exec("./server/serverCommands.cs");
exec("./client/initClient.cs");
exec("./client/clientTTT.cs");
exec("./client/clientCommands.cs");
File: *gameName*\game\gamescripts\onStartUp.cs
function onStartUp()
{
$mainScreen = mainScreenGui;
$mouseObj = new t2dSceneObject() {scenegraph = $sceneGraph; };
//global variables for game
$nonePlayer = 0;
$xPlayer = 1;
$oPlayer = 2;
$currentTurn = $nonePlayer;
Canvas.pushDialog(NetworkMenu);
}
function guiXPlayerIndicator::onLevelLoaded(%this, %scenegraph)
{
$xMarker = %this;
}
function guiOPlayerIndicator::onLevelLoaded(%this, %scenegraph)
{
$oMarker = %this;
}
function initTicTacToeBoard(%sceneGraph, %connect)
{
$boardPos = "-8 -8";
// initialize tile board
for(%i=0; %i<9; %i++)
{
// get tile position in 2D array
// 0 1 2
// 3 4 5
// 6 7 8
%y = mFloor(%i/3);
%x = %i - %y*3;
// create tile with necessary properties
%sceneGraph.boardImagesArray[%i] = new t2dStaticSprite()
{
sceneGraph = %sceneGraph;
class = "BoardTile"; // we need setup class of tile to write custom onMouseUp() callback function
imageMap = "tileImageMap";
frame = $nonePlayer; // we will use frame to determine state of the tile
canSaveDynamicFields = "1";
Position = ($boardPos.x + %x*8) SPC ($boardPos.y + %y*8); // set up tile position
size = "8 8";
Layer = "20";
tileId = %i; // custom variable that we will use to determine what tile was clicked
};
if(%connect $= "client")
%sceneGraph.boardImagesArray[%i].setUseMouseEvents(true); //allow use mouse events callbacks
}
}
File: *gameName*\game\gamescripts\mouse.cs
function SceneWindow2D::onMouseMove(%this, %modifier, %worldPos, %mouseClicks)
{
//set the position of the object that is designated to follow the mouse
$mouseObj.setPosition(%worldPos);
}
function SceneWindow2D::onMouseDragged(%this, %modifier, %worldPos, %mouseClicks)
{
//set the positiong of the object that is designated to follow the mouse
$mouseObj.setPosition(%worldPos);
}
function BoardTile::onMouseUp(%this, %modifier, %worldPos, %mouseClicks)
{
attemptSelectSpot(%this);
}
File: *gameName*\game\gamescripts\client\initClient.cs
function initClient()
{
Canvas.popDialog(NetworkMenu);
Canvas.popDialog(TitleScreenGui);
Canvas.setContent($MainScreen);
}
function TicTacToeSceneGraph::onLevelLoaded(%this, %scenegraph)
{
$sceneGraph = %this;
initTicTacToeBoard($sceneGraph, "client");
}
File: *gameName*\game\gamescripts\client\clientCommands.cs
function clientCmdNotYourTurn()
{
//tell the client that you -cannot- play when its NOT your turn
MessageBoxOK("Not your turn...", "It is currently not your turn.", "");
}
function clientCmdInitClient()
{
initClient();
}
function clientCmdSetTeam(%team)
{
//set the player's team
$playerTeam = %team;
}
function clientCmdInMatch()
{
//spawn the message telling a client they are rejected, we are ina game without you
MessageBoxOK("Server in a Match...", "The server you are trying to connect to is already in a match.", "");
}
function clientCmdStartGame()
{
//call the start game on the client side
clientLaunchGame();
}
function clientCmdSetPlayerTurn(%val)
{
//set the player's turn to the value passed
$myTurn = %val;
echo("$myTurn = " @ $myTurn);
}
function clientCmdMarkBoard(%tile, %marker)
{
//Server has determined a valid selection, mark the clients boards appropriately
clientMarkBoard(%tile, %marker);
}
function clientCmdYouWin()
{
MessageBoxOK("Game Completed","You win!","clientResetGame();");
}
function clientCmdYouLose()
{
MessageBoxOK("Game Completed","You Lost!","clientResetGame();");
}
function clientCmdTieGame()
{
MessageBoxOK("Game Completed","Nobody wins!","clientResetGame();");
}
File: *gameName*\game\gamescripts\client\clientTTT.cs
function clientLaunchGame()
{
Canvas.setContent($MainScreen);
//call the game started function on the server
commandToServer('gameStarted');
}
function clientResetGame()
{
for(%i=0; %i<9; %i++)
{
$sceneGraph.boardImagesArray[%i].frame = $nonePlayer;
}
}
function attemptSelectSpot(%tile)
{
//first we check if it is our turn
if(!$myTurn)
{
MessageBoxOK("Not your turn...", "It is currently not your turn.", "");
}
else if(%tile.frame != $nonePlayer)
{
//Client tried to pick a spot they know is used, don't bother the server
MessageBoxOK("Invalid Selection...", "That spot is already used!", "");
}
else
{
commandToServer('AttemptSelectSpot', %tile);
}
}
function clientMarkBoard(%tile, %marker)
{
//Mark the board and update the image
$sceneGraph.boardImagesArray[%tile.tileId].frame = %marker;
}
File: *gameName*\game\gamescripts\server\initServer.cs
function initServer()
{
//Create an object to act as our server TicTacToe board
new t2dStaticSprite(ServerTTTBoard)
{
scenegraph = $sceneGraph;
};
//init the inmatch value to false;
$inMatch = false;
initTicTacToeBoard($sceneGraph, "server");
}
function onServerCreated()
{
//toggle first connection to true
$firstConnection = true;
}
function onClientConnected(%client)
{
//if this is the first connection, it's local, second is client, any beyond is sent an "in match" message
if($firstConnection)
{
$serverConnection = %client;
setTeam(%client, $xPlayer);
$firstConnection = false;
initClient();
initServer();
Canvas.popDialog(NetworkMenu);
Canvas.setContent($MainScreen);
}
else if(!$firstConnection)
{
if($inMatch)
{
commandToClient(%client, 'inMatch');
}
else
{
setTeam(%client, $oPlayer);
$playerConnection = %client;
commandToClient(%client, 'initClient');
serverLaunchGame();
}
}
}
function setTeam(%client, %team)
{
//store the team for the client
%client.team = %team;
//tell the client what team they are
commandToClient(%client, 'setTeam', %team);
}
File: *gameName*\game\gamescripts\server\serverCommands.cs
function serverCmdGameStarted(%client)
{
//tell the server to begin the game
serverBeginGame();
}
function serverCmdAttemptSelectSpot(%client, %tile)
{
//Have the server check the spot selected by the client
serverAttemptSelectSpot(%client, %tile);
}
function serverCmdResetGame(%client)
{
//tell the server to reset the game
serverResetGame();
}
File: *gameName*\game\gamescripts\server\serverTTT.cs
function serverLaunchGame()
{
//we are now in a match
$inMatch = true;
//init the server's boards
//ServerCheckerBoard.init();
//ClientCheckerBoard.init();
//tell the client to start the game
commandToClient($playerConnection, 'StartGame');
}
function serverBeginGame()
{
//set turn to server's turn
setTurn($serverConnection);
}
function setTurn(%player)
{
//get the other player
%otherPlayer = getOtherPlayer(%player);
//call the client's set turn function properly
commandToClient(%otherPlayer, 'setPlayerTurn', false);
commandToClient(%player, 'setPlayerTurn', true);
//set the server's turn properly
$playersTurn = %player;
}
function getOtherPlayer(%player)
{
//check to see what play was passed and return the other player
if(%player == $playerConnection)
{
%otherPlayer = $serverConnection;
}
else
{
%otherPlayer = $playerConnection;
}
return %otherPlayer;
}
function checkPlayerMarker(%player)
{
//check to see who is playing, and send back either x or o player
if(%player == $serverConnection)
{
%marker = $xPlayer;
}
else
{
%marker = $oPlayer;
}
return %marker;
}
function swapTurns()
{
//get the other player
%player = getOtherPlayer($playersTurn);
//set the turn to the other player
setTurn(%player);
}
function serverAttemptSelectSpot(%client, %tile)
{
if(%tile.frame != $nonePlayer)
{
//Somehow the client's board must be in error
//If this ever happens, should probably send the board back to the client
error("Coding Problem!!!! The Client selected a non-empty spot and passed it to the server!!!");
}
else
{
%marker = checkPlayerMarker(%client);
$sceneGraph.boardImagesArray[%tile.tileId].frame = %marker;
%other = getOtherPlayer(%client);
commandToClient(%client, 'MarkBoard', %tile, %marker);
commandToClient(%other, 'MarkBoard', %tile, %marker);
checkWinState();
swapTurns();
}
}
function checkWinState()
{
%winner = $nonePlayer;
%this = $sceneGraph;
// check lines
if (%this.boardImagesArray[0].frame!=$nonePlayer && %this.boardImagesArray[0].frame==%this.boardImagesArray[1].frame && %this.boardImagesArray[0].frame==%this.boardImagesArray[2].frame)
%winner = %this.boardImagesArray[0].frame;
else if (%this.boardImagesArray[0].frame!=$nonePlayer && %this.boardImagesArray[0].frame==%this.boardImagesArray[3].frame && %this.boardImagesArray[0].frame==%this.boardImagesArray[6].frame)
%winner = %this.boardImagesArray[0].frame;
else if (%this.boardImagesArray[0].frame!=$nonePlayer && %this.boardImagesArray[0].frame==%this.boardImagesArray[4].frame && %this.boardImagesArray[0].frame==%this.boardImagesArray[8].frame)
%winner = %this.boardImagesArray[0].frame;
else if (%this.boardImagesArray[2].frame!=$nonePlayer && %this.boardImagesArray[2].frame==%this.boardImagesArray[4].frame && %this.boardImagesArray[2].frame==%this.boardImagesArray[6].frame)
%winner = %this.boardImagesArray[2].frame;
else if (%this.boardImagesArray[2].frame!=$nonePlayer && %this.boardImagesArray[2].frame==%this.boardImagesArray[5].frame && %this.boardImagesArray[2].frame==%this.boardImagesArray[8].frame)
%winner = %this.boardImagesArray[2].frame;
else if (%this.boardImagesArray[3].frame!=$nonePlayer && %this.boardImagesArray[3].frame==%this.boardImagesArray[4].frame && %this.boardImagesArray[3].frame==%this.boardImagesArray[5].frame)
%winner = %this.boardImagesArray[3].frame;
else if (%this.boardImagesArray[1].frame!=$nonePlayer && %this.boardImagesArray[1].frame==%this.boardImagesArray[4].frame && %this.boardImagesArray[1].frame==%this.boardImagesArray[7].frame)
%winner = %this.boardImagesArray[1].frame;
else if (%this.boardImagesArray[6].frame!=$nonePlayer && %this.boardImagesArray[6].frame==%this.boardImagesArray[7].frame && %this.boardImagesArray[6].frame==%this.boardImagesArray[8].frame)
%winner = %this.boardImagesArray[6].frame;
if (%winner != $nonePlayer)
{
if (%winner == $xPlayer)
{
commandToClient($serverConnection, 'YouWin');
commandToClient($playerConnection, 'YouLose');
serverResetGame();
}
else if (%winner == $oPlayer)
{
commandToClient($serverConnection, 'YouLose');
commandToClient($playerConnection, 'YouWin');
serverResetGame();
}
}
else
{
// check for drawn game
%filled = true;
for (%i=0; %i<9; %i++)
{
if (%this.boardImagesArray[%i].frame==$nonePlayer)
{
%filled = false;
break;
}
}
if (%filled)
{
commandToClient($serverConnection, 'TieGame');
commandToClient($playerConnection, 'TieGame');
serverResetGame();
}
}
}
function serverResetGame()
{
for(%i=0; %i<9; %i++)
{
$sceneGraph.boardImagesArray[%i].frame = $nonePlayer;
}
}
That's it! Test it out! Enjoy!
Special Thanks go to Alexey Stashin, who did the TicTacToe Tutorial, as well as the author of the Checkers tutorial.
-Doc 308- 12/15/08



