The Stochastic Game

Ramblings of General Geekery

Posts tagged with programming

Overtime at Frostbite Cinematics

These past couple days most of the video games development community was set on fire by some pretty bad article written by some pretty famous guy on some pretty high traffic website. I’m not going to comment on it – other people like Rami Ismail did that very well already. Interestingly enough, it revived the old debate about “passion” and “crunch”, and we’ve seen a fair number of interesting articles about it as a result. This is not one of those articles either.

Frostbite plaque

What this is is just a simple look at what’s going in my team, Frostbite Cinematics, which I think is interesting because Frostbite is in a fairly unique position in the industry, and that translates to a fairly different approach to overtime.

Spoiler alert: there’s pretty much none.


The extension method trick

If you’ve been writing C# code lately you should be pretty familiar with extension methods: it’s a nice compiler feature added to C# 3.0 which lets you “attach” methods to an existing type.

So for instance, if you define the following extension method:

    static class ConsoleExtensions
        public static void WriteReversed(this TextWriter writer, string message)
            writer.WriteLine(new string(message.Reverse().ToArray()));

You can write:

    Console.Out.WriteReversed("hello world!");

You effectively “extended” the TextWriter type with a new WriteReversed method in which you get a pseudo-this reference. Before extension methods, these types of methods would have been in a ConsoleHelper class of some kind – a lot less discoverable for the clients of your library.

Now, the (not so-)peculiar thing about it is that the pseudo-this variable can be null. Indeed, since this whole feature is only syntactic sugar, as they say, it’s just a more elegant way to write the following, which is a plain old function call:

    ConsoleExtensions.WriteReversed(Console.Out, "hello world!");

But it means it would look like you’re calling a method on a null object without getting slapped in the face with the infamous NullReferenceException.


You just need to add some simple parameter validation code to your extension method:

    static class ConsoleExtensions
        public static void WriteReversed(this TextWriter writer, string message)
            if (writer != null)
                writer.WriteLine(new string(message.Reverse().ToArray()));
    TextWriter writer = null;             // woah, are you crazy!?
    writer.WriteReversed("hello world!"); // woah, this is crazy!!

This trick can be pretty useful if you have an API that aims to be as minimalist as possible for the user and null is a valid case for one of the objects exposed publicly. You can already handle this with various well known techniques like wrapping or the NullObject pattern, but the extension method is a nice new alternative that’s enough in some cases and requires a minimum of code.

I’ve seen that trick used only once so far, in the excellent MvcMiniProfiler:

    using (MiniProfiler.Current.Step("Getting the answer to the ultimate question"))
        var result = DeepThought.Compute();
        ViewBag.UltimateAnswer = result;

Here, it’s totally OK if you disabled profiling in your web application, which means MiniProfiler.Current would return null. That’s because the Step() method is an extension method that checks for a null value provided as “this”, and doesn’t do much in that case.

Sure, they could have abstracted the profiler behind an IProfiler interface and have the MiniProfiler.Current return a NullProfiler when profiling is disabled, or something like that, but that would have been a lot more code than just using an extension method.

Formatting mongo output

If you’re working with MongoDB‘s mongo command line tool and you often see yourself reading through this:

> db.contactInfos.find({"source":ObjectId("4e39da93058cfe28a039b546")})

{ "_id" : ObjectId("4e6e9a9f058cfe01dc1bcded"), "source" :ObjectId("4e39da93
058cfe28a039b546"), "sourceId" : "754731", "sourceUserName" : "loudej", "ful
lName" : "Louis DeJardin", "description" : "A software guy on the ASP.NET te
am and author of Spark", "avatarUrl" : "
161371928/DSC01367_normal.jpg", "websiteUrl" : "",
"location" : "Kenmore, WA", "friendCount" : 97, "followerCount" : 1428 }
{ "_id" : ObjectId("4e6e9a9f058cfe01dc1bcdf3"), "source" : ObjectId("4e39da9
3058cfe28a039b546"), "sourceId" : "16925866", "sourceUserName" : "sebastienr
os", "fullName" : "S├ębastien Ros", "description" : "", "avatarUrl" : "http:/
normal.jpeg", "websiteUrl" : "", "location" : "S
eattle", "friendCount" : 18, "followerCount" : 142 }
{ "_id" : ObjectId("4e6e9a9f058cfe01dc1bcdf4"), "source" : ObjectId("4e39da9
3058cfe28a039b546"), "sourceId" : "768197", "sourceUserName" : "haacked", "f
ullName" : "Phil Haack", "description" : "This bio is not true.", "avatarUrl
" : "", "websi
teUrl" : "", "location" : "Bellevue", "friendCount" : 225
, "followerCount" : 15947 }

…then you may want to add toArray() at the end of your line:

> db.contactInfos.find({"source":ObjectId("4e39da93058cfe28a039b546")}).toArray()

        "_id" : ObjectId("4e6e9a9f058cfe01dc1bcded"),
        "source" : ObjectId("4e39da93058cfe28a039b546"),
        "sourceId" : "754731",
        "sourceUserName" : "loudej",
        "fullName" : "Louis DeJardin",
        "description" : "A software guy on the ASP.NET team and author of Spark",
        "avatarUrl" : "",
        "websiteUrl" : "",
        "location" : "Kenmore, WA",
        "friendCount" : 97,
        "followerCount" : 1428

        "_id" : ObjectId("4e6e9a9f058cfe01dc1bcdf3"),
        "source" : ObjectId("4e39da93058cfe28a039b546"),
        "sourceId" : "16925866",
        "sourceUserName" : "sebastienros",
        "fullName" : "S├ębastien Ros",
        "description" : "",
        "avatarUrl" : "",
        "websiteUrl" : "",
        "location" : "Seattle",
        "friendCount" : 18,
        "followerCount" : 142
        "_id" : ObjectId("4e6e9a9f058cfe01dc1bcdf4"),
        "source" : ObjectId("4e39da93058cfe28a039b546"),
        "sourceId" : "768197",
        "sourceUserName" : "haacked",
        "fullName" : "Phil Haack",
        "description" : "This bio is not true.",
        "avatarUrl" : "",
        "websiteUrl" : "",
        "location" : "Bellevue",
        "friendCount" : 225,
        "followerCount" : 15947

Much more readable, isn’t it? The only problem is that mongo will, by default, return only the first 20 hits of a query (it will write “has more” at the bottom to indicate that, well, there’s more). The toArray() method, however, will get everything and print it out. This could mean printing a lot of stuff, so you may want to stick a limit(20) in between if you’re not sure:

> db.contactInfos.find({"source":ObjectId("4e39da93058cfe28a039b546")}).limit(20).toArray()