Browse Entries — Entries From category

  1. 2001
  2. 2002
  3. 2003
  4. 2004
  5. 2005
  6. 2006
  7. 2007
  8. 2008
  9. 2009
  10. 2010

The Problem

I've spent the last week optimizing a lot of our JavaScript code at work. We rely heavily on jQuery there (a great framework!) and it's partner in crime jQuery UI for some of our UI widgets. Part of my optimizing including tracking down some of the memory leaks we've been seeing on the site with extended use, especially in IE (surprise!).

After tracking down quite a few closures and fixing those up, I was still left with 10 meg leaks here and there. After a bit I tracked it down to every time we used a dialog from jQuery UI's library.

Our helper function was pretty simple -- allow the code to specify the title and the text, and optionally provide a callback function that'll be called with what they clicked to close the dialog. The dialog would pop up on the screen and gray out the background forcing the user to interact with the alert box. I was concerned because we rely on modals quite a bit, especially in the edit pages. A 10meg leak per dialog shown would quickly add up to 100,200,300+megs leaked over the course of a normal session.

I removed a lot of the surrounding code, making sure I wasn't leaking memory in my own code. I was left with a pretty simple use of the dialog use:

function AlertDialog(dialogTitle, dialogText, callbackFunction) {
    // dynamically generate and add a div so we can create the pop-up
    $('body').append("
" + dialogText + "
"); // define/configure the modal pop-up $("#alertDialog").dialog({ draggable: false, resizable: false, modal: true, buttons: { 'OK': function() { if (callbackFunction != undefined) { callbackFunction('OK'); } //remove the default beforeclose event and trigger the close $("#alertDialog").unbind('dialogbeforeclose').dialog('close'); } } }).bind('dialogbeforeclose', function(event, ui) { // Close (X) button was clicked; NOT the OK button if (callbackFunction != undefined) { callbackFunction('cancel'); } }).bind('dialogclose', function() { $('#alertDialog').dialog("destroy").remove(); }).dialog("open"); }

Despite all my efforts I couldn't get the leak to stop. Simply opening the alert would cause it, so I figured it wasn't my callback function. Frustrated I changed the modal option to false. Bam, no more memory leak in IE! How could this be? So I tried playing around with the appearance of the modal in their CSS file. I tried setting the background property to a GIF instead of a PNG, since IE is terrible with PNGs, with no luck. I tried removing the opacity filter in the CSS with no luck too. Turns out it's just how jQuery UI generates the gray background behind the dialog that causes the leak.

The Solution

The solution was to never use the modal: true setting in our use of the dialog boxes. I instead wrote two helper methods that would be used to gray out the UI and block interaction with anything but the alert.

HTML:
CSS:
#BlockUI 
{
    position: fixed;
    display: none;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 600;
    background: url(/Resources/grayout.gif);
}
JavaScript:
var blockCount = 0;

function BlockUI() {
    blockCount++;
    $('#BlockUI').show();
}

function UnBlockUI() {
    blockCount--;
    if (blockCount <= 0) {
        blockCount = 0;
        $('#BlockUI').hide();
    }
}

With this I had a common method for triggering a grayed-out background effect. The GIF I used was a checker-board pattern one, alternating between a light gray color and a transparent pixel. One could use a solid PNG with it's alpha set to something around 50%, but I wanted IE6 compatibility. The difference between the two is minimal.

With that in place I just needed to update the dialog to trigger the block and unblock calls. JQuery UI's dialog lets you specify a function for the "open" option which will be called when the dialog opens.

function AlertDialog(dialogTitle, dialogText, callbackFunction) {
    // dynamically generate and add a div so we can create the pop-up
    $('body').append("
" + dialogText + "
"); // define/configure the modal pop-up $("#alertDialog").dialog({ draggable: false, resizable: false, modal: false, open: function() { BlockUI(); }, buttons: { 'OK': function() { if (callbackFunction != undefined) { callbackFunction('OK'); } //remove the default beforeclose event and trigger the close $("#alertDialog").unbind('dialogbeforeclose').dialog('close'); } } }).bind('dialogbeforeclose', function(event, ui) { // Close (X) button was clicked; NOT the OK button if (callbackFunction != undefined) { callbackFunction('cancel'); } }).bind('dialogclose', function() { $('#alertDialog').dialog("destroy").remove(); UnBlockUI(); }).dialog("open"); }

And with that the memory leak was fixed. It's not ideal to have to rely on custom-code to get around the leak, but until jQuery UI is fixed to avoid this it's the best work around to make sure IE doesn't bloat like crazy when you use modals.

cover

The first Assassin's Creed was a major disappointment, I think few will argue that fact. The original largely boiled down to a game engine with lots of promise but nothing to draw you into the game beyond that. The repetition was mind-numbing, the plot was a joke. I never actually finished the game, I got about 2/3 of the way through it.

After the bad taste the first left in my mouth you can imagine by reservation when Assassin's Creed 2 came out. But this time I was not disappointed; instead I was finding myself unable to put the controller down. Assassin's Creed 2 is the perfect sequel: it took everything that was weak about the first and improved it.

What a difference a plot can make. The first one had a plot but it wasn't a personal plot. You were just a tool of a larger war between two factions being told to go kill boss X and Y. In between bosses they didn't even try to hide that you were doing the same thing over and over again. You always had ~9 smaller missions before the boss, which were always of the same type. In Assassin's Creed 2 it is actually personal and diverse. You start out not knowing a thing about Assassins or Templars (heck you spend most of the game not caring about that), you're just a guy whose family is attacked and you go after the killers in revenge (sweet stabby revenge). You're going around killing people because you want to, not because you're told to. And everything in between killing bosses doesn't feel like repetitive kill X, pickpocket Y, loot treasure Z. Sure it boils down to that in the end, but it doesn't feel like that at all. The game flows so well.

The asides from the plot are vastly improved too. If you find yourself wanting a distraction from the revenge-plot there is plenty to do this time around beyond just climbing around. You can help out in the cities with various assassination and beat-the-cheating-husband up style missions that reward you with money. Partway through you get your own villa which you can chose to sink money into. Doing so gets you the warm fuzzy feeling (also helps the place stop looking so run-down) but also makes upgrades cheaper as you spend money to upgrade your blacksmith. Plus as you upgrade your villa more and more it will start generating money for you every 20 minutes.

This money comes in handy as there is a full weapon and armor system in the game, finally. There are quite a few classes of weapons you can choose to carry around, from daggers to swords to maces, all of which cause you to fight a little differently. Along with weapons and armor there are plenty of little upgrades too, like additional medicine pouches and knife-slots that really help out later in the game. There are six Prince-of-Persia style jump-puzzles (Assassin's Tombs) you can do if you want the best armor in the game.

For the obsessive-collectors they kept plenty to do in the game that requires running around everywhere trying to find things. Most importantly you have hidden glyphs you can seek out (with small hints as to where in the cities they are) that help unlock something within the game. There are hundreds of treasure chests around the city, and a side-quest where you need to find 100 feathers in memory of someone in your family. You can try to buy all the paintings available as part of the side-quest to restore your villa to its former glory.

They added in a notoriety system for the second game. It tracks your notoriety per-city and really helps you gage how cautious you need to be around guards. And now there is finally a way to lower your notoriety if you care: running around ripping down wanted posters, bribing town criers, or killing city officials. And when you are running from guards they're a lot more intelligent with their chase, they'll actually look for you in hiding spots sometimes. This makes it feel much more realistic instead of just having them give up once you jump in some hay.

One can tell that they really learned from the shortcomings of the first game and made sure they didn't exist in the second. A complaint I had was the rebounding-off-walls controls. There were a few times it would have me jump off in a different direction then I was going for, but this really only showed itself during the more difficult jump-puzzles (the most inopportune time for it to happen). The combat wasn't vastly changed in the sequel, it was still a bit of well-timed button-mashing, but it didn't really detract from the fun of the game.

Without a doubt Assassin's Creed 2 was a wonderful game and well-worth the time I sank into it. It's what the first game should have been. I'm glad to see Ubisoft pull a 180 with this game and really produce something with such high quality. And fortunately it ends with a pretty cool twist leaving it open for a third (which they've already said will happen).