BoringOKGoodZestyLemonade (2 votes, average: 5 out of 5)
Loading ... Loading ...

Free Playing Cards

Besides David Bellot’s free SVG playing cards I haven’t found any other source of cards in vector format.

So here is my take. A deck of classic playing cards with place holders for the face cards (T, J, Q and K) and a second deck of playing cards with a simpler design, perfect for games. This is free, use it for your own games, prints, tutorials.

Simple Cards

Simple Cards

Deck of classic cards

Deck of simpler cards (original design).

In the flash files you will find that each card is a symbol as well as the suite icons (hearts, diamonds, clubs and spades) and the card blank base.

Enjoy!

Share it!
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • Technorati
BoringOKGoodZestyLemonade (3 votes, average: 5 out of 5)
Loading ... Loading ...

SmartFoxServer Tutorials: Bulls and Cows

Overview

This time we are building a simple game. If you have licensed SmartFoxServer Lite, this tutorial will show you how to build a basic game without extensions, feature only available in SmartFoxServer Pro.

Bulls and Cows Login

Bulls and Cows Login

We are making Bulls and Cows, a classic paper and pencil game where you try to guess a four letter string code previously written by your opponent and vice versa. The game ends when either code is discovered. To help you break the code, in each attempt you will get the number of Bulls and Cows. A bull is a letter in the right order in the code and a cow a letter in the code but not in the right order. Four bulls and you win.

Also, we are extending the default InputBox component to AutoClearInput that basically implements an specific behavior desired for the game. The text in the control will be removed the first time it gains focus.

Finally we have created a custom zone and room in the SmartFoxApplication/Server/config.xml configuration file and the code is listed at the end of the tutorial. To learn more about zones and how create them follow this link.

Bulls and Cows Game

Bulls and Cows Game

The Code Explained

The import statements.

import mx.controls.Alert;
import mx.collections.ArrayCollection;
import mx.events.ValidationResultEvent;
 
import it.gotoandplay.smartfoxserver.SmartFoxClient;
import it.gotoandplay.smartfoxserver.SFSEvent;
import it.gotoandplay.smartfoxserver.data.Room;
import it.gotoandplay.smartfoxserver.data.User;

We declare the properties to store our secret code and username. We need to store the username because when you exit the game you are logged out and we can’t use smartFox.myUserName anymore.

The rest of the code is the usual connect, login and room join schema.

private var smartFox:SmartFoxClient;
private var myUsername:String;
private var mySecretCode:String;
 
private function init(e:Event):void
{
	connect();	
}
 
private function connect():void
{
	smartFox = new SmartFoxClient(true);
 
	smartFox.addEventListener(SFSEvent.onConnection, smartFox_onConnection);
 
	smartFox.addEventListener(SFSEvent.onLogin, smartFox_onLogin);
	smartFox.addEventListener(SFSEvent.onRoomListUpdate, smartFox_onRoomListUpdate);
	smartFox.addEventListener(SFSEvent.onJoinRoom, smartFox_onJoinRoom);
	smartFox.addEventListener(SFSEvent.onJoinRoomError, smartFox_onJoinRoomError);
 
	smartFox.addEventListener(SFSEvent.onPrivateMessage, smartFox_onPrivateMessage);
 
	smartFox.connect("localhost");	
}
 
private function smartFox_onConnection(e:SFSEvent):void
{
	if (!e.params.success)
	{				
		trace("The connection failed. " + e.params.error);
	}
}
 
private function smartFox_onLogin(e:SFSEvent):void	
{ 
	if (!e.params.success)
	{
		Alert.show("Error: " + e.params.error);
	}
}
 
private function smartFox_onRoomListUpdate(e:SFSEvent):void
{
	smartFox.autoJoin();
}

This code handles a user joining the room. There is no need to check if the room is full because as previously mentioned, the zone and the room are custom made for this application and there is a limit of 2 users top. If the room is full and you try to login a joinRoomError event will trigger.

We are creating a room variable to store the current user id. Note the name of the variable uses the player id which is an incremental integer (1, 2 ,3 …) only available in game rooms. Room variables are accessible to all users in the room. This way we are able to retrieve the user id using the player id. The advantage is the ease of calculating the next player id based in the current player id, nextId = playerId % playersCount + 1. This technique works for a game of any count of players.

private function smartFox_onPrivateMessage(e:SFSEvent):void	
{
	if (e.params.sender.getId() == smartFox.myUserId)
	{
		return;
	}
 
	if (e.params.message == "$init")
	{
		currentState = "playState";
		clearLists();		
		myCodeLabel.htmlText = "Shhh, your code is <b>" + mySecretCode + "</b>.";						
	}
	else if (e.params.message == "$next")
	{
		guessButton.enabled = true;
		whoseTurnLabel.htmlText = "It's <b>your</b> turn.";
		sendMessage("$wait", getNextPlayerIndex());  
	}	
	else if (e.params.message == "$wait")
	{
		myCodeLabel.htmlText = "Shhh, your code is <b>" + mySecretCode + "</b>.";
		guessButton.enabled = false;
		whoseTurnLabel.htmlText = "It's <b>" + e.params.sender.getName() + "'s</b> turn.";
	}
	else if (e.params.message.substr(0,6) == "$guess") 
	{ 
		var guess:String = e.params.message.substr(6);
		var bulls:int = 0;
		var cows:int = 0;
 
		for (var i:int = 0; i < 4; i++)
		{
			guess.charAt(i) == mySecretCode.charAt(i) ? bulls++ : null;
		}
 
		for (i = 0; i < 4; i++)
		{
			for (var j:int = 0; j < 4; j++)
			{
				guess.charAt(i) == mySecretCode.charAt(j) ? cows++ : null;
			}
			if (cows == 4) break;
		}
 
		cows = cows - bulls; //don't count bulls as cows
 
		var result:String = guess + " | " + bulls.toString() + "B | " + cows.toString() + "C";					 
 
		if (playerGuessList.dataProvider == null)
		{
			playerGuessList.dataProvider = [];
		}						
		playerGuessList.dataProvider.addItem({label: result});
 
		sendMessage("$result" + result, getNextPlayerIndex());
 
		if (bulls == 4)
		{
			sendMessage("$win", getNextPlayerIndex());
		}
	}
	else if (e.params.message.substr(0,7) == "$result") 
	{
		if (myGuessList.dataProvider == null)
		{
			myGuessList.dataProvider = [];
		}
		myGuessList.dataProvider.addItem({label: e.params.message.substr(7)})
	} 
	else if (e.params.message == "$win")
	{
		currentState = "winState";
		sendMessage("$lose", getNextPlayerIndex());
		smartFox.logout();
	}
	else if (e.params.message == "$lose")
	{
		currentState = "loseState";
		smartFox.logout();
	}
}

This code handles the onJoinRoomError event and the basic input from the user. The guess and play button click events. In the guessButton_click handler we are implementing the core of the turn based functionality. We are sending application messages to the next player. Basically this code moves the turn around.

Finally note that when we exit we deliberately log the user out and change the state. This design choice allow us to reuse the onJoinRoom code when the users attempts replaying or enter coming from the wait state if the room was full.

private function smartFox_onJoinRoomError(e:SFSEvent):void	
{
	currentState = "roomFullState";
}
 
private function guessButton_click(e:MouseEvent):void	
{
	sendMessage("$guess" + guessText.text, getNextPlayerIndex());
	sendMessage("$next", getNextPlayerIndex());
	guessText.text = "";
}
 
private function playButton_click(e:MouseEvent):void	
{
	if (validateCode(codeText.text))
	{
		mySecretCode = codeText.text;
		myUsername = usernameText.text;
		smartFox.login("simpleGame", usernameText.text, "");
	}
}
 
private function tryAgainButton_click(e:MouseEvent):void	
{
	smartFox.logout(); 
	smartFox.login("simpleGame", myUsername, "");
}
 
private function exitButton_click(e:MouseEvent):void	
{
	smartFox.logout();
	currentState = "";
}

Here is the rule to validate codes. The digits can’t repeat and for this implementation of the game we are enforcing using 4 digits exclusively.

private function validateCode(code:String):Boolean
{
	if (code.length != 4)
	{
		return false;
	}
 
	var charmap:ArrayCollection = new ArrayCollection();
	for (var i:int; i < code.length; i++)
	{
		if (charmap.contains(code.charAt(i)))
		{
			return false;
		}
		else
		{
			charmap.addItem(code.charAt(i));		
		}
	}
	return true;
}

Clear the lists items safely.

private function clearLists():void	
{
	if (myGuessList.dataProvider != null)
	{
		myGuessList.dataProvider.removeAll();
	}
	if (playerGuessList.dataProvider != null)
	{
		playerGuessList.dataProvider.removeAll();
	}
}

The following are helper functions used throughout the application to handle sending private messages, retrieving the next player id in turn, …

private function sendMessage(message:String, playerIndex:int):void	
{	
	smartFox.sendPrivateMessage(message, getUserFromPlayerIndex(playerIndex).getId());
}	
 
private function getPlayer(index:int):String
{
	return "Player" + index.toString();
}
 
private function getNextPlayerIndex():int
{
	return smartFox.playerId % 2 + 1;
}
 
private function getUserFromPlayerIndex(index:int):User
{
	var room:Room = smartFox.getActiveRoom();
	return room.getUser( room.getVariable( getPlayer(index) ));
}

Click here to download the application. You are downloading a ZIP file that contains the MXML for the BullsAndCows game and the AutoClearInput extended InputBox.

Creating the Zone

This is the code for the custom zone and room the game is based on. Note that among other things autoJoin is true because there is only one room in the zone and isGame is also true because we need the player id feature enabled. Finally note that the maximum number of spectators is set but the application doesn’t implement any functionality for spectators currently.

<Zone name="simpleGame">
  <Rooms>
    <Room name="default" autoJoin="true" isGame="true" maxUsers="2" maxSpectators="10" />
  </Rooms>
</Zone>
Share it!
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • Technorati
BoringOKGoodZestyLemonade (1 votes, average: 5 out of 5)
Loading ... Loading ...

SmartFoxServer Tutorials: The Server Configuration File

Overview

This time we will talk about the config.xml server configuration file. We will discuss only what we need to get started. To learn more about the server configuration file read the official documentation.

The server configuration file SmartFoxServer/Server/config.xml is loaded when SmartFoxServer is started. It can be reloaded using the Admin.swf tool. This file lays out several server side configuration attributes and properties that you can modify or create. We will mostly use it to create custom zones and rooms.

Zones and Rooms

Open config.xml and locate:

<!--
	Zones Configuration.
-->
<Zones>	
 
...
 
</Zones>

Inside this tag set you will find several zones and rooms. All of which are created by SmartFoxServer at startup. Zones are collections of rooms, generally dedicated to one application / game. SmartFoxServer can serve more than one zone but evidently the performance will suffer as more applications / zones run.

The XML structure of zones and rooms is somehow self-explanatory and just checking out its code helps understanding how they work, however the definition of each property and attribute is covered in the documentation. Open config.xml and locate the following zone.

<Zone name="simpleChat" uCountUpdate="true" buddyList="20" maxUsers="4000" customLogin="false">
  <Rooms>
    <Room name="The Hall" maxUsers="50" isPrivate="false" isTemp="false" autoJoin="true" uCountUpdate="true" />		
    <Room name="The Kitchen" maxUsers="50" isPrivate="false" isGame="false" isTemp="false" />
    <Room name="The Garden" maxUsers="50" isPrivate="false" isTemp="false" />
    <Room name="The Bathroom" maxUsers="50" isPrivate="false" isTemp="false" />
    <Room name="The Garage" maxUsers="50" isPrivate="false" isTemp="false" />
    <Room name="The Living Room" maxUsers="50" isPrivate="true" isTemp="false" pwd="test" />
  </Rooms>
 
  <Extensions>
    <extension name="json" className="jsonSample.as" type="script" />
  </Extensions>
 
  <Moderators status="on">
    <Mod name="modName" pwd="modPass" />
  </Moderators>
</Zone>

Sometimes we will reuse existing zones because its configuration will fit our purposes. Other times we will declare zones and rooms in the config.xml file. For example if we are using extensions and implementing a custom login we will need to declare a zone that supports this functionality.

Example Zones

The following is a zone we could reuse for two player simple games.

<Zone name="myGame">
  <Rooms>
    <Room name="default" autoJoin="true" isGame="true" maxUsers="2" maxSpectators="10" />
  </Rooms>
</Zone>

The following is a zone we could reuse for applications that require custom login. Note that we need to specify the name of the extension that handles the custom login.

<Zone name="myChat" customLogin="true">
  <Rooms>
    <Room name="default" autoJoin="true" />
  </Rooms>
  <Extensions>
    <extension name="myChat" className="myChat.as" type="script" />
  </Extensions>
</Zone>
Share it!
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • Technorati
BoringOKGoodZestyLemonade (2 votes, average: 5 out of 5)
Loading ... Loading ...

SmartFoxServer Tutorials: Simple Chat Application

Overview

This time we are going to build a very simple chat system. This application will be the basis of the AdvancedChat that will get its own tutorial later.

SimpleChat Login

SimpleChat Login

For this application we are extending the TitleWindow component in the ChatWindow to handle chat bubbles, vertical scrolling and more. Really nothing to do with the SmartFoxServer logic part of the application.

SimpleChat Application

SimpleChat Application

The Code Explained

The import statements.

import mx.events.CloseEvent;
import mx.containers.VBox;
import mx.containers.HBox;
import mx.controls.Text;
import mx.controls.Label;
import mx.controls.TextInput;
import mx.containers.TitleWindow;
import mx.managers.PopUpManager;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
 
import it.gotoandplay.smartfoxserver.SmartFoxClient;
import it.gotoandplay.smartfoxserver.SFSEvent;
import it.gotoandplay.smartfoxserver.data.Room;
import it.gotoandplay.smartfoxserver.data.User;

The chats array will keep the list of chat windows having a conversation with the current user. We are storing colors in dynamic myColorA and myColorB properties using the myBuddyVars collection that is relative to each user.

private var smartFox:SmartFoxClient;
private var chats:Array;		
 
private function connect(e:Event):void
{
	smartFox = new SmartFoxClient(true);
 
	smartFox.addEventListener(SFSEvent.onConnection, smartFox_onConnection);
	smartFox.addEventListener(SFSEvent.onConnectionLost, smartFox_onConnectionLost);
 
	smartFox.addEventListener(SFSEvent.onLogin, smartFox_onLogin);
	smartFox.addEventListener(SFSEvent.onRoomListUpdate, smartFox_onRoomListUpdate);
	smartFox.addEventListener(SFSEvent.onJoinRoom, smartFox_onJoinRoom);
 
	smartFox.addEventListener(SFSEvent.onUserEnterRoom, smartFox_onUserEnterRoom);
	smartFox.addEventListener(SFSEvent.onUserLeaveRoom, smartFox_onUserLeaveRoom);
 
	smartFox.addEventListener(SFSEvent.onPrivateMessage, smartFox_onPrivateMessage);
 
	smartFox.connect("localhost");
 
	smartFox.myBuddyVars.myColorA = 0x544EC8;
	smartFox.myBuddyVars.myColorB = 0x0F63B2;		
}

This application is using one handy technique in Flex, states that basically allow us to have different screens that we can navigate programmatically. Is similar to having scenes in different frames in Flash and positioning the cursor on any of them programmatically. Note that we are updating the user’s List in onJoinRoom, onUserEnterRoom and onUserLeaveRoom.

private function smartFox_onConnection(e:SFSEvent):void
{
	if (!e.params.success)
	{				
		trace("The connection failed. " + e.params.error);
	}
}
 
private function smartFox_onLogin(e:SFSEvent):void	
{ 
	if (!e.params.success)
	{
		loginStatus.text = "Error: " + e.params.error;
	}
}
 
private function smartFox_onRoomListUpdate(e:SFSEvent):void
{
	smartFox.autoJoin();
 
 
}
 
private function smartFox_onJoinRoom(e:SFSEvent):void
{
	currentState = "chatState";
 
	var room:Room = e.params.room as Room;
	updateUserList(room);
	userLabel.text = "You are " + smartFox.myUserName + "!";
	statusLabel.text = "Welcome!";
}
 
private function smartFox_onUserEnterRoom(e:SFSEvent):void
{
	var user:User = e.params.user as User;
	statusLabel.text = user.getName() + " joined the room.";			
	updateUserList(smartFox.getActiveRoom());
}
 
private function smartFox_onUserLeaveRoom(e:SFSEvent):void
{
	statusLabel.text = e.params.userName + " just left.";
	updateUserList(smartFox.getActiveRoom());
}

When receiving a private message, a chat window is created for the sender in the current’s user chat box array. Notice that the user id is used as a handle for the chat in the array. If a conversation already existed the message is added to the chat via the createChatRow method that is implemented in the ChatWindow extended component.

private function smartFox_onPrivateMessage(e:SFSEvent):void
{
	var user:User = e.params.sender as User;
	var chatWindow:ChatWindow;
 
	if (user.getId() != smartFox.myUserId)
	{ 
		chats = chats == null ? new Array() : chats;
 
		chatWindow = chats[user.getId()];
 
		if (chatWindow == null)
		{
			chatWindow = createChatWindow(user.getId(), user.getName());
		}			
 
		chatWindow.createChatRow(user.getName(), e.params.message, smartFox.myBuddyVars.myColorB);
	}
}

In the following code we create the chat box for the user that is initiating the conversation.

private function smartFox_onConnectionLost(e:SFSEvent):void
{
	Alert.show("The connection with the server was lost.", "Connection Lost");
	currentState = "";
}
 
private function loginButton_click(e:MouseEvent):void
{
	smartFox.login("simpleChat", usernameLoginText.text, "");
}
 
private function chatButton_click(e:MouseEvent):void	
{
	if (usersList.dataProvider.length > 0)
	{
		if (chats == null)
		{
			chats = new Array();
		}
 
		if (usersList.selectedIndex >= 0 && chats[usersList.selectedItem.data] == null)
		{ 
			createChatWindow(usersList.selectedItem.data, usersList.selectedItem.label);
		}
	}
}

The code in the messageInput_keyDown event handler displays the message for the current user and sends a private message to the other user in the conversation. The recipient user id is retrieved from the chat window id which is always set to be the same as the user’s. This is implemented in the createChatWindow method.

private function messageInput_keyDown(e:KeyboardEvent):void
{
	if (e.keyCode == 13)
	{ 
		var chatWindow:ChatWindow = ((e.currentTarget as TextInput).parentDocument as ChatWindow);
 
		var messageText:String = (e.currentTarget as TextInput).text;
 
		smartFox.sendPrivateMessage( messageText, int(chatWindow.id) );
		chatWindow.createChatRow( smartFox.myUserName, messageText, smartFox.myBuddyVars.myColorA );
		(e.currentTarget as TextInput).text = "";
	}
}
 
private function chatWindow_close(e:CloseEvent):void
{
	chats[(e.currentTarget as ChatWindow).id] = null;
}
 
private function createChatWindow(id:int, name:String):ChatWindow
{
	var chatWindow:ChatWindow = PopUpManager.createPopUp(this, ChatWindow, false) as ChatWindow;
	chatWindow.label = name;
	chatWindow.id = String(id);
	chatWindow.messageInput.addEventListener(KeyboardEvent.KEY_DOWN, messageInput_keyDown);
	chatWindow.addEventListener(CloseEvent.CLOSE, chatWindow_close);
	chatWindow.title = "Chat Window | " + smartFox.myUserName + " >> " + name;
	chats[id] = chatWindow;
	return chatWindow;
}

Finally here is the updateUserList method that hasn’t changed a bit from previous examples.

private function updateUserList(room:Room):void
{
	var users:ArrayCollection = new ArrayCollection();
	for each (var user:User in room.getUserList())
	{	
		if (smartFox.myUserId != user.getId())
		{		
			users.addItem({label: user.getName(), data: user.getId()});
		}
	}
	usersList.dataProvider = users;			
}

The code for the ChatWindow component is very straightforward if you already know a little of Flex and since it has no relation with the SmartFoxServer logic we will avoid explaining it here.

Click here to download the application. You are downloading a ZIP file that contains the MXML for the SimpleChat application, the ChatWindow component and the styles.css. Don’t freak out about the styles.css, if you delete this file the application will not break. We use it to describe style elements like what colors or fonts go with what control.

Share it!
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • Technorati
BoringOKGoodZestyLemonade (18 votes, average: 5 out of 5)
Loading ... Loading ...

SmartFoxServer Tutorials

Share it!
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • Reddit
  • StumbleUpon
  • Technorati