Sections

  • Search Chrome Cow

Design a Day



I’m doing some multiuser/player development using the Palabre Socket Server, a nice XML socket server for Flash written in Python.

The documentation is a bit spotty, as it’s primarily the work of a lone developer who choses to code more often than document, so I combed through the FAQ and the Forums and the News Posts and assembled this document, which I will try to update as I discover new things.

This doc is for reference only, and I’m not an expert on this software, so your best bet for questions is the Palabre Forums.




To download Palabre Server or client go to the Download Page. You may also check the Sourceforge Project Page for mirrors

New: Flow Diagrams

I’ve started making a flow diagram of the Palabre systems to help get my head around what it’s doing. Here’s the work so far:

Starting the Server  |  Parsing Incoming Data Client Triggered Actions (1) Client Triggered Actions (2)

(These images are formated to be printed out on legal paper or viewed on a 1600×1200 display)

You can also download the original Visio document.

Which XML node can i send and receive ?

This is a list of simple nodes to connect to the server and send simple messages.
 

Connection with nickname and optionnaly password for root
SEND>  <connect nickname="KoolBoy" [ password="YYY" ] />

Answer from the server (nickname in use or non acceptable)
RECEIVE>  <connect isok="1" />

If you want to check the connection
SEND>  <ping />

The server should answer :
RECEIVE>  <pong />

Asking for room list
SEND>  <getrooms />

Rooms list (also supports sub-rooms, operator mode for some clients in rooms, params in rooms (title, locked, …)
RECEIVE> <rooms nb="2" ><room name="XXX" clients="5" /><room clients="20" name="YYY" /></rooms>


Asking to join Room XXX (if room does not exists it is created then)

SEND>  <join room="XXX" />

Room Joined
RECEIVE> <joined room="XXX" />

And the list of clients for this room
SEND>  <clients room="XXX" nb="5" > <client name="Toto" /> <client name="Titi" /> […] </clients>

Joined/Left Information

RECEIVE> <client name="Nickname" joined="Lobby" /> 
RECEIVE> <client name="Nickname" left="Lobby" />

Now when you change/add a room param the full list is not sent back, only the modified param.

And now, the room creator may set the room params as "open"  to everybody with :

<setparam name="openParams" value="True" />
<setparam name="openParams" value="False" />

If set to "true" (or "1"), anybody can use <setparam /> and <removeparam /> in this room

If set to false, only the room creator can do it.

<setparam name="color" value="blue" />
and remove :
<removeparam name="color" />

 UPDATE: it seems the syntax has changed a bit:

<setparam room="roomname" name="color" value="blue" />

<removeparam room="roomname" name="color" />

There are a few others like:

<addchild /> to add a sub-room , or

<setparam /> / <removeparam /> to set parameters for the room,
<leave /> / <quit /> to leave a room or disconnect , …

<broadcast /> / <shutdown /> / <getinfo />

 

# Leaving a room
node == "leave":

# Setting a param for a room
node == "setparam":

# removing a param from a room
node == "removeparam":

# Adding or changing Client Param
node == "clientparam":

# adding a child room
node == "addchild":

To create a sub room use the following node :

<addchild parentroom="nameoftheexistingroom" childroom="nameofthenewroom" />

and you will receive something like :

<room name="nameofthenewroom" clients="2" parent="nameoftheexistingroom" >
</room>

 
# Shutdown server
node == "shutdown":
# Must Be root

# Kick User from server nickName="XYZ"
node == "rmuser":
# Must be root
attribute : (’nickName’):

# Get Informations about a room="XYZ"
node == "getinfo":
# Must be root
attribute : (’room’):

# This can possibly be any node with attribute that tells me to send
# this exact same node to the clients or Room
# I prefer to encapsulate everything in a MSG node,
# but obviously people like to have their own nodes
attrs.has_key(’toroom’) or attrs.has_key(’toclient’) or attrs.has_key(’r') or attrs.has_key(’c'):
if node != "error":

And you can send messages to client directly
(don’t need to join a specific room for two people)

<msg toclient="Nicknameoftheother">Blabla</msg>
or
<m c="Nicknameoftheother">blabla</m>

 

Custom Nodes works exactly as MSG nodes

You can pass toroom/toclient/r/c params to tell Palabre who to send it too but the name of the node is up to you !
So it’s easier to send Messages/Position/Whatever information :

<msg toroom="X" >Hi everyone</msg>
<position toroom="X"><x>12</x><y>15</y></position>

Sub XML nodes allow you to encapsulate XML subnodes in <msg> or whatever nodes

<msg toroom="X" ><title>Hi everyone</title><content>Bla bla bla bla bla bla</content></msg>
<position toroom="X"><x>12</x><y>15</y></position> 

 

Sending a message "msg" or "m"

(Param "c" or "toclient" -> the message is only delivered to one client)
(Param "r" or "toroom" is delivered to entire room)
(Param "b" or "back" tells the server to send the same message back to the send)
(Param "broadcast" sends to everyone)

SEND> <m r="XXX" >Hello everyone i’m very happy to join you all!</m>
RECEIVE>  <m f="Toto" r="XXX">Hello KoolBoy glad to meet you!</m>
RECEIVE>  <m f="Titi" r="XXX">Hi how are you KoolBoy ?</m>
SEND>  <m r="XXX" >Have to go ! Bye !</m>
SEND>  <leave r="XXX" />

 

Setting Clients Params

<clientparam name="Avatar" value="girlwithlonghair2" />

 And everyone receives :

<clientparam r="Lobby" name="Avatar"
value="girlwithlonghair2" f="Nickname" /> 

 Or when you join a new room you receives everyone’s params:

<clients room="Lobby" nb="1">

  <client name="Nickname">

     <param name="Avatar" value="girlwithlonghair2" />

     <param name="HairColor" value="blue" />

     <param name="posX" value="12" />

     <param name="posY" value="64" />

  </client>

  <client name="OtherNickname">

     <param name="Avatar" value="boywithshorthair1" />

     <param name="HairColor" value="green" />

     <param name="posX" value="15" />

     <param name="posY" value="65" />

  </client>

</clients>

Database Connectivity

Database identification supports all the databases modules of AdoDB . If you need to know who is connecting to your server, and have for example, a Mysql database with logins and passwords,
you can enable database Support.

odbc    access    mssql    mysql    mxodbc    mxoracle   oci8   odbc    odbc_mssql    postgres    vfp    sqlite 

If you enable it you need to have AdoDb Python and the required DB module installed .
(view  : http://phplens.com/lens/adodb/adodb-py-docs.htm )

With compiled Win32 version, DB identification works with Mysql,PostgreSQL, and SqlLite because of modules Licences. If you want other Databases with Win32 you’ll have to use the Source version and install the correct module.

Currently under Windows, Postgresql support is buggy.
Under other systems (non compiled palabre versions) you need to have the python Adodb module installed
and the correct database module installed (ex: for mysql : MysqlDB, for Posgresql : PsycoPg, …)

For full list and informations, please see :
http://phplens.com/lens/adodb/adodb-py-docs.htm#databases

To enable this support, open palabre.conf and in the section [database] change :

checkpassword = false

to

checkpassword = true

Then fill the lines bellow according to your database informations

dbType = mysql
dbHost = localhost
dbUser = root
dbPassword =
dbDatabase = test

And specify the request to be used to connect

dBRequest = SELECT * FROM t_logins WHERE login_nickname LIKE [LOGIN] AND login_password LIKE MD5([PASSWORD])

[LOGIN] and [PASSWORD] will automaticaly be replaced with the "nickname" and "password" attributes of the connect node

So you can now use :

<connect nickname="toto" password="str0ngp4ssW0rd" />

to connect.

How to Connect from Flash

Simple Actionscript code to connect to the server.

 

XML.prototype.ignoreWhite = true;

/* Create a new XMLSocket Object */
x = new XMLSocket();
x.ignoreWhite = true;

/* Define an handler function to check connection */
x.onConnect = function (status) {

    if(status) {
        trace(’Connection OK’);

        /* If connection is OK then we can identify and start sending XML */
        _
root.x.send(’<connect nickname="test_’+random(30)+’" ></connect>’);
    } else {
        trace(’No Connection’);
    }
}

/* Define an handler function to trace incoming XML */
    x.onXML = function (xmlNode) {
    trace(’Just Received : ‘+xmlNode);
}

/* Then start the connection, adjust IP and port to reflect the server information */

x.connect(’127.0.0.1′,2468);


 

 

Modules

How to write a module?

1. in the "modules" subdirectory create a file "yourModule.py"
2. In that file create a class yourModule

class yourModule:
    def __init__(self,palabreServer):
        self.server = palabreServer
        self.name = "yourModule"

3. Then specify when your module should be called.
Two options are available : self.server.registerAction() and seld.server.registerNode()

If you want your plugin to be called every time Palabre executes a specific action (example when server startup, when a client connects, when a client Leaves, when a client sends a password for connection, …)

Example : Do something when server starts :

Then add in your __init__ method :

self.server.registerAction(’onStartup’,self.name)

And Create a method called : ‘onStartup’

def onStartup(self,params):
    # Do something
    self.server.logger.info(’My Module is loaded’)
    return

If you want to add an action when clients sends a specific node, add in __init__ :

self.server.regsiterNode(’mynode’,self.name):

And create a method called ‘doNode’

def doNode(self,nodeName,node, client):
    if nodeName == ‘mynode’:
        client.clientSendMessage(’<anynode>You sent MYNODE !</anynode>’)

4. In palabre.conf edit the [modules] section and add your module name to the list :

[modules]
list = helloworld,yourModule

Restart Palabre … and You’re done !

Accessing Server Variable from a Module

You can access all the methods and properties of the server via :

self.server.XXX

Example: to find out if a particular nickname exists, you would look at the sever variable that holds the list of all nicknames:

self.server.allNickNames

so your test might look like :

if self.server.allNickNames.has_key(node["attrs"]["nickName"]):
    client.clientSendMessage("blabla")

You can also access to a client object via :

self.server.allNickNames[ClientNickName]

And to a room object via :

self.server.allRooms[RoomName]

And then access to all the methods and properties of each object, for example :

self.server.allNickNames[clientNickName].clientSendErrorMessage("Canno t ask for nickName and Room in same request")

or

self.server.allRooms[RoomName].removeRoom()

Included Modules


helloworld.py

[view source]

It adds two nodes "hello" and "quote"
If you send "<hello/>" it will reply "<world>Hello World</world>" …
If you send "<quote>" it will reply with a random quote "<quote>Balblablabla</quote>"
Very simple plugin but shows you how to add actions and nodes easyly with Palabre

dbQueries.py

[view source]

WARNING ! Don’t use this one on production environments !
IF database access is configured in palabre.conf, it allows you to send ANY sql query in a <db/> node !
example : <db>SELECT * FROM my_table</db>
(WARNING, request could be DROP DATABASE ….)
And it will reply :

<dbres >
    <row>
        <field_1>Value</field_1>
            <field_2>Value</field_2>
    </row>
    <row>
        <field_1>Value</field_1>
            <field_2>Value</field_2>
    </row>
</dbres>



You can send "SHOW TABLES" or "SHOW COLUMNS my_table" and it will work too …

simpleLogin.py

[view source]

Just a plugin to explain how to write your own login module
This one is pretty easy .. just don’t send "hacker" as password and you’re in.

Cross Domain Support


Crossdomain support, will really help clarify the way you authorize SWF files to connect the server.

In palabre.conf, in the [crossdomain] section, on the line :

alloweddomains = domainthatdoesnotexist.bar otherinexistantdomain.foo

just specify the domains were people will access your SWFs from

Example:

If you have an swf on http://www.domain.com/test.swf
that can also be accessed through http://www.domain.net/test.swf ,
or http://192.168.0.1/test.swf

specify :

alloweddomains = domain.com domain.net 192.168.0.1

And in your test.fla(swf), execute before the connection to www.domain.com on port 2468 :

System.security.loadPolicyFile("xmlsocket://www.domain.com:2468");
XMLs ocket.connect("www.domain.com",2468);

And even if the SWF is hosted on www.domain.net/test.swf the connection will work.

Using Palabre from Webpage SWF

  • You have Palabre up and running on your computer (IP : x.x.x.x) on port 2468
  • Port is open and anyone could do a telnet x.x.x.x 2468
  • Your SWF is hosted on yourdomain.com/xyz.swf
  • Your SWF is trying to connect to x.x.x.x on port 2468
  • Your palabre.conf has the line
  •  alloweddomains = yourdomain.com www.yourdomain.com
  • Your SWF calls before connecting :
  • System.security.loadPolicyFile("xmlsocket://x.x.x.x:2468");


The Game Design section was starting to look a little overrun by Flash and ActionScript tutorials, so I’ve added a whole new page for Flash and ActionScript. Check there for the latest experiments.

Speaking of which: Drawing Regular nGons in Flash.

That’s right. NGons, baby! Try to curb your enthusiasm.

I’m working on a project that requires the drawing (or at least calculating) of regular nGons (pentagons, hexagons, octagons, etc.), so I threw together a demo movie. As usual, source code is included.


I’ve collected together all of my various experiments with Flash on this page. Most of what you will find here are snippets of code and small movies that test certain features of Flash, or solve some small, specific problems. There are also a few game-like projects.

Each of the movies posted here also show the source code (or at least the important bits). The goal is to create a resource for other people who are learning Flash, especially ActionScript . All of the exercises below are created in Flash 8. Some of them will work in earlier versions, some will not.

<

 Drawing Regular NGons

Documentation: Palabre 0.6

I am in need of a Flash compatible Socket Server, and found a great open source project, Palabre, with poor docs. This is info gather from docs, forums, etc. [ Link ]


 Drawing Regular NGons

Drawing Regular NGons

I have a project in progress that require me to draw regular NGons (Pentagons, Hexagons, Octagons, etc). I’ve posted the Flash movie and the source code for drawing them, using a simple rotation equation. [ Link ]


Learning Flash

Learning Flash

Since returning from GDC, I have been fired up to learn Flash as a tool for doing some simple gameplay prototyping. It has been rewarding, and at times fairly frustrating, as Flash has some interesting quirks of implementation. It has…personality.

  • Loading Text as Unicode
  • Create and Format a Dynamic Text Field
  • Write-On Text in a Fixed Amount of Time
  • Using Checkboxes
  • The Game Loop

  [ Link ]


Radial Solo Ball

Radial Solo Ball

Flash game number one. Basic goal, build a game with a main execution loop.

First experimentation with collision detection in Flash. [ Link ]


eRadiRace

eRadiRace

A simple wall avoidance game.

First iteration using built-in collision handling in Flash.
[ Failed ]

Second iteration using Flash 8’s Draw API. [ Somewhat Final ]


 


Regular nGons. We’ve all wanted to draw them at some time, right? Look no further, seeker of knowledge, I’ve got just what you’re looking for.

The method I’m using is pretty simple.

  • Assume we are drawing our nGon with its center at 0,0
  • Start with a point at
    • x = 0
    • y = nGon radius
  • for an nGon with n sides
    • rotate the point 360/n degrees
    • do this n+1 times
      • to close the polygon
    • add 360/n degrees each time we iterate

So assume we want a 12 sided nGon. We rotate the first point 30 degrees, the next point 60 degrees, 90 degrees and so on.

For the rotation, I’m using:

x’ = x cos Φ - y sin Φ
y’ = y cos Φ + x sin Φ

Where:
x and y are the original position
x’ and y’ are the new position
Φ is the degree of rotation

And because Flash uses radians instead of degrees, we have to convert from degrees to radians when doing this calculation:

radians = (degrees * Π/180)

Add in an x and y offset to each point to place the polygon in the center of the screen.

Here the code that run the movie:

// Drawing Regular Ngons
// Sean Hyde-Moyer
// April 2007
//
// Initialize variables

xOffset = 350
yOffset = 200
var xArray:Array = new Array();
var yArray:Array = new Array();

vert.text = 5;
radius.text = 120;

this.createEmptyMovieClip("planet_mc", this.getNextHighestDepth());

this.onEnterFrame = function() {
    incrementalRot = 0.0
    updateFlag = 0
    startVerts = Number(vert.text)           
    ngonRadius = Number(radius.text)
   
        //sanity check user values
        if (ngonRadius >180) {
            ngonRadius = 180
            //radius.text = 180;
        }
        if (ngonRadius <= 20) {
            ngonRadius = 20
            //radius.text = 1;
        }

        if (startVerts >512) {
            startVerts = 512
           
        }
        if (startVerts <= 3) {
            startVerts = 3

        }       
   
    // If values change, redraw poly
    if (oldStartVerts != startVerts ){
        updateFlag = 1
    }
    if (oldngonRadius != ngonRadius) {
        updateFlag = 1
    }
    if (updateFlag == 1) {

        degreeRot = 360/startVerts
   
        // Resize the demo circle
       
        circle_mc._height = ngonRadius*2
        circle_mc._width = ngonRadius*2
        circle_mc._x = xOffset
        circle_mc._y = yOffset
   
        // Draw the Ngon
       
        planet_mc.clear()

        planet_mc.lineStyle(1, 0xFFFF00, 100, true, "normal", "square", "miter", 3);
        planet_mc.moveTo(xOffset,ngonRadius+yOffset);
       
        for (i=0; i<=startVerts; ++i) {
            radianRot = (incrementalRot * Math.PI/180)
            xArray[i] = ((0*Math.cos(radianRot)) - (ngonRadius*Math.sin(radianRot)))
            yArray[i] = ((ngonRadius*Math.cos(radianRot)) - (0*Math.sin(radianRot)))
            xArray[i] += xOffset
            yArray[i] += yOffset
            incrementalRot = incrementalRot + degreeRot
            planet_mc.lineTo(xArray[i],yArray[i]);
        }
    }
oldStartVerts = startVerts
oldngonRadius = ngonRadius
}



Since returning from GDC, I have been fired up to learn Flash as a tool for doing some simple gameplay prototyping. It has been rewarding, and at times fairly frustrating, as Flash has some interesting quirks of implementation. It has…personality.

So this is to be a dumping ground for bits and bobs of code and information that I’ve learned, and might help others over some rough spots. No particular order or theme. Just nuggets of Flashy goodness.



Loading Text as Unicode

Use the standard loadVars method:

myLoadVars = new LoadVars( );
myLoadVars.load("unicodeSample.utx");

myLoadVars.onLoad = function (success) {
    if (success) {
        textWindow_txt.text = myLoadVars.quote1;
    } else {
        trace("Load Failed");
    }
}

 

 

The trick is to save the  text out as UTF-8 or UTF-16, with the *.utx file extension. I use JEdit for this, though there are a number of other free unicode text editors available.

Download the Flash File
Flash unicode.fla


Create and Format a Dynamic Text Field

This code creates a dynamic text field, creates a text formatting object, and applies the text format to the newly created text object.

createTextField("display_txt", getNextHighestDepth(), 20, 10, 460, 200);
//createTextField(textObjectName, Zdepth, X, Y, width, height)

my_txt.autoSize = true;
display_txt.text = "Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.";

var my_fmt:TextFormat = new TextFormat();
    my_fmt.font = "Times"
    my_fmt.size = 33;
    my_fmt.bold = false;
    my_fmt.color = 0×0055CC;
    my_fmt.kerning = true;
   

display_txt.selectable = false;
display_txt.antiAliasType = "normal";

this.onEnterFrame = function() { // Update Format Every Frame (for button update)
    display_txt.setTextFormat(my_fmt);
}

Download the Flash File
Flash dynamicText.fla


Write-On Text in a Fixed Amount of Time

This code writes a text string into a dynamic text field, left-to-right, one or more characters at a time, in a fixed amount of time.

This was created for the Design-A-tron, to write-on a text string of arbitrary length, in the amount of time it took to play the gears running animation and sound. It is completely dependent on the framerate of your movie.

var displayMe = "This was created for the Design-A-tron, to write-on a text string of arbitray length, in the amount of time it took to play the gears running animation and sound. It is completely dependent on the framerate of your movie."
displaySize = displayMe.length;

delay = 70;  // The larger, the slower the write on
displayLoop = 0;
endFlag = 0;
displayRate = displaySize/delay;
   
onEnterFrame = function() {  // Runs this loop every frame
   
    if (endFlag == 0) {  // Only Done for Duration of Write-On
   
        // Text Write-On
        tmp = displayMe.substr(0, displayLoop);
        display_txt.text = tmp;
        displayLoop = displayLoop + displayRate;

        if (displayLoop > displaySize) {
            // This writes on the last few characters
            // if the math doesn’t come out right
            // And sets the end condition for the loop
            endFlag = 1;
            display_txt.text = displayMe;
        }
    }
}

 

Download the Flash File
Flash TextWriteOn.fla


Here is a re-write of the base eRadiRace code, using the new Flash 8 Draw API to make the trails and do the simple collision.

 


 

Added a musical score. Now at v0.254

It still has a few bugs:

  • Not all sounds seem to be playing when called (intermittent).
  • [Fixed] A last minute change to add the Tutorial screen has made the avatars disappear after the first round.
  • Collision could be tightened up just a bit.
  • The frame where the player collides with the line is delayed in drawing by about a half a second, which makes it look like they shouldn’t have lost.

I’ll fix that avatar thing and post up the game loop code.


One of these days, I’ll start making games that fit in my pages.

Instructions

  • Move the mouse left and right to control the paddle.
  • Score by hitting the ball into the Green top and bottom goals.
  • If the ball hits the Red left and right goals, that’s bad.

Because this is a flash game, you need to keep the mouse cursor within the active window to move the paddle. If the paddle stops moving, you’ve wandered out of the window.

Next Page »