15 March 2010

Be Careful with Default Arguments

CRC16::computeMemory( const char* data, unsigned length, uns16 crc = 0 );
CRC16::computeString( const char* data, uns16 crc = 0 );
This little gem caused quite a headache. Allow me to explain.

EQII's streaming client uses 16-bit CRCs to tell if an asset has changed. All 500,000 individual assets have a 16-bit CRC calculated and stored in the master asset list (manifest). When an asset is downloaded, it is cached on disk and stored with the 16-bit CRC. To save time, the client doesn't calculate the CRC on each asset that it downloads, it just uses the CRC stored in the manifest.

Every time the client runs, we download the manifest and check the CRCs in the manifest against the stored CRCs for our cached assets to see if any changed. If an asset has changed (the CRC is different), we delete the cached copy and request the replacement, even if it's not immediately needed.

When the streaming client launched, everything worked as planned. It worked great! Little did we know that a particularly evil bug was lurking.

I discovered a problem when I wrote a utility to convert old PAK files (the game data shipped on the DVD) to cached streaming assets. NOTHING matched the CRCs in the manifest. Every asset was wrong. I looked over the code several times and everything looked fine. This bug didn't make sense.

And then it hit me.

The code that was building the manifests was doing this:
uns16 crc = CRC16::computeString( data, dataLength );
Talk about /facepalm. The function I meant to call was CRC16::computeMemory(). The function I actually called treated the input like a null-terminated string. This means that the CRC was only calculated up to the first NUL character and the dataLength parameter was actually being treated as a starting CRC value. This was a bug that had to get fixed. Someday, perhaps many years from now, this would be a huge bug that would waste a lot of someone's time to hunt down.

Oh, but the fun doesn't end there. I couldn't just change the function to fix the bug. Doing that would mean that every streaming client user would have to re-download everything that they had already downloaded. Every CRC would change and the naïve client would happily delete everything and start over. To fix this properly would take a highly-synchronized effort to fix and push the manifests while deploying a one-time-only tool that would re-calculate the CRC for all assets that people had already downloaded.

The moral of the story: be careful with default arguments. They can really hurt if misused.

09 February 2010

Evolution of a Streaming Client

It's funny how many things in the game industry start out as "I wish..."
... I wish we had Guild Halls.
... I wish we had Shader 3.0 support.
... I wish we had Battlegrounds.

... I wish our game was easier to download.

It's equally funny how many things are started by people in their own time just trying to make the game better. That's how EverQuest II's streaming client started out.

Taking an existing game (with 12GB of client assets no less) and streaming it is no simple task. I started off with a "proof of concept" just to prove that it could be done with EverQuest II. As I got into it, the concept became a full fledged project. It wasn't officially on the schedule, so it was really a labor of love on my part. After a few weeks of silently working on it, I called the producer into my office and said, "Hey, check this out." Needless to say he was pretty surprised.

There are three major conversion steps for a streaming system.

Serving the assets


EverQuest II has roughly 500,000 client-side asset files: meshes, textures, collision meshes, shaders, data files, sounds, music, you name it. Have you ever tried putting half-a-million tiny files in a directory? Take my word for it: Don't.

My first inclination was to build a custom server. The server would run off of the PAK files that we already ship with the DVD-based game client. I had grandiose plans about how to track files that clients were downloading and automatically send assets to clients that they didn't know they needed.

But alas, it was not to be. A custom server means that every client would have to be talking to our server. We would have to think about where to place the server geographically, handling varying load characteristics, availability, bandwidth, etc. These were all questions that had already been answered; we didn't need to ask them again and try to come up with our own answers.

What else is great at serving files to a large number of clients all over the world? Web servers! Specifically, HTTP servers. We already used a CDN for patching purposes--we just needed to serve all the game assets individually and on-demand now.

This caused another wrinkle. The client needs to know a list of all the assets that are available and whether the assets that it has previously downloaded are out of date. We call this the "manifest." This manifest must be fully up-to-date before the client tries to load ANY assets. My custom server knew how to negotiate a manifest with the client in a fairly bandwidth-friendly way because it was smart. CDNs are less smart--they just serve files. EQII's manifest is about 6MB, which you definitely don't want to download every time you run the game. The solution I developed involves parts of the manifest available as separate files and an overarching CRC file that is requested first. The CRC file is always requested, but it's only about 8KB. Based on comparisons with the CRC file, the client reconstructs the full manifest by grabbing parts that it needs.


Requesting the assets


Compared to everything else, serving the assets is the "easy" part. Requesting the assets is far more difficult. You're essentially replacing file system access with a network connection. That sounds a lot easier than it is. File system access is inherently goverened by the Operating System and allows any thread to open nearly any file and read data from it. A network connection is a single pipe (or in our case, a collection of pipes) that must well-defined and tightly-controlled access. Any thread that could just expect to read from a file at any point must now be synchronized with other threads requesting assets from a network resource.

Another major difference is that file system access is synchronous from an application's perspective. This means that while waiting for the Operating System to read data from a file, the thread goes to sleep and allows the system to do other things. Generally this happens so quickly that you barely notice, but network connections aren't nearly as fast as your local hard disk. For this reason, we want most of our asset requests to be asynchronous: we send the asset request and go about doing other things until it finishes at some later time.

Unfortunately, it's much easier to do synchronous reads than asynchronous. The EverQuest II client had many synchronous reads that you didn't even notice because the file system is fast enough. If they weren't made asynchronous, a streaming client would appear to 'lock up' while waiting for an asset to be fetched. Obviously, this is undesirable, and nearly unavoidable in some cases.

Furthermore, network connections in games are usually given time by the main thread to do their work (colloquially referred to as "pumping"). That won't work in this system. What if the main thread needs to synchronously load an asset (which still happens occasionally, especially on client startup)? It would be waiting for an asset to finish loading and wouldn't be able to update the network connection that it is effectively waiting on.

Clearly, a system is needed that can pump itself. Any thread can request an asset synchronously or asynchronously and the network connection continues updating as long as the client is running. The system should be able to determine if a request for an asset has already been sent and we don't need to waste bandwidth by requesting it again. The system should be able to recognize and quickly send higher priority requests. And, oh yes, let's not forget about failure cases. This piece of technology is the very heart of the streaming client.


Storing the assets

Obviously, once an asset has been downloaded, we don't want to waste bandwidth downloading that asset again. It might take minutes to enter a zone for the first time, but we don't want to take that long every time we enter that zone. Therefore, that asset must be stored locally.

A possibility is to store each asset as its own file, but this fails in practice. Operating Systems are not optimized for hundreds of thousands of tiny files. No, these files must be stored in a larger file, packed together and easily accessible.

EQII already has a packed file format. Unfortunately, the way it's set up does not lend itself to modification. When EQII's packed files are written, they're never intended to change. With new assets being downloaded constantly, these files will be changing, and often.

My solution was to develop a new type of asset database specifically suited to our needs. These database files can store a large number of tiny assets, rapidly add and remove assets and quickly retrieve individual assets.


Other Considerations

The most difficult part of building a streaming system for an existing client has been trying to change synchronous asset requests into asynchronous. Consider the following simple example:
Animation* pAnim = pAssetSystem->LoadAnimation( "animation/player_anim1" );
if ( pAnim )
{
// Do something with loaded asset
}
The above example would need to fetch player_anim1 synchronously. Changing this to be asynchronous might look like the following example:
Asset<Animation> anim( &myAnimLoadHandler );
pAssetSystem->StartLoad( &anim, "animation/player_anim1" );
...

void AnimLoadHandler::OnLoaded( Asset<Animation>& a )
{
// Do something with loaded asset
}
There's much complexity missing from the second example, but the point should be clear: making something asynchronous is much more difficult than making something synchronous.


Conclusion

Working on the streaming client was one of the most fun projects that I've ever worked on in a technical sense. It was challenging, but the results are a huge payoff.

11 November 2009

Dr. Laura: Dr Jekyll and Mr. Video Gamer

Dr. Laura has a video blog about a man who plays video games for his down time:


Now, I actually enjoy reading most Dr. Laura articles. Her common-sense approach to most family psychology issues provides both entertainment and self-reflection.

However, sometimes she's just a little too old-fashioned for me. She seems to come off in this video as entirely anti-video-game. I'll grant that the guy mentioned in the video is acting like a complete idiot, but not all video games are bad. Consoles like the Wii consistently encourage family gameplay.

Maybe it's just because I'm in the video game industry, but if a guy wants to play video games for his downtime, that seems fine. Of course, you have to look at your priorities. Personally, family comes first. Always. I will usually sit down to play games after the rest of my family has turned in for the night. If the rest of the family is napping, occasionally my son and I will enjoy LEGO Star Wars or some other innocuous game.

Everything in moderation, but please don't demonize all video games because some idiot guy can't put down the controller and spend time with his family.

24 October 2009

Keep Designer Data in Files (not Databases)

Whoa! Did I just fall off the deep end?! Designer Data in ... files?! Data... doesn't that belong in a database?! There are those who say that it does, but I disagree, at least when talking about development.

In MMO terms, Designer Data is data that our Design team creates: Quests, Spells, NPCs, Paths, Items, Recipes, Locations, etc. Tons and tons of data that, together with art, is really the lifeblood of the game.

Designer Data is to Designers as Source Code is to Programmers. It's constantly changing and growing. Different people own different parts of it. Everyone on the dev team needs all of it and as up-to-date as possible to do their current tasks. Servers need it in a highly-optimized form to run the game. There are many parallels between Designer Data and Source Code.

Which brings me to my first major point. As files, Designer Data can be stored in Source Control (we use Perforce, and I love it). This gives an amazing amount of incredibly important functionality and basically for free:
  • Revision history - As text (EQII uses XML) files, anyone on the team can go back and see (through the same tool that everyone must use) all previous changes to a file. Anyone can see exactly what changed between each version or between multiple versions and who made the changes. Also, you can go back in time and grab previous file revisions.
  • Changelists - Most source control systems group together file changes. Perforce calls these Changelists. Grouping files together is an effective tool for seeing relative changes based on concept. One changelist might be labeled "Zone 1 population" and contain NPCs, Quests that those NPCs give out, Paths that those NPCs follow, etc. If a Changelist breaks the game, it can be rolled back wholesale (and you can see who made it and go pound them).
  • Integration - Changes are made in branches and we have branches for each distribution of the game. As a group of changes become ready to go Live, they are integrated into the next distribution (Main goes to QA, QA goes to Staged, Staged goes to Live). With Code and Data both in the same system, integration to the next distribution becomes a one-step process.
  • Code/Design in sync - Since Source Code and Design Data are both stored in the same system, they are always in sync. If a Programmer makes changes that affect both (which often happens), he can rest assured that everyone will easily get the latest in both.
  • Sandboxing - Designers are only affecting their own local version of design data until they check it in. They can do testing and make tweaks before checking in something that could potentially break everyone else.
  • Blame - It's so easy to see who made a change (and confront them if necessary).
If you were doing Design development in a database, you would have to develop your own solution around each of these (or use database tools that not everyone would have access to). And since the database is always up-to-date, and everyone is generally working out of the same database, problems could arise from changing formats or unexpected data.

Furthermore, Designer Data in files are free to have complex formats. For instance, in EQII the Designer Data is object oriented. Data definitions specify that a Character inherits from Entity and Entity inherits from a Base type. This also allows us to do things like have a base Predicate type and more complicated types like CharacterHasQuestPredicate that inherits from Predicate but has additional data members that only make sense for CharacterHasQuestPredicate (like the Quest name). In a database, you might have a Predicate table with a type column that is a number. You would have to look up what that number means from somewhere or write special tools that understand the relationship. Additionally, that Predicate table would have to include all of the options that any types could have in a very generic fashion. So you might have columns called "StringParam1" and "NumParam1". In the database then, you might have a row with type set to 12 and StringParam1 set to a quest name (or more likely NumParam1 set to a unique ID for a particular Quest). If you were looking at raw data, what would you rather see:
<object name="CharacterHasQuestPredicate">
  <string field="sQuest">quests/heritage/dwarven_work_boots</string>
</object>
Or:
Type     IntParam1    IntParam2
12 1243 <null>

The EQII Designer Data tool is very generic as it knows how to read the game's data definition. To actually add a whole new data type (Achievement for instance) actually takes zero changes to the Designer Data tool. It just takes writing a small text file that describes the Data Definition such as this one for our aforementioned CharacterHasQuestPredicate:
<objectdef name="CharacterHasQuestPredicate" inherits="Predicate">
  <fields>
    <fielddef name="sQuest" type="String" require_dir="quests/" default="" />
  </fields>
</objectdef>
However, reading XML files isn't the fastest thing to do. Server startup is fairly slow when reading from XML files, but EQII has a utility that converts the myriad XML files into a single file that contains all of the data in a highly optimized binary format (I've actually written about this file before). This file is mapped into virtual memory allowing all server processes running on the same physical machine to share one copy of the file in memory. For development purposes, developers can run their own servers against the XML data (and both internal development servers and rapidly changed external servers such as a Beta server could run against XML as well for fast turn around). Plus, there are enough options for converting XML data to databases that you could still use XML files for development and run production servers against a database.

The only major shortcoming of keeping Designer Data in files as opposed to a database is for searching and updating large amounts of data--things that a database is designed to do.

For searching data, EQII actually has a very workable solution. The EQII team has developed a system called VooDLe (a name playing off of Google and VDL--the internal name for EQII's data library). This is a very simple web solution that syncs to the latest data and indexes it for searching. It also detects file references and generates links. However, it doesn't help if you'd want to find all of the shields that have a block value higher than 300. For this, you can search VooDLe for the field that declares shield block value and quickly scan through the results. (Rarely is searching for a numeric value so cut and dried in EQII though with level scalars and combat scalars that affect everything.)

Updating large amounts of data is actually something that (shouldn't) happen very often. Furthermore, updating large amounts of data is error-prone and should be limited. You might even call this an inherent benefit to having designer data in files. When this is necessary, it's possible with a simple Perl script. If something does go wrong (even later) the Changelist can easily be reverted.

Based on all this, I feel it's more beneficial to a development team to use files for Designer Data during development rather than a database.

25 August 2009

EverQuesting

It has been a busy time for me. For the past few weeks I've been splitting my time at SOE between three different game projects.

Two of them are new projects that I can't talk about yet. I'm very excited about both of them (and think know you will be too!) and look forward to being able to talk more freely about them. One was a temporary post while waiting for the second to ramp up. Now that it has I will be transitioning to it full time.

The third is the project that I've been working on for over four years: EverQuest II. However, with the new project my involvement on EverQuest II is shrinking. My official title has moved away from Technical Director on EverQuest II to being the Technical Director on one of the new projects. Don't worry though: the EverQuest II programming team is in the hands of the very capable Greg "Rothgar" Spence. Plus I fully expect to be available to the EverQuest II team for questions and technical direction for a while to come.

I'm currently finishing up my last task with EverQuest II. This last hurrah is designed to do something very important--increase exposure to EverQuest II. I've been personally involved with nearly every change to EQII's trial program over the past four years, but this is the one I'm the most excited about.

EverQuest II is a very large game. Overwhelming content from the get-go followed by five (Soon™ to be six) expansions have conspired to create a client footprint of about 10GB. Yikes! That takes hours upon hours to download even on pretty fast connections. As my last task, I'm working on reducing the initial download down to about 60MB (which is barely a blip with broadband) and streaming the rest as the client needs it.

A few things make this possible. First, the fetching technology that we're using was first pioneered by the incredible talent on the Free Realms team. This made it much faster for us to get on-demand fetching up and running since they had already done the legwork and experimentation. Second, the fetches are nothing more than HTTP GETs which allows us to distribute all of EverQuest II's 500,000+ individual assets over a very fast CDN. Using a CDN means that we don't have to develop, test, distribute and manage our own file serving solution which obviously would take more time. Third, EverQuest II already has a fairly robust asynchronous resource-loading system that was fairly easy to extend with remote file fetching.

Getting asset fetching in place was really only the beginning of the story. My time since then has been focused on heuristics for prefetching the next assets the client will need and reducing some dependencies on synchronous IO (which would appear to "lock up" the client until the needed assets were fetched). These challenges are far more complicated, but the challenge is welcome!

It's been a great four+ years on EverQuest II, and I look forward to getting people playing after downloading a mere 60MB.

13 April 2009

Interview of Yours Truly

I was recently interviewed (a month ago, heh) on SOE Podcast #58. The interview starts at about 1:19:44. Whew, long podcast!

SOE does a podcast about every two weeks and feature interviews, goofy ads and exciting news about current and upcoming SOE games. You can also follow SOE_Podcast on Twitter for news and notification.

08 April 2009

Lost in Translation

EverQuest II currently runs in five languages: English, German, French, Japanese and Russian. However, all of the programmers and designers actually build the game in English. To accomplish the translation, we use a proprietary SOE library: the Text Translation and Templating Tool, or T4 for short. T4 is a very powerful tool that allows us to do translation through anything from dictionary lookups to macro expansion. Our Internationalization department (i18n for short) is responsible for maintaining the T4 library and the various language modules for it.

Unfortunately, sometimes we write things in a way that isn't easily language-agnostic. Take the following example for instance:

"Rothgar's Touch of Pestilence critically double attacks Autenil for 38 disease damage."

It's a common bit of text that appears in the combat chat when you're fighting in EverQuest II.

Let's break it down a bit.

First of all, there's the actor performing the action. This can be you or someone else; Rothgar in this example. There's also the target which can be you or someone else; Autenil in this example. There's an optional spell name (Touch of Pestilence) and how much of which kind of damage. Oh, and the action -- double attack in this case -- and actions may or may not be critical.

With the way T4 works with insertion macros, we use a base string like this:
"^1 $2 critically $3 $4 for +5 $6 damage"

But this really doesn't work, at least not for anything but English. That pesky verb needs context in other languages to be conjugated properly. We currently put in either "double attack" or "double attacks" based on who the source and target actors are. It works fine in English but comes out in other languages like gibberish.

The way to fix this is to build base strings that include the verbs, but this can explode quickly. Say there are five different verbs that could be used in that case. To make base strings that include the verbs with all of the other possibilities you end up with about 640 strings, and that's assuming that there is only one damage type (EQII allows multiple per attack).

In the end, I went with using a generic verb for non-English languages ("hit") and put special attacks or critical status parenthetically on the end:
Rothgar's Touch of Pestilence hits Autenil for 38 disease damage. (critical double attack)

This will be much easier on our translators and will make sense for our players. It's been a long time coming.