Network Programming

Flash Player 9 Local swf Security

Let’s say you had an external settings file that you wanted to load using the following code:
[as]protected var _loader:URLLoader = new URLLoader();
_loader.addEventListener( Event.COMPLETE, function():void { trace(“settings.txt was loaded successfully”); } );
_loader.load( new URLRequest(“settings.txt”) );
[/as]
You finish writing your class, save your fla, and compile. After compilation completes, the code above properly trace s “settings.txt was loaded successfully” (assuming settings.txt actually exists in the proper location). Yay, it worked!

However, if you were to immediately run this swf file a second time, you would not see this trace statement. “Why not!?” you may ask in complete frustration after trying for 20 minutes to figure out what the heck is going on and have a few less hairs on your head and a few more in your clenched fists (or maybe that’s just what happened to me).

By default, when you compile any class using the Flash IDE, it is automatically “trusted,” but for this single execution of the swf file that immediately follows the compilation only. All other flash files run from your local system, by default, are not trusted. This means that they cannot access external assets (such as text files) or communicate using the network. This may seem frustrating when you’re developing, but since the capabilities of the FP are growing so much, this is a very good thing or hackers would be going crazy and everyone would be very ticked at Adobe.

In order for local swf’s to have further privileges, they must be “trusted.” There are two main ways to “trust” a swf:

  1. Add the swf or folder containing the swf to the Global Security Settings panel
  2. Add the swf or folder containing the swf to the FlashPlayerTrust.

The first method can be completed by doing the following:

  1. Run any swf file.
  2. Right click on the swf file and choose “Settings”
  3. Click on the “Privacy” tab and click on “Advanced…”
  4. This should bring up a Flash Player Settings Manager website (note that steps 1-3 can be skipped by directly accessing this site).
  5. On the left-hand pane, click “Global Security Settings Panel
  6. Click on “Edit locations…” and then “Add location…”
  7. Choose the file or folder that you wish to “Trust” and press “Confirm”

The second method can be completed by doing the following:

  1. Open the FlashPlayerTrust folder on your computer.
    • Windows: system\Macromed\Flash\FlashPlayerTrust
      • For example: C:\windows\system32 \Macromed\Flash\FlashPlayerTrust
    • Mac: app support/Macromedia/FlashPlayerTrust
      • For example: /Library/Application Support /Macromedia/FlashPlayerTrust
    • Linux: /etc/adobe/FlashPlayerTrust
  2. Create a new text file (any text editor should do) and call it whatever you want, but with a .cfg extension.
  3. Add the absolute path of the file or folder that you wish to to the text file.
  4. Save the text file.

Note that the locations listed above are for trusting flash content for all users. If you wish to trust content only for a specific user, use the following FlashPlayerTrust locations:

  • Windows: app data\Macromedia\Flash Player\#Security \FlashPlayerTrust
    • For example: C:\Documents and Settings\JohnD \Application Data\Macromedia\Flash Player \#Security \FlashPlayerTrust
  • Mac: app data/Macromedia/Flash Player/#Security /FlashPlayerTrust
    • For example: /Users/JohnD/Library/Preferences /Macromedia/Flash Player /#Security/FlashPlayerTrust
  • Linux: usr data/adobe/FlashPlayerTrust
    • For example: /home/JohnD/adobe/FlashPlayerTrust

For further information, see the flash_player_9_security.pdf white paper.

Registered Socket Functions

Now that you know how to dynamically cast variables at runtime, we can create a very cool static class I dub RegisteredSocketFunctions. The purpose of this class is to allow any function to be “registered,” which, by doing so, allows global access to the function directly.

The goals of the RegisteredSocketFunctions class:

  • Any function in any class can be very easily “registered.”
  • Once registered, any function in any class can be executed, maintaining all parameter functionality.
  • Any implied or direct usage of the “this” expression in the executed function will be interpreted properly.

Here’s the API:
[as]
/**
* Adds a new function to the list of available socket function calls.
*
* @param sFName Socket function call string reference name.
* @param funcReference A Function reference to the actual function.
* @param thisObject A reference to the object that should be the “this” object when this function is executed.
*
* @usage RegisteredSocketFunctions.registerFunction(“myTestFunction”, myTestFunction, this);
*/
public static function registerFunction(sFName:String, funcReference:Function, thisObject:Object):void
[/as]

[as]
/**
* Removes a function from the list of registered socket function calls.
*
* @param sFName The socket function call that should be removed.
*
* @usage RegisteredSocketFunctions.unregisterFunction(“myTestFunction”);
*/
public static function unregisterFunction(sFName:String):void
[/as]

[as]
/**
* Executes a Function that was previously registered using registerFunction with the name sFName.
*
* @param sFName The string reference name of the function that is to be executed.
* @param argArray The optional list of parameters that should be executed with the function.
* @param thisObject The optional object to which the Function referenced by sFName is applied. The “this” operator for the executed function will be thisObject if thisObject is non-null; otherwise, it will be the thisObject specified when registerFunction was called.
*
* @return Returns whatever value (if any) is returned by the function being called.
*
* @usage RegisteredSocketFunctions.registerFunction(“myTestFunction”, [var1, var2]);
*/
public static function executeFunction(sFName:String, argArray:Array = null, thisObject:Object = null):*
[/as]

Example:

Let’s say you have the following class
[as]
package
{
import com.natejc.utils.communication.RegisteredSocketFunctions;

public class RSFTestClass
{
private var _sName:String;
private var _nAge:uint;

public function TestClass()
{
RegisteredSocketFunctions.registerFunction(“testFunc”, testFunc, this);
} // END CONSTRUCTOR

public function testFunc(sName:String, nAge:uint):String
{
this._sName = sName;
this._nAge = nAge;

trace(“Hey ” + _sName + “, I can’t believe you’re ” + String(_nAge) + ” years old!”;
return sName + String(nAge);
} // END FUNCTION testFunc

} // END CLASS TestClass

} // END PACKAGE
[/as]

Then any other class in any other function could execute testFunc by doing the following:
[as]
var s:String = RegisteredSocketFunctions.executeFunction(“testFunc”, ["Joe Mama", 90]);
trace(s);
// which traces:
// Hey Joe Mama, I can’t believe you’re 90 years old!
// Joe Mama90
[/as]

Caution:

This class is intended to allow execution of functions across a network (which we will be discussing further later). Although this may seem like a cool, time saving way to allow classes all over your code interact with each other, doing so is definitely poor coding practice. Just like in real life, a child should never have command over its parent ;) . Having said that, if your project is due in 1 hour and you don’t have time to write all of the event handling that you should write, well, congratulations; you just found a class that will encourage your bad habits…

Download: RegisteredSocketFunctions.as

Dynamic Casting and * in ActionScript 3

This discussion is necessary in order to continue with our network programming lessons.

Casting (converting one variable type to another) in ActionScript 3 is very easy. The following code casts a uint as a String:
[as]var nNum:uint = 5;
var sNum:String = String(nNum);[/as]
But what if you don’t know what the variable should be cast as? For example, let’s say you have a client-server application. The server sends you an XML packet that contains the data for a variable and it tells you what the type of the variable should be with a written String expression. Your goal is to store this data in a variable of the proper type.

First, you have to know about the * (Special Type) operator. This can be used to create a variable that is “untyped.” For example, the following function can take any type of variable as a parameter:

[as]public function helloUntypedVariable(myVar:*):void
{
trace(“hello ” + String(myVar));
}[/as]
If you have an untyped variable you can determine which type of variable it is at runtime using the is operator:
[as]public function customTypeOf(dataObject:*):String
{
if (dataObject is uint)
return “uint”;
if (dataObject is String)
return “String”;
}

var n:uint = 5;
var s:String = “Hello World”;

trace( customTypeOf(n) ); // traces “uint”
trace( customTypeOf(s) ); // traces “String”
[/as]
It’s important to note that the “is” operator returns true for anything and everything that the variable in question is qualified to be. For example, the following code all returns true:
[as]var n:int = 5;
trace(n is uint); // traces “true”
trace(n is int); // traces “true”
trace(n is Number); // traces “true”
trace(n is String); // traces “true”[/as]
Even though n is of type “int”, it even returns true in the statement “n is uint” because the value is positive and therefore qualifies to be a uint.

Now we know enough to create our “CustomCaster” utility class. This static class has two purposes: to determine the most restrictive type of a variable (CustomCaster.customTypeOf) and to cast a variable as some other data type that can be specified at runtime (CustomCaster.customCast):

Download the latest version here.

Flash Network Programming via TCP/IP, part 2

Today, I give you a free utility class that handles everything you need to handle in order to establish a connection. Tomorrow, I give you the world a class that make it seem like you are calling functions directly on the server and visa versa.

Download the latest version of the ConnectionEstablisher class here.

Usage example:
[as]// To establish a new connection, do the following:
var _server:ConnectionEstablisher = new ConnectionEstablisher(“127.0.0.1″, 4500);

// To send data to the server, do the following:
var xmlPacket:XML =
2007
Toyota
Baja 1000 FJ Cruiser

_server.send(xmlPacket);[/as]

Flash Network Programming via TCP/IP, part 1

A while back I led an ActionScript 2 project with two other classmates in a Network Programming class I was taking at EWU. We created a Flash game we called “Zelda Arena” in which multiple players on multiple computers would connect via TCP/IP to a C++ server. You could custom color your own hero (Link) and duke it out against each other with your sword. I wanted to take the idea a lot further, but figured Nintendo might not appreciate it. Here are a couple of screen shots:

Zelda Arena screenshot 1 Zelda Arena screenshot 2
Zelda Arena screenshot 3 Zelda Arena screenshot 4

If Nintendo happens to read this, I would love to build you this game for your website! A guarantee it would generate a lot of traffic. But alas, you will probably just steal my idea. Then again, I guess I stole yours first. :)

Anyway, I have since redone a lot of ActionScript 3 networking code. In the near future, I’ll post some further information on Flash/AS3 networking and share some free classes to simplify the process. Stay tuned…