Thursday, November 11, 2010

Mochi ZIP integration issue resolved

It took a couple of weeks working back and forth with the tech folks at Mochi, but we finally uncovered an interesting ooopsie.

There are three ways (that I'm aware of) to build a game for Mochi distribution:

1 - Use a single SWF file that contains all your components, images, sounds, etc.
....This one works pretty well for the most part. Very few moving parts so not a lot to worry about.

2 - Use a single SWF that loads external resources from a site you host.
....Works well as long as you have a reliable server with the proper crossdomain.xml file setup and all that, but is subject to dropouts of your assets and potentially slower loading times. If you have a decent CDN it's better.

3 - Use a .ZIP file that contains your SWF plus all the externally loaded assets.
....Neat idea and has its merits, but it's not idea. Firstly, you can't use sub-directories (as of this writing anyway). Not a huge deal, but I like to try and keep my assets categorized so it bugs me a little bit. Secondly, you have to watch how you load these assets because Mochi's launchpad pages (the pages where they launch the games on) fail on external loads of the .ZIP'd content in some instances. Third, even if you use try{}...catch{} blocks on items that aren't available, using the catch to resort to different methodology for getting your assets/data, the game will fail to launch.

The point of this post is to discuss what I did to get the .ZIP file local loading issue resolved in the hopes that it'll help someone else.

The key component came from the Mochi techs that said I needed to have any loaders based off of the loaderURL. This snags the URL of the initiating SWF file, thus providing a fully qualified URL instead of relative. Here is a bit of sample code for what I mean:

var loader:Loader;
var resourceURL:String;
var request:URLRequest;

loader = new Loader();
resourceURL = loader.contentLoaderInfo.loaderURL;
resourceURL = resourceURL.substring(0,resourceURL.lastIndexOf("/") + 1);

resourceURL = resourceURL + "tempflashfile.swf";
request = new URLRequest(resourceURL);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler, false, 0, true);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loaderErrorHandler, false, 0, true);
loader.load(request);

The key point is to make sure that instead of having just "tempflashfile.swf" as the resource URL we end up with like "http://www.yourdomain.com/tempflashfile.swf". In the case of a Mochi distribution point, this "yourdomain" would be whatever domain the game gets loaded from, of course.

The next trick comes from making sure you either a) do this on ALL externally loaded items or b) you don't put in any try{}...catch{} blocks for stuff you know isn't going to be there. In other words, if you have code that checks for an external asset and defaults to an internal asset if the external one is not found...don't do that. Any non-found external asset seems to knock your game to a halt, even if you use the above methodology (at least it did for me!). Instead, for your Mochi build, just rely on the internal asset and avoid the external check completely.

If you're loading .XML files or somesuch, you will likely just be using the URLLoader and not the Loader. That's fine, but the URLLoader doesn't have a corresponding contentLoaderInfo.loaderURL data point, so you may scratch your head a bit. Just use a dummy Loader to get that full domain and stack it globally so you have it when building your resource URL.

private var gLoader:Loader;
private var gResourceURL:String;

gLoader = new Loader();
gResourceURL = gLoader.contentLoaderInfo.loaderURL;
gResourceURL = gResourceURL.substring(0,gResourceURL.lastIndexOf("/") + 1);

Now you can just preface any of your URL's with the gResourceLoader (gResourceLoader + "tempflashfile.swf") and you should be fine. Creating a class out of this is a snap and will save you a bit of headache and cut down on globals too.