Dirty tricks in the code of games



the [When the schedule is running out and it is time to release, programmers can resort to dirty tricks in order to shove the game out the door. In this article are nine examples of such "crutches" from real life.]

Usually programmers are methodical and tidy creatures, all the forces striving for clean and beautiful code. But when the stakes are high, the ideal schedule is falling apart, and the game it's time to let the principle of "to finish at any cost" may be more important than elegance.

In such cases, the exhausted and overworked programmer is likely to ignore the best approach, replacing it with a less appropriate solution, just to end the game. We gathered nine stories of these developers about those moments when they could not meet the schedule and they had to save the project to resort to tricks.

the

put me in the best perspective


About four years ago [note. lane: the article was written in 2009] I worked as a programmer in a multi-platform project for the PlayStation 2, Xbox and GameCube. The development time was coming to an end, it is not surprising that in the code began to infiltrate hacks. In the PS2 version was later discovered a problem that was very difficult to keep track of: long-term test of the stability of the first level standing motionless character the game crashes. Unfortunately, this error occurs only in the disk Assembly for retail sale, which had no debug information. Fear of rejection from the verification division of the technical compliance (technical requirement check, TRC) Sony, we worked hard on a solution.

To narrow the boundaries of an error, we are constantly rendering a border around the edge of the screen with individual colors for different parts of the code. For example, blue is designated the render setting, green indicates the update control player. Since failures arise not always, lead engineer and I manually burn a bunch of disks and run them on multiple consoles PS2. (We must remember that the company was an independent developer, she had no it Department and a steep dakotapayday equipment.) The test cycle by modifying code, recording discs, start-up and expectation to narrow the approximate boundaries of the error — took hours. It was the last stage of development, and we were able to make only a strictly limited number of such cycles.



Spending weekends in the office waiting for a failure, we killed time World of Warcraft. Suddenly one of us noticed that the game does not end if you turn the camera 90 degrees to the right. First, the programmers rightly dismissed this as the randomness that hides a deeper error. The fix for this behavior would not deal with the root of the problem. However, we still have fixed it! When we ran out of time, we have created the last disc in the PS2 turned into the beginning of the level (for two other platforms were already ready) and sent for review. The game passed all the tests of the platform owners and was on time released on the market.

I wouldn't call the decision "a trick in the code", but it was definitely "dirty". Mystic this hotfix is still not revealed, but, fortunately, I've never heard that users complained about such a problem.

the — mark cook (Mark Cooke)

the

identity Crisis


This situation is familiar to all game developers: today we sent the "gold" of our game for the Xbox 1. The team testing the game all day, wanting to make sure that everything is in order. All happy and calm, for us the job is not finished.

After lunch, we create the latest build with a small final adjustments of game balance and begin the last test session, and then disaster happens: the game hard crashes! We're all running to their work machines, run the debugger and trying to figure out what was going on. This is not something trivial, like assert, and not even something that is relatively difficult to track, like dividing by zero. It seems that a few fragments of memory is garbage, but the memory test reports that everything is in order. What's going on?
Many hours later, our dreams of release, in time destroyed. We're trying to find the error and discover that one data file is loaded with incorrect data. Wrong data? How is this possible? Our system resource controls each resource by using the 64-bit identifier, consisting of a CRC32 the full file name and CRC32 of the entire file contents. In the same way we were United by the same resource files of the game into one. We had tens of thousands of files and two years of development, the conflict never arose. Ever.

Until this very moment.

It turned out that one of the innocent small fixes that designers checked in after lunch, led to the fact that the text file had exactly the same name and CRC, and other file resources, despite the fact that they were completely different!

When we discovered the problem, we heart sank. There was no chance to change the indexing system resource in such a short period of time. Even if we were working all night and we would never be able to know if everything is stable this morning.

As fast as we became desperate, there was a sense of how we can correct the error in time and manage to release the "gold". We opened to blame for the conflict text file that is added at the end of the blank and saved it. Looked at each other with wide smiles on their faces and said,

"Sent!"

the Noel Llopis (Noel Llopis)

the

DUI


I have been tasked to work on transport management system, which was an important part of our game. Fortunately, we were able to take most of the code from another Studio. Unfortunately, the code has not always seemed perfect, as is the case with almost all of the code that doesn't write itself. I found this cute piece of code that gets a variable from the cycle of the engine, but does it in the most blunt way possible (see listing 1).

the
//**************************************************
// Function: AGameVehicle::Debug_GetFrameCount
//
//! A very dirty way to get the current frame count; the variable is protected.
//!
//! \Returns the current number of frames.
//**************************************************
UINT AGameVehicle::Debug_GetFrameCount()
{
BYTE* pEngineLoop = (BYTE*)(&GEngineLoop);
pEngineLoop += sizeof( Array<FLOAT> ) + sizeof( DOUBLE );
INT iFrameCount = *((INT*)pEngineLoop);
return iFrameCount;

The funny thing in this piece of code is incredibly disgusting is that the object was a simple function to get the number of frames. But even if it would not have happened, then who wrote this code could easily add it yourself! Needless to say, when I pointed out the error, this code was not used neither in my game nor in the where he was taken. And if that's not an example of why you should do code analysis, I really do not know which examples need more.

the Austin McGee (Austin McGee)

the

Decimal code


Working at [company X], I thought we would never get to completion [of the project]. On one level we had an object that had to be hidden. We do not want to re-export the level and we didn't use names and checksums. So right in the middle of the engine code we had something like this. And the game was released with this code.

the
if( level == 10 &&object == 56 )
{
HideObject();

About a year later we were approached by a very frustrated artist who used our engine, and asked why at his level, find themselves tenth, not visible object. Really, why?

the — Anonymous

the

All signs point to "no"


This problem arose in the development of the new Wolfenstein Raven Software. I wrote the setup controller support for the Xbox 360. It turned out that when integrated with Live, all you need to know what the controller sends input events. We used code type Doom 3 was almost entirely copied from a Quake 3, so it was a pretty simple system.

The system events each event is transmitted with two integer arguments and a null pointer in case you need more options. Therefore, the task was to associate a controller ID with each input event. Like, no problem — just pack the controller ID to one of the integer arguments of the event, right? And of course, both arguments were already occupied.
So we came up with another idea: simply use our quick frame-by-frame memory without fragmentation to allocate memory, write to it, the controller ID, and then pass a pointer to it in the pointer events.

It turned out that the system of events after processing events independently cleans using free() a null pointer event. Of course, such behavior was totally incompatible with the multiheap our-approach. Moreover, the code we present the code snippet Doom 3, which relied on the invocation of free() in your code, so we couldn't just remove the call without making non-trivial changes in the entire code base. And if you add a third integer argument? Then you have to change several hundred of function calls.

The deadline was already on the nose, and I didn't have time to solve the problem. So I decided on the unthinkable — Packed the controller ID into the pointer parameter. In four-line comments entirely typed in "caps", I marked it as a terrible hack, and loaded into the code base. And this crutch was working without problems until the entire system was not replaced with something a little nicer.

the — David Dynerman (David Dynerman)

the

Sticky crooks


image

Here is the story of the project from the early days of the PS2. We had a lot of problems with the conflicts/boundaries, which was generally decided in the last minute rewriting of the system of conflicts of the character. It was changed to the model of "colliders" — a set of spheres that are much better handled collisions than our hierarchical tree of oriented bounding contours (that was the days before the advent of the Havok engine).

However, we have constantly had a rare "bug" that drove me crazy. We called it the " = " — every time the player character was next to the wall and slid along it, the sphere Collider has suddenly decided that he is on the other side of the wall, and not allowed to put it (i.e. the character is stuck to the surface).

It was one of those damned bugs "everything seems to be simple." Only need to find out why the sector believes that is on the other side, and fix it. The problem is that you need to track what happens in all cases, recognition of conflicts before this problem occurs.

When we failed to solve the problem of convenient conditional breakpoints in code that tracks strange responses, or change the position of zaputyvaet Collider, we just reduced the frame rate to "bug" simply do not arise. (The real solution to this problem would require blood, sweat, tears, and other fluids, without which usually it is possible to do, but that's another story.)

We sent the game to test, and always received the answer that a game with such a mistake will not be released. Even came up with one "solution" is to increase the scope enough to guarantee the absence of problems, but it is not really the correct fix. We've ported the game with this "bug" team of artists, so they fixed it, and I bought myself precious time to search for the true, underlying problems.

The testers started to send us feedback such as "the player collides with an invisible wall, this should not be". This problem of the same severity level (again, we've narrowed the boundary of the Collider and the character began to come close to the wall, because "=" is encountered very rarely and it was difficult to reproduce), but after a few such cycles, for which we fixed the rest of the game, a decision that required blood, sweat and tears, was finally found and the game continued its way into circulation and on store shelves.

Not to say that I am proud of this cycle of solving problems by avoidance and evasions, but at least we were able to temporarily remove this burden from his shoulders. We always said "Oh, no, we already sent the game to the test", and continued to solve the damn problem.
the — Anonymous

the

I and my patches


There's an old joke, something like this:

the Patient: "Doctor, I hurts when I do this."

the Dr: "Then don't do it".

It's funny, but unless in that situation, it is not wise words? Imagine my pain when I worked on porting a three-dimensional third-person shooter with PC on the first PlayStation.

Let's start with the fact that the PS1 didn't support floating point, so the porting was performed by dekompilierung code with the replacement of all floating-point values with a fixed decimal point. And it worked pretty good till it came to the recognition of collisions.

The PC version of the game, the geometry of the levels worked fine, but when converted to values with a fixed decimal point due to microscopic differences between floating and fixed-point manifested all the seams, T-shaped joints and other problems. This difficulty made itself felt: the main character (named Damp) just fell through this tiny hole into the void under the level.

We fixed all the holes found by configuring the geometry so that Damp no longer fails. But when the game was sent for testing to the publisher, he suddenly told us about many of the problems of "falling through the world". Every day was a new list of places, through holes in which could fail Damp. We corrected, correcting the geometry, and the next day we reported about a dozen other places. This went on for several days. The testing Department of the publisher hired one employee whose sole task was jumping around the world for ten hours a day to find places in which you can fail.

The problem was that the geometry was bad. She was tight and seamless. It was enough for the PC, but not for the PS1, where math is fixed-point greatly complicates the problem. The ideal solution would be a correction of the geometry, making it seamless.

However, this time-consuming task that with our limited resources it would be impossible to accomplish in time, so we relied on the testing Department looking for us the problem areas.

The problem in this case was that they found such places. Every day brought more pain. Every day new variations of the old "bug". It seemed that it would never end.

Finally reached us: the true problem is not the holes geometry. The problem is that the Damp sinks into the holes. Realizing this, I could write very fast and simple fix that looked something like this:

the
IF (Damp falls through a hole()) THEN
Not to fail

Actually the code was slightly more complicated (see listing 2).

the Listing 2: me and my patches

the
damp_old = damp_loc;
move_damp();
if (NoCollision())
{
damp_loc = damp_old;

Thousands of errors have been corrected in one fell swoop. Now instead of fall through the level, passing over the holes, Damp just twitched slightly. We have found the cause of our suffering and ceased "to do that." The publisher fired my tester-the"jumper", and the game was released.

That is released after some time. Inspired by the success of the approach of "if A==bad then NOT A", I have used this tool to patch a few more bugs. Almost all of them were connected with code recognition of collisions. Towards the end of development the bugs became more and more specific, and the fixes were more like "don't do vtechnology" (see listing 3: this is the real code left in the game).

the Listing 3: me and my patches

the
if (damp_aliencoll != old_aliencoll &&
strcmpi("X4DOOR",damp_aliencoll- > enemy- > ename)==0 &&
StartArena == 6 && damp_loc.y<13370)
{
damp_loc.y = damp_old.y; // never give
damp'have to touch the door. (the floating x and y)
damp_loc.x = damp_old.x;
damp_aliencoll = NULL; // this accept

What does this code do? The problem arose when Damp was concerned with a particular type doors at a certain level and in a certain place, and instead of correcting the root cause I made it so that when you touch the door Damp slid away from her and pretended nothing happened. Problem solved.
In retrospect, I find the code rather daunting. He patchel mistakes and not corrected them. Unfortunately, the real solution would be to rework the geometry of the game and recognition system clashes with the features of the PS1 computation. From the beginning the schedule was very aggressive, so we always thought that the end is near. Naturally, in such situation a quick patch always had an advantage over the difficult and costly decision problem.

But everything went so smoothly. Required hundreds of patches, then the patches themselves become the causes of problems and had to add more patches to change the behavior of the patches in very specific cases. There were new errors, and I again won their patches. In the end I won, however, the price of steel delayed the game's release for a few months and my daily 14-hour work all these months.

This experience turned me against patches. Now I always try to get to the root of the error even in the presence of simple and seemingly safe patch. I want my code was healthy. When you go to the doctor and say "it hurts when do this", then expect him to find out the cause of the pain and cure it. Pain, like bugs, can be a symptom of something much more serious. The moral: treat your code like you should contact the doctor.

the Mick West (Mick West)

the

In anger I am terrible


I once worked at THQ Studio Relic Entertainment on The Outfit, which you may remember as one of the first games for the Xbox 360. We started with the engine for PC (single-threaded) and I wanted about 18 months to turn it into a complete game for multi-core next-generation consoles. Approximately three months before the game's release, she worked on 360 at a speed of approximately 5 FPS. It was obvious that the game need a serious optimizations.

Measure the performance, I realized that in addition to slowness and "PC-style" code, there is also a lot of problems with the content. Some models were too detailed, the shading is too costly, and on some levels too many characters.

It's hard to convince a team of a hundred people that programmers can't just "fix" the performance of the engine, and I need to change the templates work, to which many are accustomed. They had to realize that the game performance is an issue for everyone, and I came up with the best way to show it, which was the truth.

The decision took about an hour. A fellow programmer took four pictures of my face: I'm happy, calm, a little angry, tearing my hair out. I put the photo in the corner and tied it to the frame rate. When the game was more than 30fps, I was happy when below 20, I was angry.

After these changes approach to the problem of FPS has changed completely: instead of "Oh, this is a problem programmers", "Hmm, if I use this model, Nick will be angry! It is better to optimize it". People always saw how their changes affect the frame rate, and as a result we released the game with 30fps.

the — Nick wanders (Nick Waanders)

the

Antihero programming


I just graduated from College, was young and naive. We just switched to the beta version of my first professional project game for the PC late 90's. It was a fascinating rollercoaster ride, as often happens with projects. All the content was ready, and the game looked good. However, we found one problem: not been able to fit in the available memory.

Since most of the memory was busy with the models and textures, we artists try as much as possible to reduce the occupied volume. We reduced the image scale, simplified models and compressed textures. Sometimes the artists were sometimes resisted by all means.

We cut a megabyte at a time, and a few days of insane stress has reached the point where to do nothing was impossible. Even though we cut part of the important content, to free up more memory, we had nothing. Exhausted, we measured the current amount of memory used. He was on half a megabyte more than the maximum allowable!
At this point, one of the most experienced programmers in our team, which has experienced many years of development experience in the "good old days", I decided to take the decision into their own hands. He called me into his office and we began the work which I was at first another exhausting marathon to free memory.

Instead, he opened the file of the source and showed me this string:

the
static char buffer[1024*1024*2];

"See?" — he asked me. And then deleted it with one click. Done!

He probably saw the horror in my eyes, so I explained that he singled out those two megabytes of memory early in the development. In my experience he knew he was almost never possible to meet the desired amount of memory, and that many projects because of this failed. So it always allocates a decent block of memory to free it if necessary.

He resigned from office and announced that he had reduced the amount of memory needed to, and everyone celebrated him as a hero.

As I was then so shocked at the "barbaric" approach, I must admit that now fully support it. Until I came to this way of thinking, which will is able to use it, but I see that being in a difficult situation, it is never too to have some memory for a "rainy day". Interesting how time and experience change our views.

the Noel Llopis (Noel Llopis)
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Automatically create Liquibase migrations for PostgreSQL

Vkontakte sync with address book for iPhone. How it was done

What part of the archived web