NOTE: The permanent version of this tutorial, updated as SwfConduit develops, is located at our project wiki: https://github.com/doublecluepon/SwfConduit/wiki/A-simple-chat-server. You can ask questions about this tutorial in our SwfConduit Forum.

As part of the first stable release of SwfConduit, Double Cluepon’s free, open-source flash socket server written in Python, I’ve prepared a simple example to show how to get started writing your own servers.

In this example, we’ll create a very simple chat room. Users will join the room by launching a swf file and then they can talk with all the other users in the room.

Installing SwfConduit

Server Requirements

First, we will need to install the server’s requirements:

  • Python 2.6+ (Python 3 will not work)
  • Twisted
  • PyAMF 0.6

Twisted and PyAMF can both be installed using Python’s easy_install:

$ sudo easy_install twisted   
$ sudo easy_install pyamf

Finally, we can get the latest SwfConduit from https://github.com/doublecluepon/SwfConduit/tarball/master

Client Requirements

Using Flash Builder

If you’re using Flash Builder, you should have everything you need to create SwfConduit projects.

Using any editor

If you want to use another editor, you can download the free Adobe Flex 4 SDK from http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4. Once you’ve downloaded the Flex SDK, you’ll want to make sure you can run “mxmlc” from your terminal.

Running the Example

Start the server

Start the chat server by invoking twistd from a terminal:

$ cd examples/chat
$ sudo twistd -ny chat.py

A bunch of text should scroll onto the screen, resembling the below block

2011-05-28 22:42:31-0500 [-] Log opened.
2011-05-28 22:42:31-0500 [-] twistd 11.0.0 (...) starting up.
2011-05-28 22:42:31-0500 [-] reactor class: twisted.internet.selectreactor.SelectReactor.
2011-05-28 22:42:31-0500 [-] swfconduit.socketpolicy.SocketPolicyFactory starting on 843
2011-05-28 22:42:31-0500 [-] Starting factory <swfconduit.socketpolicy.SocketPolicyFactory instance at 0x10196eb48>
2011-05-28 22:42:31-0500 [-] swfconduit.server.Server starting on 8000
2011-05-28 22:42:31-0500 [-] Starting factory <swfconduit.server.Server instance at 0x10196eab8>

If there are no error messages, the server is running. Leave the terminal open while you start the client.

Start the client

Using Flash Builder

First, we need to create a new Flex Project. We’ll call our new project Chat. Give it an Application Type of “Web” and click Finish.

Creating the project

Next, we need to import the swfconduit libraries. Right-click on the “libs” folder in the Package Explorer and choose “Import…” from the pop-up menu. In the dialog box, open the General folder, choose “File System” and click Next. Choose the swfconduit/flex folder and make sure it’s checked in the box on the left. Then click Finish to import the files.

Click the Import... menu item

Choose "File System"

Select the swfconduit directory

Files are imported

Finally, we need to import the files from the swfconduit/examples/chat folder. We follow the same procedure as we did when we imported the swfconduit libraries. Right-click on the “src” folder in the Package Explorer and choose Import… from the pop-up menu. In the dialog box, open the General folder, choose “File System” and click Next. Browse to the swfconduit/examples/chat folder, make sure it’s checked in the box on the left, and click Finish to import the files.

Importing the Chat source

Select "File System"

Select the chat directory

Chat directory is imported

Now, we can open our chat.mxml file and Run it. Your browser will open and the chat window will appear. If the server is running (it should be), you’ll be able to chat.

Open Chat.mxml

Running Chat in Chrome

 

Using any editor

Compile the chat.swf by invoking mxmlc:

$ cd examples/chat
$ mxmlc -l+=../../flex chat.mxml

Then, open the chat.swf file and try it out.

You can use /nick NEW_NICK to change your nickname, and opening the chat.swf multiple times will allow you to talk to yourself without danger of people thinking you’re weird or sending you to therapy.

Creating the Example

The Server

The SwfConduit server handles messages using Event classes. The client sends events to the server, and the server sends events to the client. All the communication is asynchronous, so the server doesn’t have to wait for the client to make a request.

So, the first thing we need to do to build a chat server is create the ChatEvent class. First we import the swfconduit Event base class and create our subclass.

# Create our event class
from swfconduit.event import Event
class ChatEvent( Event ):

Next, we add the properties of the event. ChatEvent needs a nickname of whosent the message, and the actual text of the message.

    # The nickname of the user sending the event
    nickname = ""
    # The message
    text = ""

Finally, we need to describe what to do when the server recieves a ChatEvent. SwfConduit events have a fire() function that gets called when the server recieves it. fire() recieves the swfconduit Server object for the current server, and the swfconduit Session object which is the session that sent the event.

In our fire() function, we’re going to loop over all the other connections to the server and forward the event to them.

    def fire( self, server, session ):
        # We recieved a chat event, send it to every other person
        for session_id in server.sessions:
            s = server.sessions[session_id]
            if (s is not session):
                s.sendEvent( self )

Once we’ve built our event class, we need to register the class with PyAMF in order for it to be correctly serialized. We’ll also need to register the ChatEvent class we create on the client, later. The server and client will both share the ID string “swfconduit.chat.ChatEvent” to know what they’re serializing.

# Register our ChatEvent class. the first argument is the same as the
# registerClassAlias string in the client
import pyamf
pyamf.register_class( ChatEvent, "swfconduit.chat.ChatEvent" )

With the event class created and registered, we can create our server using the swfconduit Server class. Our server will use the TCP protocol on port 8000, so we’ll pass that into the server’s configuration dict.

# Create the SwfConduit server
from swfconduit.server import Server
server = Server({ "proto" : "tcp", "port" : 8000 })

Now, we’ll load up the application that twistd will execute for us using the swfconduit Loader. The Loader will also handle the socket-policy.xml file for us, which is placed in the same directory as our server program.

# Load and run SwfConduit
from swfconduit.loader import Loader
loader = Loader()
loader.servers.append( server );
application = loader.get_application()

That’s it! We can now run our server:

$ twistd -ny chat.py

The Client

Now that we have our server, we need something to connect to it. We’ll create a simple MXML component that will take input from the user and display the incoming chat messages.

First, like our server, we need a ChatEvent. On the client, swfconduit events extend the swfconduit.Event class.

package swfconduit.chat {
    import swfconduit.Event;
    public class ChatEvent extends swfconduit.Event {

We make the same public attributes as the server’s ChatEvent. If the two disagree, the flash client will warn you about the attributes it can’t find.

        // The person who sent the event
        public var nickname:String;
        // The message being sent/recieved
        public var text:String;

Finally, we’ll make a constructor that makes it easier to create ChatEvent objects. If we override the constructor for an Event, we must provide defaults. When the Event is sent by the server to the client, Flash will use the constructor, but will not pass any arguments to it.

        public function ChatEvent( nickname:String="", text:String="" ) {
            this.nickname = nickname;
            this.text = text; 
        }
    }
}

And that’s all our ChatEvent needs to do. Notice how the client doesn’t do any handling of the Event. Unlike the server, the client’s handling of the event is done using event listeners.

But before we can listen for ChatEvents, we have to build a form. We’ll create a Spark Application that has an area for the chat, an input field to type into, and a label for the user’s current nickname.

<?xml version="1.0" encoding="utf-8"?>
<s:Application width="400" height="300" 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
>
    <s:layout><s:VerticalLayout /></s:layout>
    <s:RichEditableText editable="false" id="chatbox" width="100%" height="250">
        <s:text>*** Type /nick NEW_NAME to change your nickname</s:text>
    </s:RichEditableText>
    <s:TextInput id="input" width="100%" height="25" />
    <s:Label text="You are {nickname}" height="20" />
</s:Application>

We’re using RichEditableText so that we get scrolling automatically, but we’resetting editable to false because users should be typing into the TextInputbelow it. Finally, a Label with text bound to the “nickname” field (that wehave yet to create).

So now that we have our form, we’ll add our script. First we need to import the swfconduit libraries and our ChatEvent. Since we’re also going to be working with keyboard events (pressing Enter to send text), we’ll import the flash KeyboardEvent class.

    <fx:Script><![CDATA[
        import swfconduit.Socket;
        import swfconduit.chat.ChatEvent;
        import flash.events.KeyboardEvent;

Now, we'll need to keep some data around, like the hostname and port to connect to, the connected socket to the swfconduit server, and the user's nickname.

        public var hostname:String = "localhost";
        public var port:uint = 8000;
        public var socket:swfconduit.Socket;
        [Bindable]
        public var nickname:String;

Next, we’ll need to connect to the server. We’ll create an init function that will register our classes and connect to the server.

        public function init():void {
            // Register our event class
            registerClassAlias( "swfconduit.chat.ChatEvent", ChatEvent );
            // Connect to the server
            socket = new swfconduit.Socket( hostname, port );
        }

We’ll need to call our init() function when our application is completed, so add ‘applicationComplete=”init()”‘ to our <s:Application> tag.

<s:Application width="400" height="300" applicationComplete="init()"

Next, we need to handle incoming ChatEvent messages. As they come in, we need to add the text to our chatbox text area.

        public function handleChat(event:ChatEvent):void {
            chatbox.appendText( "\n" + event.nickname + ": " + event.text );
        }

We add our event listener in our init() function, after we create our socket.

        public function init():void {
            // Register our event class
            registerClassAlias( "swfconduit.chat.ChatEvent", ChatEvent );

            // Connect to the server
            socket = new swfconduit.Socket( hostname, port );
            socket.addEventListener( "ChatEvent", handleChat );
        }

Now, as ChatEvents come in, they’ll be added to the chatbox. Finally, we need to send out ChatEvents of our own. We’ll add a handler for the Enter key so that it sends out the message.

        public function sendChat(event:KeyboardEvent):void {
            if ( event.keyCode == 13 ) {

We also want to allow users to change their nickname, so we’ll do that first.

                // Let the user change their nickname using /nick NewName
                if ( input.text.match('^/nick') ) {
                    nickname = input.text.substr(6); // Everything after "/nick "
                }

If they’re not changing their nickname, they’re trying to talk, so we’ll create a new ChatEvent object and send it out over the socket. We also add their text to the chatbox, because we won’t get our ChatEvent back.

                else {
                    socket.writeEvent( new ChatEvent( nickname, input.text ) );
                    chatbox.appendText( "\n" + nickname + "> " + input.text );
                }

Last, we clear out the input so the user can chat more.

                input.text = "";
            }
        }

Again, we add the listener in our init() function.

        public function init():void {
            // Register our event class
            registerClassAlias( "swfconduit.chat.ChatEvent", ChatEvent );

            // Connect to the server
            socket = new swfconduit.Socket( hostname, port );
            socket.addEventListener( "ChatEvent", handleChat );

            // When enter is pressed, send the event
            input.addEventListener( KeyboardEvent.KEY_UP, sendChat );
        }

And that’s all the script we need.

    ]]></fx:Script>

Now we can compile our swf file and try it out.

$ mxmlc -l+=../../flex chat.mxml
$ open chat.swf

That covers the basics of how to use the SwfConduit server.

How To Do Anything

Server Side

At its core, SwfConduit is a very thin wrapper around Twisted and PyAMF. The swfconduit Server class is a Twisted Factory, the swfconduit Session class is a Twisted Protocol that handles encoding and decoding the AMF. You can extend the Server and Session classes to track more data, such as database connections and player information.

But the grunt work is really done in your Event classes. By registering your event class with pyamf.register_class(), any event that comes in will have its fire() method called. Your events can log a user in, retrieve and update objects from the database, or just pass events along to other people (like theChatEvent did).

Client Side

SwfConduit is merely the transport and dispatch layer. Your Event classes just send and recieve data, all the real work is done by your event listeners. By passing the SwfConduit socket around, you can build ORMs, game clients, editors, and anything else you want.

The Future

SwfConduit is the beginning of a platform for multiplayer game development in Flash. As more pieces are developed, we’ll release them as SwfConduit plugins. Simple things like an ORM based on SQLAlchemy, users and profiles, or a chat system that can be quickly integrated with your own projects.

SwfConduit will always be free and open source. Game development should be more accessible to beginning programmers, and SwfConduit will be one of our tools to make that happen.