Ryerson Flash Communication Development Server  
  Ryerson Home > Ryerson Flash Communication Development Server > Flash/FlashCom Programming Notes

Flash/FlashCom Programming Notes


Introduction

These notes are based on my experience reviewing student work, fixing bugs, reading about problems on various FlashCom mailing lists, and reviewing preproduction code. They are an attempt to help people at Ryerson (students and staff) who are writing Flash/FlashCom code. For more official recommendations about things like style and good coding practices, see Macromedia's site. In particular check:

Also, while not about ActionScript, you may find this useful:

This page is very ad hoc and will grow whenever I have a chance to add to it. Also, I have simplified some of the code samples. Consequently, there is a small chance I have introduced new problems.

Improving some code samples

Sample 1: Connecting and getting a Shared Object

This sample code has two problems:

  1. It does not wait to make sure a network connection has been successfully established before attempting to connect to a remote SharedObject. Just because the SharedObject.connect method is called after the NetConnection.connect call does not mean the network connection has succeeded. This problem may not show up when testing a client on the same machine as the server. Over a slow network connection it will.
  2. The code defines the onSync method after the SharedObject.connect method is called. In this case if the connection is established before the onSync method is defined the first onSync method call may never occur.
//---------------lobby Net Connection----------//
lobby_nc = new NetConnection();
lobby_nc.onStatus = function(info) {
   trace("info: "+info.description);
};
//-------------connecting to Server--------------//
lobby_nc.connect("rtmp://"+server+"/mmsLobby/Lobby");
//---------------Shared Objects-------------------//
lobby_so = SharedObject.getRemote("Lobby", lobby_nc.uri, false);
//connecting to lobby net connection.
lobby_so.connect(lobby_nc);
lobby_so.onSync = function(list) {
   //clear the game list box.
   loginScreen_mc.game_lb.removeAll();
   //loop through all properties of the shared object 
   for (var i in lobby_so.data) {
      //add the games to the list box.
      trace("lobby info: " + list.description);
      loginScreen_mc.game_lb.addItem(lobby_so.data[i].name, lobby_so.data[i].status);
   }
};

This sample could be recoded a number of ways. Here is one possibility:

/**
 * initLobbySharedObjects initializes a shared object and connects it 
 * to the remote Lobby shared object after the network connection
 * is established. In this example it is called within the onStatus
 * handler of the lobby_nc net connection.
 */
function initLobbySharedObjects(nc){
   lobby_so = SharedObject.getRemote("Lobby", nc.uri, false);
   lobby_so.onSync = function(list) {
      //clear the game list box.
      loginScreen_mc.game_lb.removeAll();
      //loop through all properties of the shared object 
      for (var i in lobby_so.data) {
         //add the games to the list box.
         trace("lobby info: " + list.description);
         loginScreen_mc.game_lb.addItem(lobby_so.data[i].name, lobby_so.data[i].status);
      }
   }   
   lobby_so.connect(nc);
}

//---------------lobby Net Connection----------//
lobby_nc = new NetConnection();
lobby_nc.onStatus = function(info) {
   trace("info: "+info.description);
   if (info.code == "NetConnection.Connect.Success"){
      initLobbySharedObjects(this);
   }
}

lobby_nc.connect("rtmp://"+server+"/mmsLobby/Lobby");

This guarantees that:

  1. the network connection is established before attempting to connect to the remote SharedObject by making sure the network connection's onStatus method is called with an info.code success message and
  2. the onSync handler of the shared object is defined before the shared object connects so that all onSync events are received.

However, it doesn't deal with the problem of having to hard-code in the name of the list box that the onSync method must update. The answer to that problem is to make the shared object an event broadcaster and let delegate objects handle event processing. Another problem is that no onStatus method is defined. There should be one.

Sample 2: A simple FlashCom main.asc file

Here is a code sample from a simple FlashCom server-side main.asc file followed by some suggestions for improvements:

application.onAppStart = function() {
   application.LogHash = new Object();
};
application.onConnect = function(NewClient) {
   application.acceptConnection(NewClient);
   NewClient.pingServer = function(){
      return;
   }
   NewClient.loginPlayer = function(player){
      if(application.LogHash[player] == undefined){
         application.LogHash[player] = player;   
         NewClient.name = player;
         return "player logged";
      }else{
         return "player exists";
      }
   }
};
application.onDisconnect = function(client) {
   application.LogHash[client.name] = null;
};

Here are some comments/suggestions:

  1. Use the prototype object to define functions when they will be defined for every object. Every time a client connects it is wasteful to assign a method to it that all clients will have. Instead of doing this inside onConnect:
       NewClient.pingServer = function(){
          return;
       }
    do this outside of any application method:
       Client.prototype.pingServer = function(){
          return;
       }
    The same thing should be done with the loginPlayer method.
  2. Use this to assign a property to an object inside an object method. This does work:
       NewClient.loginPlayer = function(player){
    
             NewClient.name = player;
    
       }
    but consider doing this instead - I think it is less likely to lead to to problems:
       NewClient.loginPlayer = function(player){
    
             this.name = player;
    
       }
  3. Are you sure you want to store a string in a hash when the string is also the key? In the statement:
       application.LogHash[player] = player;
    It seems redundant. Also on the server-side you don't always have to compare a string to undefined the way you do on the client side this way:
       if(application.LogHash[player] == undefined){
    If you don't mind an empty string evaluating to false you could do this:
       // Don't try this in Flash:
       if(application.LogHash[player]){
    In any case, if you store the boolean value true in the hash then you don't need to test the string at all:
          if(application.LogHash[player]){
    
             return "player exists";
          }
          else{
    This is largely a matter of style. ActionScript, unlike ECMAScript/JavaScript/JScript evaluates all strings as false so you have to test if they are equal to undefined on the client.
  4. If you want to delete something from a hash then you probably do not want to just set it to null this way:
       application.LogHash[client.name] = null;
    instead use the delete keyword like this to remove it entirely from the hash:
       delete application.LogHash[client.name];
    In this case when a client disconnects it may or may not have a name property. You can show this explicitly in the code this way:
       if (client.name != undefined){
          delete application.LogHash[client.name];
    }
    even though it is not strictly necessary. If you don't want to code the if statement at least include a comment.
  5. There are no comments describing what each method does.

Here is the rewritten code:

/**
 * pingServer is called by the client to determine the 
 * time it takes to call a remote method and get a 
 * response. The function does nothing but return.
 * The timing of the round trip is done on the client.
 */
Client.prototype.pingServer = function(){
   return;
};


/**
 * loginPlayer is called by the remote client
 * to give itself a name. If the name is already
 * taken the client is told the name already exists.
 */

Client.prototype.loginPlayer = function(player){
   if(application.LogHash[player]){
      return "player exists";
   }else{
      application.LogHash[player] = true;   
      this.name = player;
      return "player logged";
   }
};
   
application.onAppStart = function() {
   // Create a hash to store player names in.
   application.LogHash = new Object();
};

application.onConnect = function(newClient) {
   application.acceptConnection(newClient);
};

application.onDisconnect = function(client) {
   // Not all clients will have a name.
   if (client.name != undefined){
      delete application.LogHash[client.name];
} };

Finally logHash isn't that descriptive. Something like playerNames would have been better.


This document was written by Brian Lesser and last updated: