As we've discussed in the past, video game code probably shouldn't be held to the standards of your average WTF: they're operating under wildly different constraints. So, for example, when a popular indie game open sources itself, and people find all sorts of horrors in the codebase: hey, the game shipped and made money. This isn't life or death stuff.

It's a little different when you're building the engine. You're not just hacking together whatever you need to make your product work, but putting together a reusable platform to make other people's products work.

Rich D, who previously shared some horrors he found in the Unreal engine, recently discovered that UnrealScript has a useful sounding JsonObject. Since Rich is thinking in terms of mods, being able to read/write JSON to handle mod configuration is useful, but anyone designing a game might have many good reasons to want JSON documents.

The file starts promisingly with:

class JsonObject extends Object native; /// COMMENT!! …

It's good that someone put that comment there, because I assume it was meant as a reminder: comment this code. And the comments start giving us hints of some weird things:

/** * Looks up a value with the given key in the ObjectMap. If it was a number * in the Json string, this will be prepended with \# (see below helpers) * * @param Key The key to search for * * @return A string value */ native function string GetStringValue(const string Key);

The method GetStringValue returns a string from JSON, but if the string is a number, it… puts a \# in front of it? Why?

function int GetIntValue(const string Key) { local string Value; // look up the key, and skip the \# Value = Mid(GetStringValue(Key), 2); return int(Value); }

Oh… that's why. So that we can ignore it. There's a similar version of this method for GetFloatValue, and GetBoolValue.

So, how do those \#s get prepended? Well, as it turns out, there are also set methods:

function SetIntValue(const string Key, int Value) { SetStringValue(Key, "\\#" $ Value); }

In addition to these methods, there are also native methods (e.g., methods which bind to native code, and thus don't have an UnrealScript body) to encode/decode JSON:

/** * Encodes an object hierarchy to a string suitable for sending over the web * * @param Root The toplevel object in the hierarchy * * @return A well-formatted Json string */ static native function string EncodeJson(JsonObject Root); /** * Decodes a Json string into an object hierarchy (all needed objects will be created) * * @param Str A Json string (probably received from the web) * * @return The root object of the resulting hierarchy */ static native function JsonObject DecodeJson(const string Str);

Guided by this code, Rich went on to do a few tests:

  • Calling SetStringValue with a string that happens to start with \# causes EncodeJson to produce malformed output.
  • Calling SetStringValue with any string that might require escape characters (newlines, backslashes, etc.) will not escape those characters, producing malformed output.
  • Which means that the output of EncodeJson cannot reliably be parsed by DecodeJson, as sometimes the output is invalid
  • Sometimes, when DecodeJson receives an invalid document, instead of throwing an error, it just crashes the entire game

Rich has wisely decided not to leverage this object, for now.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!