[SmartFox] Simple chat (part 1)
[ August 10, 2004 ] by Marco Lapi, a.k.a Lapo
Article 3: learn how to build a basic chat application (part 1)


The related source file for this article is found in the "Examples/simpleChat" folder under the name of "simpleChat.fla"

Open the source fla and have a look at the timeline. Don't worry about all those layers, the ones in which we are interested now are only the top two called "labels" and "{ code }". Most of the other layers contain the graphics for the chat interface.

In this chapter we are going to create a very simple chat application that will be able to:

» connect to the server
» log in users
» send and receive user messages

Have a look at the "labels" layer and you will find three different labels corresponding to 3 different "states" of the application
Let's examine those states in detail:

1) Load:
The playhead will start here and it will wait until the movieclip is fully loaded.
We've just implemented a very basic preloading system in order to prevent that the movieclip attempts to connect to the server
before it's completely loaded.



2) Connect:
In this screen we will use most of the code from the previous example: we will present a screen with a dynamic textbox saying "Connecting...". Also we have one more hidden input field and a button that will appear only when the connection is established.

The showLogin() method is responsible for showing/hiding these 2 controls.
In case the connection attempt fails, those controls will never appear and an error dialog box will be shown with a message in it.
We'll cover the error handling in depth in the next chapter, for now we'd like to concentrate more on the chat application and see how
we can send and receive messages in realtime.

Please note that the server connection code is very similar to the previous article, however we've added two extra things:

zone = "simpleChat"

The zone name corresponds to a zone defined in the server config file. As you may remember you can define as many zones as you want on the server and each of these is just like an isolated world where all your users can interact. In this example we will use the "simpleChat" zone that is already defined in the config.xml file, found in the "SmartFoxServerLite/Server" folder.

smartfox.debug = true

The "debug" flag can be turned on to inspect the messages that are sent and received by the client application.
You can safely turn it off if you don't want to see all those debug info in the Flash output window.


When the "log in" button is pressed the sendLogin() function is called which in turn calls the login method of the SmartFoxClient object. The method takes two arguments: login(zone, username)

function sendLogin()
{
if (!_global.isBusy)
smartfox.login(zone, login_txt.text)
}

The username can be whatever string but not all characters are allowed in a username so when the user tries to log in, its user name
is sent to the server, processed and then sent back to the client.
Also the server does not allow two identical usernames and in case it finds a duplicate it will emit an error.
If you send and empty log in name the server will automatically create a user name like "guest_n" where n is a progressive number.

(If you're wondering what the _global.isBusy variable represents, don't worry. It's just used by the error-message window to
disable the other GUI buttons. We'll cover this topic in the 2nd part of this article.)

In order to handle the server response to our login request we need to handle the onLogin event. When the event is fired an Object is sent to the client with the following properties:

success = boolean, determines if the login operation was successfull
error = string, an error message if the operation failed

The code that handles this event is the following:

smartfox.onLogin = function(resObj)
{
if (resObj.success)
{
// Login Successfull
_global.myName = resObj.name
gotoAndStop("chat")
}
else
{
// Login Failed
_gloabl.isBusy = true
// Show an error window
var win = showWindow("errorWindow")
win.errorMsg.text = resObj.error
}
}

If login succeeded the user name is saved in a _global variable otherwise an error window is displayed.


3) Chat:
After the connection and login events are handled we can move in the chat label and start working on the application itself.
First of all take a look a the image below. It shows how the interface has been laid out:



The most important folder in the timeline is the one called "components" contained in the main "GUI" folder
There you will find the main 4 interface components: the chat multiline dynamic text box, the user listbox, the chat input text box and the user name dynamic text box.

The first thing we do in the code is writing our user name in the top-rigth textbox so that we know our nickname in the chat room.
(see the 2nd line of code)

In the previous section we created an event handler to manage the login response, but there's something more we should do to complete this activity. When a login request is successfull you will receive two responses or events: one of them is already known and the missing one is called onRoomListUpdate. What is it all about ?

Every time our login request is ok you will receive an onLogin event and a onRoomListUpdate with a list of available rooms to join.

This happens because upon reception of a successfull login the client API automatically send a "getRoomList" request to the server and you will need to setup an event handler for it.

Have a look at the onRoomListUpdate method and you will notice that everything is very simple, just one line of code:

this.autoJoin()

This command tells the server to automatically join (enter) the default room in the zone.

NOTE:
You can do this only if you have specified an autoJoin room in the server config.xml file

What we are doing here is that we are ignoring the room list data sent by the server and just proceed to joining the default room:
in a "real-world" application we should analyze the data sent by the server and, for example, populate a list box with all the available rooms and show their status, but this is far beyond the purpose of this tutorial.
We'll analyze these more advanced concepts in the next articles.

Let's sum up what we've done so far:

1) We've established a socket connection with the server
2) We've sent the server a login request for a specific zone in the server and handled the onLogin and onRoomListUpdate events.
3) We've sent an autoJoin request, asking to enter the defualt room in the zone but we have not yet defined a response handler
so this is our next step.

smartfox.onJoinRoom = function(roomObj)
{
var userList = roomObj.getUserList()
 
// Clear text area
chat_txt.htmlText = ""
 
// Clear current list
userList_lb.removeAll()
 
for (var i in userList)
{
var user = userList[i]
userList_lb.addItem(user.getName(), user.getId())
}
 
// Sort names
userList_lb.sortItemsBy("label", "ASC")
 
chat_txt.htmlText += "<font color='#cc0000'>>> Room [ " + roomObj.getName() + " ] joined</font>";
}

Let's analyze the onJoinRoom function:
the first thing we should notice is the argument passed to the method: roomObj.

roomObj is an instance of the SmartFoxClient Room object and here's a quick view of its properties and methods:

» getId() = get room id
» getName() = get room name
» isPrivate() = is the room private ? (0 = false, 1 = true)
» isTemp() = is the room temporary ? (0 = false, 1 = true)
» isGame() = is it a game room ? (0 = false, 1 = true)
» getUserCount() = get # of users in the room
» getMaxUsers() = get capacity of the room
» getUser(id) = get the user Object from a known user id
» getUserList() = get the userList Object
» variables = a property Object containing all room variables

The methods we'd like to talk about right now are getId(), getName(), getUserList()

The first two just return the unique id and name of the joined room while the getUserList() returns an array of User objects, one for each user already logged in the room.

The User object is simpler than the Room obj and it has just three methods:

» getId() = get user unique id
» getName() = get user nickname
» variables = a property Object containing all user vars

Time for a break? Too much info all in one shot? Dont' worry.
Everything is really very simple. It just takes a little time to get accustomed to it. Take a look at the scheme below, it will help understanding the datastructure

From top to bottom you can see that each Zone is a container of Rooms and each room contains its Users.
All users can interact together when they are in the same room, also they can see the rooms within their zone and change from
one to another. In other words this allows more applications (a chat, a game, a whiteboard etc...) to run at the same time on the server independently.

Does it make more sense? It should now! :-)

Basically you enter a zone which essentially is a container of rooms.
Every room contains users and you can join one of them (actually you can join more of one simultaneously) and start interacting with the users inside.

Back to our onJoinRoom code:
we need to read the userList, get the name and id of each user and populate the userList list box on stage.

First we get the list:

var userList = roomObj.getUserList()

(remember, this is an array of User objects)

Then we cycle through it and add data to the list box component:

for (var i in userList)
{
var user = userList[i]
userList_lb.addItem(user.getName(), user.getId())
}

Not so difficult? Uh? :-)

Ok, now for the final touch: let's sort the items in the list box so that user names are in ascending alphabetical order.

userList_lb.sortItemsBy("label", "ASC")

Voilà!

The last line of the method writes the name of the just-joined room in the main chat text-area using a red font


[ MORE ON USER EVENTS ]
Before we move on to the sending/receiving message part, I'd like to analyze two simple events that are handled in this
simpleChat application.

In order to have our application behave in the right way we need to update our user list each and every time a new user enters
the room or leaves the room. This way we can have a real "real-time" view on what's going on in the room.

To keep everything in synch SmartFoxServer will send us events called onUserEnterRoom and onUserLeaveRoom that we need to handle.

Scroll the source code down a little and look at the onUserLeaveRoom, it's all very basic.

We can ignore the "fromRoom" argument as it is only used when you are logged in more than one room at the same time.
The next parameter is the User object of the newly joined user, so we can just take it's id and name and safely add it
to the listbox.

The onUserLeaveRoom handler is almost identical, only this time you will receive the user Id of the client that left the room.
In order to take its name out of our list box we have to loop through the entire component data until we find the right id.
Once it's found we just remove it. That's all!

Also we add a line in the chat text-area with the name of the user that left the room.


[ SENDING AND RECEIVING MESSAGES ]

An now for the fun part, let's send and receive messages like a real chat!

We already have our input textbox ready for use and we have a big "Send" button on stage that is linked to the sendChatMsg() function.

Let's have a look at it:

function sendChatMsg()
{
if (input_txt.text.length > 0)
{
smartfox.sendPublicMessage(input_txt.text)
input_txt.text = ""
}
}


Wow! That's difficult! ;-)

We just check that there's some text in the input field and then we send it to the other clients using the sendPublicMessage command. Also we clear the input field so that it's ready for a new message!

Now let's see how we handle the reception of public messages. The event to look for is called onPublicMessage()

smartfox.onPublicMessage = function(msg, user)
{
chat_txt.htmlText += "<b>[ " + user.getName + " ]:</b> " + msg
main_sb.setScrollPosition(chat_txt.maxscroll)
}

The event handler receives a message string (msg) and a User object (user), also we set the scrollbar position to the maxscroll property of the chat text-area. This way the last message sent is always visible.

That's all for now. Please take your time to go through all these instructions and also check the additional source code comments as they can help you in better understanding the code flow!

Also be sure to have these concepts all clear in mind before proceeding to the next section.

See ya in the next article.

Lapo


    
 
 
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