[SmartFox] Multi Chat
[ April 14, 2005 ] by Marco Lapi, a.k.a Lapo
Article 11: how to join one user in multiple rooms at the same time


[ INTRODUCTION ]

In this new article we'd like to discuss a particular feature of SmartFoxServer: the ability to join one user in multiple rooms at the same time.

This feature can be pretty useful when you need to develop medium complex applications and games.
For example you could have a chat system where you can talk to other users in the "Main Lobby" and simultaneously have other small windows where you can chat in other public rooms. This could be easily achieved by using the multi-room capability of SmartFoxServer.

Another idea would be to have a main window for chatting plus one ore more game rooms where you can simultaneously play with your mates a turn-based game like chess, checkers, connect-four and so on...

[ REQUIREMENTS ]

Before proceeding with this tutorial it is necessary that you're already familiar with the basic SmartFoxServer concepts explained in the previous articles.

[ OBJECTIVES ]

In this article I will demonstrate how the "MultiChat" example file works.
The "MultiChat" is a simple chat application that allows each user to talk in a room called "Main Lobby" and also to create and join other custom rooms where you can chat simultaneously.

As soon as the user logs in the chat he's automatically joined in the "Main Lobby" using the autoJoin() command.
He will be able to create new chat rooms and join one of those without leaving the "Main Lobby" keeping two public chats open at the same time.

[ THE BASICS ]

This new demo stays consistent with the other examples in terms of code organization, so the main timeline should look familiar when you will see it the first time.
As usual we have a frame labeled "connect" where we setup the basic variables and object needed to initialize the application and connect to SmartFoxServer.

For this application we'll use a zone called "MultiChat" that is defined in the config.xml file on the server side:

<Zone name="multiChat">
	<Rooms>
	<Room name="Main Lobby" maxUsers="50" isPrivate="false" isTemp="false" autoJoin="true" />
	</Rooms>
</Zone> 

The code in the "connect" section is exactly the same we've been using in the other tutorials, so we can safely move to the next frame labeled "chat".

[ HANDLING MULTIPLE ROOMS ]

In one of the previous examples we mentioned a property of the SmartFoxClient class called "activeRoomId".
This property keeps track of the latest joined room and it is used by the client API in order to know where the user is located.

Even if this property is not used very much by the Flash developer who is working with SmartFoxServer it is very important behind the scenes because every message that is sent to the server has a roomId property that tells the server from which room the message is coming from.

In other words when you call the server.sendPublicMessage("hello!") the client API sends your text and the room id of the room where the user is currently in. If you inspect the API or just read the API documentation provided in the SFS package you will notice that all methods have an optional roomId parameter that can be used to specify the room from which the message is coming from.

With this in mind the complete form of the sendPublicMessage() method is: server.sendPublicMessage(message, roomId)

The reason why we've never passed the second parameter is because all the other examples can log the user in one room at a time, so the client API already know where the user is located and automagically sends the roomId if we don't specify it.

Now that we want to be present in more than one room at the same time we will have to keep track ourselves of the different rooms where the user is located and send the appropriate id. For example if you're simultaneously connected in the "Main Lobby" and in the "Soccer Room" you will need to specify in which of the two rooms you want your message to be sent to.

In this "MultiChat" example we're going to be automatically joined in the "Main Lobby" right after the login and we'll be able to join another room keeping two public chat windows on screen at the same time.
If you inspect the stage contents at the frame labeled "Chat" you will see how the "double-view" interface is organized: the left and right parts of the screen are symmetrical with a list box on top for user names, a text area in the middle for the public chat messages and a single line input box for sending messages.

In the middle is located another list box that will show all the available rooms, together with a button for creating new ones (labeled NEW) and a button for leaving the "secondary" room. (labeled LEAVE)

[ ROOM ID VARIABLES ]

At the very beginning of the code located under the "chat" label you will find these two variables:

var mainLobby:Number = -1
var privRoom:Number = -1

We will use these two values to track the roomId number of the "Main Lobby" and the one of the secondary chat room.
At start up the variables are set to -1 to indicate that you're currently not logged in any of the two and we'll assign them the right values as soon as the user joins the "Main Lobby" and the secondary room.

As usual we handle the onRoomListUpdate event by populating the room list box:

smartfox.onRoomListUpdate = function(roomList:Object)
{
        roomList_lb.removeAll()
        
        for (var i:String in roomList)
        {
                var room:Room = roomList[i]
                roomList_lb.addItem(room.getName() + " (" + room.getUserCount() + ")", room.getId())
        }
        roomList_lb.sortItemsBy("label", "ASC")
        
        // Join the default room
        this.autoJoin()
        
}

The last line in the method calls the autoJoin() requesting to be joined in the default room of the current zone.
Now let's have a look at the onJoinRoom handler:

smartfox.onJoinRoom = function(roomObj:Room)
{
        var txtObj:Object
        var box:Object
        
        if (roomObj.getName() == "Main Lobby")
        {
                mainLobby = roomObj.getId()
                txtObj = mainChat_txt
                box = mainList_lb
        }
        else
        {
                privRoomName_txt.text = roomObj.getName()
                privRoom = roomObj.getId()
                txtObj = privChat_txt
                box = privList_lb
        }
        
        var roomId:Number	= roomObj.getId()
        var userList:Object	= roomObj.getUserList()
        
        resetRoomSelected(roomId)
        
        _global.currentRoom = roomObj
        
        // Clear text area
        txtObj.htmlText = ""
        
        // Clear current list
        box.removeAll()
        
        // Add items to the listbox
        for (var i:String in userList)
        {
                var user:User = userList[i]
                box.addItem(user.getName(), user.getId())
        }
        
        // Sort names
        box.sortItemsBy("label", "ASC")
        
        // Add a notice in the chat box
        txtObj.text += "<font color='#cc0000'>>> Room [ " + roomObj.getName() + " ] joined</font>";
}

As you can notice the code checks if we've joined the "Main Lobby" or just one of the other available rooms.
In the first case the mainLobby variable is assigned the roomId and the box variable is pointed to the listbox on the left side of the screen. If we've joined another room we store its roomId in the privRoom variable and box is pointed to the listbox on the right side of the screen.

The rest of the code just populates the appropriate list box with the names of the users in the room.

[ THE JOIN() COMMAND ]

The smartFox.join() method has already been inspected in the past tutorials and we know it is pretty simple to use: you just pass the name or id of the room you'd like to join.

In the previous examples we have seen how a room called "The Hall" could be joined simply by using this line of code:

smartFox.joinRoom("The Hall")

Now it is time to see the advanced usages of this command. Here follows the complete set of paramaters that you can pass to the method:

smartFox.joinRoom(roomId, password, isSpectator, dontLeave, oldRoom)

roomId   the id of the room you want to join
password   the password for the room (needed if the room is password protected)
isSpectator   (optional booelan flag) If true joins the user as a spectator in a game room
dontLeave   (optional boolean flag) If true the user will not leave the room where he/shw is currently in
oldRoom   (optional) the roomId of the room to leave

As you can see the last two arguments are those we need to instruct the server that we don't want to leave the room we're currently in, while we're joining a new room. Actually the last one allows the developer to specify the id of the room he'd like to leave when he has joined the new one.

Let's take a look at the changeRoom() method which handles the room change request when a user clicks in the room list box:

function changeRoom(evtObj)
{
        if(!_global.isBusy)
        {
                // new Room id
                var newRoom:Number = evtObj.target.value
                
                if (newRoom != mainLobby && newRoom != privRoom)
                {
                        // Check if new room is password protected
                        var priv:Boolean = smartfox.getRoom(newRoom).isPrivate()
                        
                        if (priv)
                        {
                                // Save newroom as _global for later use
                                _global.newRoom = newRoom
                                
                                showWindow("passwordWindow")
                        }
                        else
                        {
                                // Pass the room id
                                if (privRoom == -1)
                                smartfox.joinRoom(newRoom, "", false, true)
                                else
                                smartfox.joinRoom(newRoom, "", false, false, privRoom)
                        }
                }
        }
}

As you may recall the _global.isBusy variable is a flag that tells us if the application GUI is currently busy, so we check it before proceeding with the rest of the code. In the next line we get the value of the item selected in the list box and we check it against the two room Ids we've stored previously.
First of all we verufy if the clicked room is different from the current ones, then we have to see if we're trying to log in a private room: if so we'll show the password dialog box and ask the user to type a valid password.

The approach used here is the same of the previous tutorials: we store the id of the selected room in a _global.newRoom variable, we launch the dialog box by using the showWindow() function and we handle the user input in the loginProtectedRoom() method.

Let's analyze the remaining four lines of code: before sending the join request we have to check if this is the first time we are logging in a "secondary" room. If privRoom is equal to -1 then we can just join the new room using the dontLeave flag set to TRUE, otherwise we have should slightly modify the joinRoom request telling the server that we want to leave the previous secondary room.
If you forget to do that, you will join the other rooms without leaving the current one keeping your user present in as many room as you want at the same time. In general this is a bad idea since the more rooms you join simultaneously the more traffic will be generated on the server to send your client all the events from all the connected rooms.
Also handling all those events from so many rooms can make your application difficult to code.

Usually two or three rooms at the same time can be more than enough for any advanced multiuser applications.
An example of using this technique could be a chat with turn-based games where you can keep talking in public chat rooms while waiting the move of your opponent in a game of chess, checkers etc...



    
 
 
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