• Examples (iOS)
• Examples (Java/Android)
• Examples (C++)
Server API Documentation

 

» Tris (Tic-Tac-Toe)

» Overview

The Tris example shows how to create a complete mutiplayer version for Android devices of the famous "Tic-Tac-Toe" game using SmartFoxServer 2X.

This implementation mimics the Flash version of the game (actually you can even play against a Flash client connected to the same server), so please read the corresponding Flash tutorial's overview. The only missing feature is the spectators support and spectator-to-player switch.

The game uses Android UI elements found in the previous Android examples and implements additional Android techniques to build the two players board game.
The application is split into two activities: the first one deals with joining the Zone and creating and joining a Game Room; the second activity deals with the actual game play. The game play activity seperates the visual aspect of the tic-tac-toe game board from the logical side of where on the device screen a user is touching.

Just like in the basic examples, when the application is launched, the user is prompted to enter SmartFoxServer 2X connection details (the pre-filled connection parameters used by this example are the same as for the previous examples; check the tutorial for additional informations on the default value for the IP address).

After the details are submitted, a connection to SmartFoxServer 2X is attempted and an event is then fired to notify if the connection has been established or not.

On successful connection to the server, the user is then asked to enter a nickname. Using this as a username the user is logged into the Tris Zone. With respect to the Simple Chat application, this example adds an additional tab to the view, showing the list of current games.

>> DOWNLOAD the source files <<

» Installation

» Running the example

In order to access the application's client-side source code and run it in the Android Virtual Device (emulator), follow these steps:

  1. copy the tris folder from the /deploy/extensions folder to your SFS2X installation folder, under /SFS2X/extensions;
  2. make sure your SmartFoxServer 2X installation contains the BasicExamples Zone definition;
  3. start SmartFoxServer 2X;
  4. in Eclipse, import the Android project contained in the /source/client folder from the download above;
  5. launch the app and change the connection details if your SmartFoxServer 2X is not installed on your machine's localhost.

» Server-side source code setup

The Tris game features a server-side Extension, as described further on. In order to access its code, create and setup a new project in your Java IDE of choice as described in the Writing a basic Extension document. Copy the content of the /source/server/ folder to your project's source folder.

» Client code highlights

The lobby of this example follows the same structure as the SimpleChat tutorial, adding an extra tab to display the available game rooms and create new ones.

Every time the Game tab is tapped, the list of games is refreshed using the code below. Firstly, the ArrayAdapter is cleared, then a list of all Rooms belonging to the "games" group is retrieved in order to add it to the adapter. The content of this adapter is displayed on the Game List tab.

	public void updateGamesList() {
		adapterGames.clear();
		List gameList = sfsClient.getRoomListFromGroup(GAME_ROOMS_GROUP_NAME);
		ListIterator gameRoomIterator = gameList.listIterator();
		while (gameRoomIterator.hasNext()) {
			Room room = gameRoomIterator.next();
			// Add each room back into the adapter with player count
			adapterGames.add(room.getName() + "         Users: " + room.getUserCount() + "/"
					+ room.getMaxUsers());
		}
		updateGamesTabLabel();
	}

In the initUI method the UI elements are initialized as in the other examples and we add a listener to detect when the user taps on a game to join a Game Room.

The code below is invoked when the user presses the Create button at the bottom of the Games tab. As long as the input field is not empty, this method creates and sends a new CreateRoomRequest to the server. A Room Extension is specified when a Room is created to provide the server-side game logic (see the Server code highlights paragraph below).

	/**
	 * Send the create room request to the server and add room name to game list
	 */
	public void createGameRoom(String roomName) {
		if (roomName.length() > 0) {
			RoomExtension extension = new RoomExtension(EXTENSION_ID, EXTENSIONS_CLASS);
			RoomSettings settings = new RoomSettings(roomName);
			settings.setGroupId(GAME_ROOMS_GROUP_NAME);
			settings.setGame(true);
			settings.setMaxUsers(2);
			settings.setMaxSpectators(0);
			settings.setExtension(extension);
			sfsClient.send(new CreateRoomRequest(settings, true, sfsClient.getLastJoinedRoom()));
		}
	}

The OnItemClickListener added to the list of games sends a JoinRoomRequest with the name of the game to the server. If the SFS2X server successfully joins the user to the Room then the client receives a ROOM_JOIN event. This is handled, as in the previous examples, in the dispatch method. If the joined Room is a Game Room then the initGame method is called. This method, see below, creates a new Intent and immediately switches to that activity.

	/**
	 * Initialize a new game, starting a new activity
	 */
	private void startTrisGame() {
		startActivity(new Intent(this, TrisGameActivity.class));
	}

Since it is a new activity, another onCreate method is called.

First the intViews method initializes the references to the views and creates the BoardViewsHelper that wraps all the functionality related to the board: mark a cell, clear the board, and listen the clicks over the cells.

Then the method initSmartFox recovers the SmartFox instance through the singleton in the class SFSController and add a listener for the events CONNECTION_LOST, USER_EXIT_ROOM and EXTENSION_RESPONSE, this is again handled in a dispatch method that is overridden from the IEventListener class. Depending on the command that is received in the Extension response, it can be produced one of the following situations:

  1. Start the game.
  2. Update the board on the user's device after a move has been made.
  3. A message to indicate the result (when the game overs).

After adding the listeners, an ExtensionRequest is sent to indicate to the server that the client is ready, and then it starts waiting for an opponent:

	private void initSmartFox() {
		// Instantiate SmartFox client
		sfsClient = SFSController.getSFSClient();
		// Register to SmartFox events
		sfsClient.addEventListener(SFSEvent.EXTENSION_RESPONSE, this);
		sfsClient.addEventListener(SFSEvent.CONNECTION_LOST, this);
		sfsClient.addEventListener(SFSEvent.USER_EXIT_ROOM, this);
		
		// Tell extension I'm ready to play
		sfsClient.send(new ExtensionRequest("ready", new SFSObject(), sfsClient.getLastJoinedRoom()));
		if (sfsClient.getLastJoinedRoom().getUserCount() == 1) {
			waitForOpponent();
		}
		gameStarted = true;
	}

When the second player joins and sends a “ready” event the server will notify both users by means of an EXTENSION_RESPONSE message with params[cmd]=start. At this point startGame is called, this method clears the board, set the value of myTurn and set the turn (waiting for a movement or for the opponent’s movement depending on the myTurn’s value).

	/**
	 * Set who's turn it is and start the game
	 * 
	 * @param resObj
	 */
	private void startGame(ISFSObject resObj) {
		gameStarted = true;
		int firstTurn = resObj.getInt("t");
		myTurn = sfsClient.getMySelf().getPlayerId() == firstTurn ? true : false;
		board.clear();
		setTurn();
	}
	
	/**
	 * Show or hide the "waiting for opponent" dialog depending on who's turn it is.
	 */
	private void setTurn() {
		if (gameStarted) {
			if (myTurn) {
				endWaitForOpponent();
			} else {
				waitForOpponent();
			}
		}
	}

As soon as a movement is received the moveReceived method is called, there the board is updated in and the turn goes to the corresponding user:

	/**
	 * Determine where to draw the icon just placed and send message to handler so icon is drawn in
	 * correct place
	 * 
	 * @param resObj
	 */
	private void moveReceived(ISFSObject resObj) {
		int movingPlayer = resObj.getInt("t");
		int x = resObj.getInt("x");
		int y = resObj.getInt("y");
		board.markCell(x, y, movingPlayer);
		myTurn = movingPlayer != sfsClient.getMySelf().getPlayerId();
		setTurn();
	}

During a user's turn, when a cell is tapped the movement is sent calling to the sendMove method.

	/**
	 * Send the move to the server's extension
	 * @param x
	 * @param y
	 */
	public void sendMove(int x, int y) {
		// Send the selected square to SFS2X
		ISFSObject sfso = new SFSObject();
		sfso.putInt("x", x);
		sfso.putInt("y", y);
		sfsClient.send(new ExtensionRequest("move", sfso, sfsClient.getLastJoinedRoom()));
	}

When the game is over the server sends an EXTENSION_RESPONSE callback with params[cmd]=win or params[cmd]=tie. For the “win” case the winner player id is sent at params[w].

When the Activity is destroyed the listeners are removed and the user is joined to the Lobby again.

	@Override
	protected void onDestroy() {
		super.onDestroy();
		// Remove listeners, rejoin the lobby and go to application's main activity
		sfsClient.removeEventListener(SFSEvent.EXTENSION_RESPONSE, this);
		sfsClient.removeEventListener(SFSEvent.CONNECTION_LOST, this);
		sfsClient.removeEventListener(SFSEvent.USER_EXIT_ROOM, this);
		Room lobby = sfsClient.getRoomByName(getString(R.string.example_lobby));
		if (sfsClient.isConnected() && !sfsClient.getMySelf().isJoinedInRoom(lobby)) {
			sfsClient.send(new JoinRoomRequest(getString(R.string.example_lobby), "", sfsClient
					.getLastJoinedRoom().getId()));
		}
	}

» Server code highlights

Please read the corresponding Tris Unity tutorial's Server code highlights paragraph.

» More resources

You can learn more about the described features by consulting the following resources: