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
|