5.2 Tutorials: Simple Chat (part 1)
The source FLA of this example is available under the Examples/(Actionscript version)/simpleChat folder. |
» Introduction
In this tutorial, together with the next one, you'll learn how to build a basic chat application that will be able to:
» connect to the server
» log in users
» send and receive user messages
Open the source .FLA and have a look at the timeline. Don't worry about the amount of 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.
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.
» 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.
» Connect
In this screen we will use most of the code from the previous tutorial: 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 tutorial, 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 tutorial, 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 "SmartFoxServer/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.
The _global.isBusy variable it's used by the error-message window to disable the other GUI buttons. We'll cover this topic in the second part of this tutorial.
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 _global.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.
» 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 four 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 second 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 with a list of available rooms to join.
This happens because upon reception of a successfull login the client API automatically sends 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 statuses, but this is far beyond the purpose of this tutorial.
We'll analyze these more advanced concepts in the next ones.
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
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 move 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.
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 (remember, it is an array of User objects):
var userList = roomObj.getUserList()
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()) }
Finally we have to sort the items in the list box so that user names are in ascending alphabetical order:
userList_lb.sortItemsBy("label", "ASC")
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 can move on to the sending/receiving message part, we have 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 onUserEnterRoom, 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.
Also we add a line in the chat text-area with the name of the user that left the room.
» Sending and receiving messages
Now we can examine the heart of a chat application: how to send and receive messages.
We already have our input textbox ready for use and a "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 = "" } }
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.
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. In the next tutorial we will examine a little more in detail the code created so far.
doc index |