Array Ordering in ActionScript 3

Update: I filed a bug about this issue.

coils This post starts out with a finite state machine, but it ends up talking about how iteration is implementing in ActionScript.

There I was – implementing a finite state machine. Each state had transitions, and the transitions are named and, additionally, processed in order. This is useful because you can order your transitions for consideration from most to least specific. Usually the last transition is unconditional.

Unfortunately, I began encountering issues with users who had the release player installed. Enemies would move erratically and transition between states inappropriately. Adding and removing log statements would sometimes make the problem go away, other times not. I suspected the VM or compiler.

As you may be aware the for and for each keywords in Flash are not guaranteed to iterate over keys or values in a consistent order. And of course, my loop for checking transitions looks like:


      	// Evaluate transitions in order until one goes.
      	for each(var t:ITransition in Transitions)
      	{
      		if(t.Evaluate(fsm) && fsm.SetCurrentState(t.GetTargetState()))
      			return;
      	}

The icing on the cake is the hashtable implementation in Tamarin, which is used internally for storing object properties as well as (partially) for Arrays. Whenever you store a value by key, the key is converted to an atom, and the atom is used as the key for the hash table look up. Atoms are (basically) pointers, so the location in the hash table is based on the memory allocation pattern of your SWF (as well as the allocations of the player binary you are using). (See HashTable::find for the implementation behind this.)

In other words, the order of iteration will be random, but consistent for a given combination of player binary and SWF (and execution pattern). In my case, the iteration order was what you’d expect (order of addition to the Array) for debug player, and backwards for release player. A case of good luck turning bad, since it led me to believe that everything was working correctly in the debug player.

How does this relate to the log statements I mentioned earlier? Because iteration order is tied to memory allocations, it looked like a VM or compiler bug, since adding or removing code would change memory allocation patterns.

From this experience, I have two pieces of knowledge to share.

First, if you need to have both non-integer keys and values, but want to iterate in a specific order, you can’t use any of the built-in ActionScript classes. Instead, use a class like senocular.com’s AssociativeArray, which subclasses flash.utils.Proxy to implement custom iteration logic.

Second, some parts of ActionScript do iterate in a consistent order:

  • “Dense” Arrays will iterate in order from first to last element. You can see the implementation for this in ArrayObject. “Dense” mode is heuristic but activated by having numeric indexes, especially starting at zero.
  • XML nodes. Children have a definite order based on the document and are iterated in this order.

The docs don’t describe this, to the best of my knowledge, which is why I post here. The best I can find is this from the online docs:

For example, you can use a for..in loop to iterate through the properties of a generic object (object properties are not kept in any particular order, so properties may appear in a seemingly random order)

Is there any more information out there? I’d like to think I’m overlooking something!

Thanks to Steven Johnson of Adobe – his feedback was essential in the process of sorting this out. Creative Commons picture by davebluedevil.

ActionScript 3 Reflection: list fields on an object with ClassFieldCache

Say you’re working on some sort of reflection/deserialization system in Flex, Flash, or another ActionScript 3 technology. It would be nice to know the type of all the fields on an object or class, including accessors, so that you can automagically infer the right way to parse your serialization data. You can use describeType to do this but it has some overhead – dumping a full XML description of a complex type can’t be something you’d want to do frequently, and then you have to parse it again.

I present the ClassFieldCache. It contains one static method, getFieldsOfClass, which will return a dictionary of types indexed by field name. It returns every kind of property you can set – both fields and accessors. (And it ignores constants and readonly accessors.)

Usage is like this:

	var dict:Dictionary = ClassFieldCache.getFieldsOfClass(myObject);
	trace("Field boo is of type " + dict["boo"]);

Which outputs “Field boo is of type flash.geom::Point” – or whatever that field happens to be.

And the class itself is as follows:

	import flash.utils.*;

	/**
	 * Utility class to get list of fields on an object or class.
	 */
	public class ClassFieldCache
	{
		/// Indexed by Class, this contains dictionaries mapping name to type (string).
		private static var smFieldInfoCache:Dictionary = new Dictionary(true);

		/**
		 * Return a dictionary describing every settable field on this object or class.
		 *
		 * Fields are indexed by name, and the type is contained as a string.
		 */
		public static function getFieldsOfClass(c:*):Dictionary
		{
			if(!(c is Class))
			{
				// Convert to its class.
				c = getDefinitionByName(getQualifiedClassName(c));
			}

			// Is it cached? If so, return that.
			if(smFieldInfoCache.hasOwnProperty(c))
				return smFieldInfoCache[c];

			// Otherwise describe the type...
			var typeXml:XML = describeType(c);

			// Set up the dictionary
			var typeDict:Dictionary = new Dictionary();

			// Walk all the variables...
			for each (var variable:XML in typeXml.factory.variable)
				typeDict[variable.@name.toString()] = variable.@type.toString();

			// And all the accessors...
			for each (var accessor:XML in typeXml.factory.accessor)
			{
				// Ignore ones we can't write to.
				if(accessor.@access == "readonly")
					continue;

				typeDict[accessor.@name.toString()] = accessor.@type.toString();
			}

			// Don't forget to stuff it in the cache. :)
			smFieldInfoCache[c] = typeDict;
			return typeDict;
		}
	}

Hope this is useful for you. You’re free to use it however you like. Attribution would be appreciated.

Forest Pack – Finally out the door

Trees With Shadows

It took a few years, but I finally released the fixed function GL forest renderer I was working on for Torque. You can find out all about it at my .plan on GarageGames. Various businessy things got in the way of releasing it (and perhaps a bit of apathy on my own part :) … but it’s nice to have it out! Done with? We’ll see. There’s always a lot of question/answer and trouble-shooting to go with these things.

On a personal level, I like to think this heralds a new chapter of my life, where things Just Get Done a lot more often. We’ll see how that pans out.

SpaceNavigator Support for Sauerbraten

SpaceNavigator I recently got a 3dconnexion SpaceNavigator after trying a friend’s. Really neat device. 6 degrees of freedom in a slick little package, and only $50. And it’s actually fun to use – not frustrating and cheap-feeling like many other “weird” input devices I’ve tried. Its big brother is the same knob with some extra buttons – for only $400. Yikes. Yet I’m beginning to see why you might want to upgrade…

Anyway, I did a lame integration with Sauerbraten, an open-source 3d engine, under Win32 against the 3dconnexion SDK. The integration is pretty simple – even crude. This is another code post, skip if you’re not a techy! :)


First, grab this file (spaceNav.cpp) and add it to the Sauerbraten project. Then add a COM reference to the TDxInput that ships with the 3d connexion SDK (which you can download here). Finally, go to engine/main.cpp and add:

extern void spaceNav_start();
extern void spaceNav_poll();

void updateCameraFromSpaceNav(float tX, float tY, float tZ, float rX, float rY, float rZ, float rAngle)
{
   // Construct a matrix from our cur yaw/pitch/roll
   matrix m(vec(1,0,0), vec(0,1,0), vec(0,0,1));

   m.rotate(-camera1->yaw * RAD,   vec(0,0,1));

   m.X.mul(tX / 10.f);
   m.Y.mul(-tY / 10.f);
   m.Z.mul(tZ / 10.f);

   camera1->vel.add(m.X);
   camera1->vel.add(m.Y);
   camera1->vel.add(m.Z);
}

Right before the main function (around line 434).

Right after the line that reads:

    SDL_ShowCursor(0);

Add:

    spaceNav_start();

And around line 630, inside the for(;;), add:

               spaceNav_poll();

You can bind to the SpaceNavigator buttons by setting up keymaps like…

keymap -131 SPACENAV_LEFT

…and similar to a cfg file that gets executed when Sauerbraten starts up – I used keymap.cfg. Then you can bind to SPACENAV_LEFT and execute whatever commands you like. The binding uses button ids -100 through -132. 31 and 32 are the two buttons on the SpaceNavigator. On other 3dconnexion products there are more buttons and thus you can make use of the other binds.

And that’s it. spaceNav_start() loads up the SpaceNavigator COM object, Connect()s to the driver, and sets some global. spaceNav_poll() queries state and calls updateCameraFromSpaceNav() with the current state. It also dispatches to keypress() when a button is hit so the console can deal with binds.

Known issues:

  • Not networked properly. I’m just stuffing into the camera1 entity’s velocity field. It’s good enough to edit with, which is what I want.
  • Not based on frame time, so will give different behavior based on framerate.
  • No rotation. Since you have to aim with the mouse anyway this didn’t seem like a big need, but if someone made clicking independent of heading it could be a really useful feature… It does take the yaw into account when translating so you can fly around pretty well.
  • Not configurable. You can tweak the code in updateCameraFromSpaceNav to change how/how fast you move but it would be better to have that all be console-controllable.

Conclusion

I hope this is useful to someone! Feel free to do what you like with this code. Sauerbraten is under zlib, and 3d connexion SDK probably has some restrictions, too.

Using the Domain class in Tamarin for dynamic code loading

Using the Domain ActionScript class (as opposed to the C++ class of the same name) in Tamarin is actually really easy. This class isn’t available under Flash 9 for security reasons. It lets you load new ActionScript code at runtime, as our example will demonstrate. Dynamic code loading is pretty cool functionality – it could be used to implement a plugin system in an application, as the basis for a web server that executed files on demand, or as part of a programmer’s workbench written in AS.

Continue reading “Using the Domain class in Tamarin for dynamic code loading”

Debugging Tamarin

I’m getting my feet wet in Tamarin, the open-source ActionScript 3 runtime from Adobe – same code that’s in Flash 9. It’s cool tech, and I’m ecstatic that they had the cajones to bring it out into the world – where it’ll definitely make the world a better place.

This gets kind of technical so I’ve hidden most of it behind the jump.

Continue reading “Debugging Tamarin”

SuperMate

So I own this Griffin PowerMate, which is essentially a USB knob with a light under it. It’s pretty cool – solid construction, simple operation. But the software it comes with is a little limited, and it’s not been updated in a while. All I want, says I to myself, is to make iTunes pause when I push the knob.

Their software can’t do that, so I set out to fix the problem. It turns out that the PowerMate is a standard USB HID device, which are ridiculously easy to interface with (<15kb of C++ code for a nice self-contained class with comments). After maybe seven or eight hours of hacking, I now have a small tray app that I call “SuperMate.”

SuperMate v1.00

  • PowerMate light functions as a disk activity light.
  • Left/right turn advances to previous/next track in iTunes.
  • Pushing the PowerMate pauses/unpauses iTunes.
  • Takes up minimal system resources – around 5mb of RAM and almost zero CPU.
  • Minimizes to system tray.
  • Deals cleanly with plugging/unplugging the PowerMate, as well as failing gracefully if iTunes isn’t loaded.
  • This program is public domain, and source is included.
  • Supports XP/2000, and probably Vista, too.

You can download Supermate 1.00 here.

Any comments, bugfixes, enhancements are welcome. You can also donate a few bucks if you find it useful: