When working with multiuser applications, the method of choice in flash is the flash media server (fms). The main alternatives are wowza, a commercial server for streaming media and red5, the opensource alternative.
No matter what server you use, keeping track of the connected users is one of the main things that need to be done correctly. We need to be able to communicate with each and every user from our clientside application via the fms, we need to be able to keep track of them and be able to know what and how something changed about their data. We want to be able to target specific users and send them messages, we want to know which user has sent us a message etc.
The way we achieve these goals is to make a domain representation in flash of a connected user that is a subclass of our nl.dpdk.users.User class: nl.dpdk.services.fms.users.RemoteUser or an implementation of nl.dpdk.services.fms.users.IRemoteUser for a lightweight adaptation specific to your application. This allows us to keep track of simple stuff like the user’s nickname and other publicly available data for quick overview. But this also means, that if or when we have some extended interaction with a user from which we get additional data, we can store the data on this object too. All the time, we only have 1 representation of the user in our application code and we have only 1 unique identifier that tells us who the user is: the RemoteId.
A RemoteId is something we assign to a connecting flash client when a user connects to the fms. The easiest way is to just assign it to the Client object on the fms side like this:
application.onConnect = function(client){ //increase some application id and set it on the Client object client.remoteId = ++application.ID; //store an easy reference to the client for quick lookup clients[client.remoteId] = client; //accept the connection application.acceptConnection(client); //send the remoteId to the flashclient, so the client knows who he is //update, thanks to Ercan for noticing the bug in demo code. client.call('setRemoteId', null, client.remoteId); }
This is a trivial example and we would normally hide this behind a serverside actionscript (ssas) abstraction, but for demo purposes it is enough. What we get from this, is that each connected client has a unique id set on it, that is given by the fms. On top of that, we can do an easy lookup if we only have a remoteId on the server by storing it inside an object. Whenever there are more properties to be stored on the Client object, we can always get to them by looking up the Client object by it’s remoteId and then getting the other properties from it. Also, we communicate the remoteId to the connected flash client itself, so it knows it’s id when communicating with others.
In our case, we mostly use a ssas User object that we set on the Client Object. We can access and use this object by looking it up on the Client object:
//get the client object with remoteId 12, and from that, get the user object. var user = getClientById(12).getUser(); //do something on the user. user.doSomething();
The user object on the Client object stores multiple type of id’s if necessary, but at least the remoteId. Optional fields are a database id that comes from the representation of a user in a database and a session id that might only be valid for a certain time when used with a ticket/validation system.
The database id might be necessary because the backend server with the database and the php server on it do not use a remoteId. Communication there is managed by database id’s, which will mostly be hashed id’s (strings), or guids. But the remoteId still serves as a mapping from flashclients to users in the fms application. In this way, we will be able to communicate with the backend to log data and track and validate user activity without sending sensitive data on the nonsecure line from fms to flash and from flash to fms. Only the fms knows database id’s and their mappings to users via their remoteId. Connected flash clients only know the remoteId of other connected clients.
Back to the RemoteId. Conceptually speaking, it is only an integer on the flash media server. But we will see that it will give us tremendous power when handling the clients from an application. As we saw before, the id is communicated to the flash movie and is handled by the nl.dpdk.services.fms.FMSService and the nl.dpdk.services.fms.FMSConnection
//in FMSConnection, which is a subclass of NetConnection to define the methods that the fms can call into the flash client. public function setRemoteId(id : int) : void { var remoteId : RemoteId = new RemoteId(id); dispatchEvent(new RemoteIdEvent(RemoteIdEvent.REMOTE_ID, remoteId)); } //in FMSService, which we use (or subclass) in our application, we listen to the FMSConnection and we pass on the remoteId to our FMSService client. private function onRemoteId(event : RemoteIdEvent) : void { dispatchEvent(event); }
We only have to listen to the event broadcasted by the FMSService in our application to use the remoteId. All of this is handled automatically, we only need to use the RemoteId in a meaningful way in our flash application.
The way to handle this is by setting the RemoteId on a RemoteUser instance, which we can easily track in our application. A RemoteUser, a subclass of User, is the domain representation of the user that is connected to the media server. It can be used to keep track of all information that belongs to a user, such as nickname, gender, email. In games, this might also be the currentscore, highscore, games played and other user profile stuff. All this data is coupled to the diverse clients by means of their remote id.
All the time, this data might live on the fms in a Remote Shared Object (rso), a list we can retrieve from fms on demand or whatever implementation we might choose for this (although rso’s are very convenient). In case of an rso, the data that we want to put on the list for each user, is set by using the remoteId as the property that identifies the data on the rso:
//in application.onAppStart, define the shared object application.users = SharedObject.get('users', false); //in application.onConnect or in a change handler when properties of the user change application.users.setProperty(client.remoteId, getPublicUserData(client)); //somewhere else we define this method, which returns an object with the properties we would like the world to see function getPublicUserData(client){ var user = client.getUser(); var data = new Object(); data.name = user.getName(); data.email = user.getEmail(); //etc., in a real life situation, the properties on the rso data object would have really short names for bandwidth conservation purposes. return data; }
As you can see, there is a property set on the rso, with an object on it that contains data about the user. The property is the RemoteId.
When we connect to the rso from our flash movie, we can use the property identifiers from the rso itself to distinguish the different users, because the property identifier is the RemoteId. We would convert all the users from the rso to RemoteUser domain objects, store these on a list and use these throughout our application code. Whenever we want to do something to a user like sending him a chat message, we send a message with a remoteId as the target, which can then easily be handled by the fms through the lookup system mentioned earlier.
The communication would look something like this:
//in a subclass of FMSService, that implements the connection to the users rso we would have code like this //before we connect to the users rso we register to some listeners (see nl.dpdk.services.fms.rso) users: FMSSharedObject = new FMSSharedObject('users', this.getConnection()); users.addEventListener(FMSSharedObjectDataEvent.NEW, onUserNew); users.connect(); public function onUserNew(data: FMSSharedObjectDataEvent):void{ //pass the FMSSharedObjectData instance to a dedicated factory object that returns a domain object var user: RemoteUser = UserFactory.createFromFms(data.getData()); //dispatch a UserEvent containing a User object to all listening clients of FMSService dispatchEvent(new RemoteUserEvent(RemoteUserEvent.NEW, user)); } public function sendChat(chat: Chat, to: RemoteUser):void{ //simple conversion for demo purpose, we would probably create an object via a factory in a real application. call a method on the flash media server, defined on Client.prototype. getConnection().call('sendChat', null, chat.getMessage(), to.getRemoteId().getId()); } //UserFactory code (in a seperate class) public static function createFromFms(data: FMSSharedObjectData): RemoteUser { var user: RemoteUser = new RemoteUser(); user.setRemoteId(new RemoteId(data.getId())); user.setNickName(data.getData().name); user.setEmail(data.getData().email); return user; }
Some sample and test code can be found in the repository where you can see how you might do a simple setyp. Be sure to check out the unit tested samples. Although implemention varies accross the mentioned servers (fms, red5 and wowza), the idea of a RemoteId can be succesfully applied to application logic on each server, and nothing will change on the flash client.
This article is probably too short to go really indepth, but feel free to add comments or ask questions and we will try to answer them. In any case, check out the nl.dpdk.services.fms package to see how to handle remote shared objects and fms connections in more generalized way then when using the out of the box flash functionality.
Happy coding ![]()
Is this right?
application.onConnect = function(client){
//increase some application id and set it on the Client object
client.remoteId = ++application.ID;
//store an easy reference to the client for quick lookup
clients[client.remoteId] = client;
//accept the connection
application.acceptConnection(client);
//send the remoteId to the flashclient, so the client knows who he is
–> client.setRemoteId(client.remoteId);
// Ercan this must be
client.call(”setRemoteId”,client.remoteId); // ??
}
Hi Ercan,
that’s right, thanks for noticing.
Whenever the flash media server is targetting a specific flash client is does so via the fms Client object.
THe correct code is below, as we are also able to pass a responder object (null in this case).
client.call(’setRemoteId’, null, client.remoteId);
I’ll update the article
kjhjkj
Wow, awesome blog format! How lengthy have you ever been blogging for? you make blogging look easy. The whole look of your site is excellent, as smartly as the content material! As consequence: thanks a lot and please please keep up the good work. I’ll surely come back and check for new articles! See yah
I’m writing this app where a student client raises a request to ask a question and when the teacher client accepts the request, the video is streamed to the teacher. I’m using FMS and flash cs5.5. Can you tell me how I could do this using RemoteId?