In this timeless feature from the March 2010 issue of Game Developer Magazine, we revisit the mighty kludges and well-meaning hacks that are occasionally required to get our games into the hands of eager consumers.
This article originally ran as a follow-up to the magazine's original 2009 Dirty Coding Tricks feature, but these hacks and heroics span the entire history of computer and video games, even extending laterally into the business software sector. Delight at the ingenuity, marvel at the audaciousness, learn from the mistakes of your predecessors, but most importantly, release games that work—on time, too.
If you want to share your own surprising solutions for getting games out the door, we want to hear them! Send your dirty tricks to Gamasutra and they may be featured in a future article.
Thanks for playing!
Back on the first Wing Commander we were getting an exception from our EMM386 memory manager when we exited the game. We'd clear the screen and a single line would print out, something like "EMM386 Memory manager error. Blah blah blah."
We had to ship ASAP, so I hex edited the error in the memory manager itself to read "Thank you for playing Wing Commander."
- Ken Demarest
100 percent pure fruit juices
When I first started working in the game industry I spent most of my time shuffling between various small, underfunded startups. Here is a horror story from the good old days when men were men and used DirectX 7. I worked for a company that had been forced to use a certain 3D engine by the publisher. The engine shall remain nameless, but the publisher had been persuaded to buy a number of licenses for it as part of a bulk licensing deal, and insisted that we use it.
To be blunt, the engine did not work, and I spent most of my time at the company making the 3D engine do obvious things correctly, such as fixing the engine company’s implementation of single-pass multi-textured lightmapping.
One of the more interesting things that did not work was the BSP compiler. Level designers would build level geometry with correct visibility, and then minor geometry adjustments would break visibility on the other side of the map. To this day, I don't know why this happened, but I believe that the engine’s BSP compiler added brushes to the BSP tree in a random order, and certain combinations would just ... randomly break things.
At the time, I had never heard of a randomized algorithm, but I invented one regardless—a preprocessing stage was added to the BSP compiler that shuffled the order of the brushes before they were fed to the engine's BSP compiler. That way, if the level geometry broke the BSP compiler, we could just try shuffling the brushes with different random numbers until we found a combination that worked, and then we stuck with it until the next time the BSP compiler broke.
The game itself was a disaster, and both the engine and the game were featured in a Penny Arcade cartoon that contained the first ever appearance of the Fruitf*cker 2000. This remains a milestone in my personal career.
- Nicholas Vining
I was working on NBA JAM TE for the Genesis, which used a flash chip to store game data. The game had been tested for months, and everything was ready to go, so the publisher ordered 250,000 copies of the cart. But it soon became apparent that no one, for months, had reset the flash chips on the test carts to make sure the flash init routines worked correctly. Nor did anyone order any carts for testing.
It was only after all the carts had been ordered that we discovered the flash init code was dead, and that the carts could not save games properly! The studio went into meltdown trying to figure out how to ship 250,000 broken carts. Suggestions of production lines adding extra resistors and other hacks to every cart were tried and failed.
When all seemed lost, someone figured out if you played the games in an odd, and very specific order, the flash memory would sort of work. So an extra leaflet was added to every box explaining how to use this "feature."
- Chris Kirby
My favorite last-minute hack was in the four-player mode of Nitrobike for PS2. As usual, the level designers and the artists had done their work without the slightest concept of real-world feasibility, and as usual, the job of "finishing" the game was left entirely to me.
After much battling and butting of heads, I got them to simplify the level artwork, create visually occluding features, and remove excessive dynamic objects, but that couldn't save one particular movie-themed level. The level design itself resisted occlusion; it consisted of two large rooms (sound stages, in the fiction) with no occluding features, connected by two open doors.
One door was in the middle of the wall. There was no possible way to arrange occluding polygons to block one room from seeing the other room in any effective way, and there was no way to cut out any more level artwork without completely ruining the character of the level. I really needed a way to put one big occluding polygon in the wall, covering the middle door. That's when it hit me ... a particle-based "wall of smoke" covering the middle door would fit in well with a movie-themed level, and would completely solve my problem.
A wall of smoke could be driven through, but not seen through, and would conceal the existence of my occluding polygon! I was able to commandeer one artist to create such an emitter, and one level designer to place emitters on either side of the door, both facing each other.
Finally, in between, I put one very large occluding polygon. It looked good, and fixed the last performance problem I had with the levels.
- Steven Boswell II
...Or is it over here!?
I've been writing games for over 20 years, and was the recent global technical director for THQ, so I've seen a lot of terrible hacks. But there’s one in particular that I still laugh at, going back to Beam Software in the early 1990s.
In those days, before nice IDEs and smart compilers, we used to write all games in assembly language. All the .s files contained the respective assembler code for a particular part of the game; creature1.s, collision.s, controls.s, and so forth. We used makefiles too, where the programmer would create a new .s file, and place it after everything else in the makefile.
The idea is that you’d type “make” on the command-line, and the assembler would assemble each file into a new .o and then the linker would link all of them together to build the final executable. We had one programmer who notoriously wrote buggy code that would typically stomp on some random piece of memory. Usually buffer overruns.
He would spend some time trying to find the bugs, but when he couldn't, he would ... get this ... reorder the makefile so the files statically linked in a different order in memory! That just meant the piece of memory being randomly written over was now somewhere else, but by pure luck, the game wouldn't crash, either. He kept doing this until basically all permutations of the makefile resulted in a crash.
At the 11th hour when the game was about to ship, he solved the problem. He simply kept creating new .s files filled with little pieces of data, which he inserted into random places in the makefile until it somehow didn't crash—then shipped it! There are at least two games (for the Game Boy) released during that time period that use this “technique.”
- Shane Stevens
We were trying to ship World Series of Poker 2008, which was our first PlayStation 3 game. The PS3 allows several different screen resolutions, and two screen aspect ratios. We had designed a widescreen 2D shell, but didn't have the time or resources to make a standard-definition 2D shell. I scoured the TRCs, and couldn't find any reason that letterboxing wasn't allowed.
So our standard-definition view was simply our widescreen view with black bars above and below the picture. The publisher tried desperately to invent TRCs out of thin air to keep us from doing this, but eventually ran out of ideas, and we went ahead with it. Besides, only a few hours after I bought my own PS3 and played it on my standard-definition TV, I started shopping for a widescreen TV. I doubt many people connect their high-tech PS3 to a low-tech tube TV anyway!
- Steven Boswell II
For one reason or another, there was this huge incompatibility between an existing code base, and a new (approximately) zillion lines of code that had been written to use the first set of code libraries. The first set of code was written by people who were more into the idea of "safe" programming, making it as strict and restrictive as possible, to avoid errors. So they used a C / C++ language feature called CONST.
CONST means "constant," and makes sure that read-only variables can't be changed inside certain functions. CONST and non-CONST code are not compatible and cause the compiler to barf. The team decided to just take a hacksaw to the code and performed this clever little trick: #define const which redefines const to be.... NOTHING. empty space. So: const int x; when pre-processed for compiling becomes: int x; This is the equivalent of buying a car, taking two of its tires off, and using it as a motorcycle.
Personally, I hate CONST with a passion, so I like this trick. But to some people this is like taking a pair of scissors to your seat belt.
We had a bug on a PlayStation 3 Unreal Engine 3 title (it was the first PS3 UE3 title that shipped) where in debug, we'd inexplicably get a crash in printf() when connecting to a multiplayer game.
The client in debug would print out the hashes of every content package the server told it to load, and apparently one of them (on this build) had a % in the hash. We couldn't figure out a reasonable fix for it, but #ifndef PS3 worked just fine until the next data build, when it disappeared.
About a year later on the next project I ran into exactly the same bug. I used exactly the same fix. The worst one was actually after ship, when we were doing a content patch. The way our DLC/patching worked, we couldn't patch any compiled UnrealScript, but there was a bug where two RPC calls had not been marked "reliable," meaning that the packets to invoke them are resent until ACKed. As a result, under reasonably crappy network conditions, the functions to toggle readiness and voice status in the lobby would sometimes not get called. But marking RPCs as "reliable" needs to be done in UnrealScript, and we couldn't patch UnrealScript.
So, at startup, we looped through all loaded UFunction objects (C++ representation of script function), did a string compare on the name, and set the "reliable" flag on those two. Worked great.
- Anonymous (sourced from Reddit replies to the original 2009 Dirty Coding Tricks article)
Your fly is down
Xbox Live Arcade games on the original Xbox needed to be packed entirely inside the .xex file. To accomplish this we stored our data in a .zip file embedded as a data section in the executable. As the file grew, it soon became impossible to load the data section into memory, and allocate enough memory to unzip it, and pull out the needed file.
To remedy this, I implemented some code which read the PE header for the executable as soon as the game loaded, and noted the offsets for data sections. That way, the file stream which was reading the executable could just skip to the offsets for the different zip files, and stream straight out of the executable without ever loading the data sections into memory.
- Pat Wilson
This is an older one, but Force 21 was an early 3D RTS which used a follow cam to observe your current platoon. Toward the end of the project we had a strange bug where the camera would stop following the platoon -- it would just stay where it was while your platoon moved on, and nothing would budge it. The apparent cause was random because we couldn't find a decent repro case.
This went on until, finally, one of the testers noticed that it happened more often when an air strike occurred near your vehicles. Using that info I was able to track it down. Because the camera was using velocity and acceleration and could be collided with, I had derived it from our PhysicalObject class, which had those characteristics. It also had another characteristic: PhysicalObjects could take damage. The air strikes did enough damage in a large enough radius that they were quite literally "killing" the camera.
I did fix the bug by ensuring that cameras couldn't take damage, but just to be sure, I boosted their armor and hit points to ridiculous levels. I believe I can safely say we had the toughest camera in any game.
- Jim Van Verth
I was a tester for The New Tetris on the N64. There was a crash that I could reproduce every time, which would display a dump of the registers just before locking up. You had to power cycle the N64 to get it to go away: even the reset key was unresponsive.
Version after version, the developer said the bug was fixed, and version after version I reproduced it. Closing in on the shipping deadline, the developer had to close out all crashing bugs in order to ship. (Testing is done by Nintendo even on third-party games, and Nintendo has to approve it.) But this bug would just not go away.
The game also had some unrelated secret codes you could enter to unlock various things. One day I joked that the developer should replace the hex dump screen with a screen that says "Congratulations! You have discovered a secret code! Turn your console off and back on, then enter the username HALUCI." So he did. And that's how it shipped.
- Anonymous (taken from Reddit replies to the original Dirty Coding Tricks article)
I was one of a few interns at IMAGIC in 1982–83, and back then we were all doing Intellivision carts. One of the programmers had to leave to go back to school, and I was chosen to fix the random crash bug in his game. It turned out to be a stack overflow in the timer interrupt handler.
Since the only reason for the handler was to update the display of the on-screen timer, I added some code to test the depth of the stack at the beginning of the interrupt routine. If we were in danger of overflowing the stack, it would return without doing anything. Since the handler was called multiple times per second, the player never noticed, and the crash was fixed.
- Anonymous (taken from Gamasutra.com replies to the original Dirty Coding Tricks article)
Back to basics
My cohort Mike Mika and I were porting Klax, the arcade tile matching game, to Game Boy Color. It was a fun, intense, six-week project to bring one of our favorite games to the system.
We had the C source code (which was just Escape from the Planet of the Robot Monsters with a lot of robot monsters commented out and Klax put in), and we had chatted a lot with Dave Akers, the original arcade programmer, who had coded the prototype in Amiga BASIC over a weekend and ported it to C in something like one day.
We coded the game in straight Z80 assembly. There was lots of fun stuff like copying code to a white board and stepping through it one line at a time mentally, updating the memory contents on another white board, because we didn't have a real debugger. Good times.
Anyway, we got to crunch time and everything was working great, I was playing the arcade version and testing the GBC version when I ran into a weird edge case scoring bug. I don't remember the actual case, but it involved something like doing a big cross which dissolved into some diagonals. Anyway, it scored wrong on the GBC compared to the arcade machine.
Needless to say, I discovered this at around 11:30 at night (right before a milestone). We ran through the code a million times, compared our assembly to the arcade C code, and the bug just didn't make sense. We were scoring logically, and our code was doing the same thing in the same order as the C, which was just a line-by-line translation of what Dave had originally done in Amiga BASIC. (I suspect Dave was a little like Mike—great at assembly and BASIC, but not a huge C fan 20 years ago when Klax was done.)
Finally, around 5 am, after smashing our heads against this all night, we had an idea—not something we thought would work, but at least something worth trying. Mike coded the scoring system in Quick BASIC, and it scored just like the arcade machine. So we translated the BASIC, line by line to Z80 assembly.
It worked. God knows why, but it behaved like the arcade machine (it might have something to do with the fact the original was coded in BASIC). We sent to build to Atari, printed out both versions of the code and went to Denny's. We looked at the code for an hour over breakfast and still had no idea why it behaved differently. We both swear today that the code should generate identical results! But sometimes when it gets late, you have to go to the voodoo programming!
- Chris Charla
Games aren't the only place where coding hacks can save the day. Here are two non-game submissions that were too enjoyable not to include as a bonus.
Five years ago I was working as programmer in the video surveillance software industry on some very sensitive and complex security software. We had a great product that worked well, and the most difficult part of this software was that it involved the display of 50 video streams onscreen at the same time. The software needed a huge chunk of memory to work, and it was supposed to be up 24/7.
We first shipped to our beta customers a few weeks before general deployment. A week later we noticed there was a huge memory leak -- about 4KB per minute. I spent a couple days investigating the leak with no results, and there was no time to fix it before shipping. Memory was a key part of the software, and a leak of that size would completely kill the application. Doing some testing (under Windows), I had to hide the software window to go back to the coding window, and noticed a huge drop in memory when I did so.
I then remembered that when you put the window in the notification area or in the start-menu bar, Windows immediately reclaims unused/freed memory. Here was our chance! I wound up adding a timer in the application that every couple of minutes would put the window in the Start menu bar, and display it fullscreen right after. It looked like a blink on screen, but it worked! We were able to ship after that, giving us some more time to fix the bug (which we found a few days later— some window handle was not properly cleaned).
- Yohan Launay
Results may vary
Back in the 1970s I was working on a banking system with a team using a long-gone programming language known as MPL2. This language had a restriction of 256 global variables and, since all were in use, adding new features to the system often meant searching for a variable to free up or use for two different purposes in different parts of the code.
It was a risky and a time-consuming process. Within the program each function could have its own 256 variables limited to the scope of that function. At home one night a revelation came into my mind. The next morning I proposed to my boss that we wrap the entire code in a function. We would then have 256 global variables of the program available to us, as the current 256 were now safe in the "inner" function.
The program itself would just declare some more variables then call the function, which was the entire original program, but now able to "see" not only its own embedded 256 variables but also the newly available 256 global variables. My boss was skeptical but allowed me the two hour compile window to try it, and was dumbfounded when it worked, overcoming what had been a problem for the team for a couple of years.
- Rob Hindle