[SmartFox Pro] Handle internal events
[ July 29, 2005 ] by Marco Lapi, a.k.a Lapo
Article 5: in this tutorial you will start seeing how to handle the server internal events and how they can be useful to create advanced server behaviors like, for example, your own login custom logic

The related source file for this article is found in the "Examples/mx2004/pro_loginExample" folder.


With this article we'll start seeing how to handle the server internal events in SmartFoxServer PRO, and how they can be useful to create advanced server behaviors. The example application is based on the simpleChat tutorial, one of the first and most simple tutorials of the SmartFoxServer BASIC series. The new example will show you how to intercept a "loginRequest" server event and use it to create your own login custom logic.

Internal Events

Internal server events are fired by the server engine each time an "interesting" action is executed. For example a login request has just arrived, or a new user just entered a Room in the current Zone, etc.

The available server events are:

userJoin   a user has joined the room / zone.
userExit   a user has left the room.
userLost   a user was disconnected.
newRoom   a new room was created in the zone.
roomLost   a room was destroyed in the zone.
loginRequest   a room was destroyed in the zone.
spectatorSwitched   a spectator in a game room was turned into a player.

Depending on the Level of your extension, you will be able to handle all of them (Zone Level) or only a part of them (Room Level: userJoin, userExit, userLost). You can find more info about extension levels in the Introduction to Extensions tutorial and in the Server-Side Actionscript API documentation.

Handling the login event

Before we dive in the code it's important to remember that the loginRequest event can be activated or deactivated from the Zone configuration in your config.xml file. By changing the customLogin attribute to true or false you can toggle the notification of this event. By default it is set to false.

For this example we will use the simpleChat Zone and we'll set customLogin="true" to make sure we'll be able to handle the loginRequest event.

Below you can see a simple diagram of how a login request is handled by the server, broadcasted to the extension and finally verified back by the server engine:

1) The client sends a login request, using the already known login(zone, name, password) method from the client side API.
2) The server checks if the Zone where the user wants to connect has the customLogin attribute turned on. If so the event is broadcasted to all Zone Level estensions.
3) In the above diagram we pretend that the extension C handles the login request. The user credentials are verified by your custom code and, if the check is passed, you will have to ask the server to finally allow this user in.

A this point some questions may arise: Why do I have to do this? If my custom logic says the user is valid why should I ask the server to allow this user? Well, there are still some extra system checks to pass before the server permits a new user to log in. The server will check if the user name is not duplicated in the Zone, if the user IP address is not banned, if the user name is not banned, if the Zone has room for this client, etc.

By passing this final tests the user will be successfully logged inside the requested Zone.

The client side

The client side code is almost identical to the original, non extension-based example. We have just added a new input field for the user password. Here's the code for the login request:

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

The server side

Now we can have a look at what happens on the server side. Below is shown the code of our extension:

var userList

function init()
        // Simple list of users
        // The key is the username, the value is the password
        userList = new Object()
        userList["tom"] 	= "tom"
        userList["jerry"] 	= "jerry"
        userList["smart"] 	= "fox"

function destroy()
        trace("Bye bye!")

function handleRequest(cmd, params, user, fromRoom)
        // no requests to handle here...

function handleInternalEvent(evt)
        if (evt.name == "loginRequest")
                var error = ""
                var nick = evt["nick"]
                var pass = evt["pass"]
                var chan = evt["chan"]
                if (userList[nick] != pass)
                        error = "Authentication failed"
                        var obj = _server.loginUser(nick, pass, chan)
                        if (obj.success == false)
                        error = obj.error
                // Send response to client
                var response = new Object()
                if (error == "")
                        response._cmd = "logOK"
                        response._cmd = "logKO"
                        response.err = error
                _server.sendResponse(response, -1, null, chan)

In order to keep things as simple as possible we setup a simple associative array called userList which will use the user name as the key to store each user password. When a login request is received we'll go look in that list and see if the key exist, if so we'll check if both the key and value match the user name and password sent by the client.

Now let's analyze the code inside the handleInternalEvent() method.

First we check the event name, then we get the parameters sent by the event: nick, pass, chan which they respectively represent the nickname and password sent by the client and the "socket channel" used by the client. You don't have to worry too much about this last parameter, we'll just store it and send it back to the server at the right moment (for more detailed informations about each event and their relative arguments please check the Server-Side Actionscript API documentation).

We also set an empty string variable called "error" to store a custom error message, if the login fails.
In the next lines we check the received user name against our local list of valid user: if the name is not found in the associative array the error message is set, otherwise if the credentials are correct we move on the last login step by calling the _server.loginUser() method. This method will return an object with a boolean value (success) and an optional error string if there's a problem with the final login phase.

Finally we can send our custom response to the user. We have chosen "logOK" and "logKO" as the command names that we'll receive on the client side based on the success of the request. Additionally an err property is sent to the user describing the reason why the login failed.

» Back to the client

To handle the response from the server we will use a slightly different code than the original in the simpleChat example.
The old application used the onLogin() client event handler to detect the server response. This time is not the server itself responding but your custom extension, so you will handle the response as you would normally do with any other extensions, using the onExtensionResponse() handler.

Here's the code we have used:

smartfox.onExtensionResponse = function(resObj:Object)
        if (resObj._cmd == "logOK")
                // Login Successfull
                _global.myName = resObj.name
        else if (resObj._cmd == "logKO")
                // Login Failed
                _gloabl.isBusy = true
                // Show an error window
                var win = showWindow("errorWindow")
                win.errorMsg.text = resObj.err

If the login was successful the playhead is moved to the "chat" label and you can start chatting with the other clients, otherwise a window will pop up and show the error message.

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