Cowboy Programming

Game Development and General Hacking by the Old West

June 3rd, 2010

1995 Programming on the Sega Saturn

This is a document I wrote in 1995, while working on Neversoft’s first game: Skeleton Warriors.  It was the first game I’d worked on where I did not use 68K Assembly.

The photo shows me at work around that time. The Saturn dev kit (the “Small Box” and the ICE) is on the right.

The State of the game

The following document briefly describes the state of the code for Skeleton Warriors on the Sega Saturn, and also points at some of the many things still to be done…..

The primary purpose of this document is to effectively bring Dan, Ken and James up to speed with the current code, explaining what each module does, and how they interact. It will also give Mick an overview of the sad and sorry state of his code, hopefully prompting him to pull his socks up.

I will also maybe go into a little detail regarding the incorporation of data (the .GOB and .GOB files) into the program, and what we will be doing in the future.

The Development Hardware

The target machine is the Sega Saturn, which has two SH2 Risc microprocessors and one 68000. Currently we only use the Master SH2, the slave SH2 will be used when we get around to figuring out how. The 68000 is used to drive the sound chip, we should not have to write any code for that as we will be using the Sega supplied sound library.

The program is nearly all written in plain C. We use the GNU SH2 compiler to produce the SH2 assembly output. There are a few SH2 modules, mostly limited to pure data. I have not, as yet written anything at all of significance in SH2.

The development system we use is PsyQ. This is not the Sega standard development system, however, it is considered to be the best by everyone who has tried it. The alternative system is SNASM, by Cross Products, who are owned by Sega. Most of the sample code supplied by Sega is intended to run on the SNASM development system, however it is easily convertible to the PsyQ.

The PsyQ system consists of a SCSI interface board that you install on the PC, and a cartridge that plugs into the Saturn, and a cable to connect the two. Source is compiled on the PC and downloaded to the Saturn where it is run. The code can be debugged from the PC.

The link is controlled by a TSR program (PSYBIOS) that handles communications between the machines. It also allows the Saturn to load files from the PC in much the same way it would load them from CD. We use this feature to load the files for each level.

Mick also has a couple of large, and loud boxes in his room. He also has two PCs. The smaller of the two boxes is a E7000PC, which is an SH2 In Circuit Emulator, useful for telling where you program has crashed if the PsyQ debugger does not stop it. Also useful for watching memory writes, but I've not used it very much yet.

The Second of the loud boxes is what is known as a "Small Box" (The original "Large Box" was about the same size as a small fridge). This is essentially a Saturn, with extra interfaces for the E7000 and for the CD emulator. It also has switches on the front to change the country ID, and to switch between PAL and NTSC.

The CD emulator is what the other computer has inside it. A big board which uses the hard drive of the computer to pretend to be a CD drive. You can build a CD image on this and emulate in real time mode to see what the game will look like when it actually gets to CD. This seems to nearly more or less work to some extent, though there are a few problems which I am working with Sega to try and resolve.


Compilation and linking

The overall construction of the final program is controlled by a single makefile: MAKEFILE.MAK. This contains the dependencies and targets for the entire project, including the compilation of the .GOB and .GOV files.

Individual C source (.C) modules are compiled into SH2 (.OBJ) object modules by the program CCSH. This first calls the GNU C preprocessor CPPSH (in the C:\GNUSH2\BIN) then calling CC1SH on this output to produce SH2 assembly code, then finally calling ASSH (in C:\PSYQ) to assemble this into the final object format.

We do not use C++, as I was told it produced prohibitively large object files. However I have not experimented with this, and those // comment would be useful. Feel free to experiment.

The few SH2 assembly language files we us (with .S extension) as simply assembled directly to .OBJ files using ASMSH (not the same as ASSH, it is a more complex macro assembler). At the moment, they are really only used for the simple incorporation of data, and have no machine specific code in there.

The RAM on the Saturn that you can put code in is split into two one megabyte chunks. One at address $06000000 and the other at $00200000. The chunk at $00200000 is used exclusively to hold the graphics for the main character. The program code actually goes at $06010000 (the first $10000 bytes are used for system space, the stack and suchlike.)

The code is position dependent and is complied to run at that specific address ($06010000) and no other.

The .OBJ files are linked together with the PSYLINK program to produce a file MAIN.CPE which is an executable program with a small header that can be downloaded to the Saturn with the RUN program. PSYLINK uses the file TEST.LNK to specify which .OBJ files to include and where to put them.

The Data

The game is split into a number of levels, many of the levels use some of the same data, but mostly it is different from level to level. Each level has all the data for that level brought together in two huge files .GOV and .GOB. (for the mine it's MINE.GOV and MINE.GOB). The GOV file contains a short header, then all the data that need to be in video memory. The .GOB file contains all the data that need to be in RAM.

A level consist of some of the following data files.

.SSQ – A sprite sequencer file

.SBM – A bitmap file, used for the bitmap backgrounds

.MAP- Both maps for the character mapped backgrounds.

.TIL – The tilesets and palettes for the character mapped backgrounds.

.PTH – The data for the path and trigger points.

.TEX – Textures for the path.

.SSQ and .SBM files are produced by Mick's increasingly irrelevant sequencer "SEQ".

.MAP, .TIL, .PTH and .TEX files are produced by Dan's increasing amazing map editor "TULE".

These files are assembled into the appropriate .GOV and .GOB files, using the ASMSH assembler. See files LEVEL.S and LEVEL1.S to see how it is done. Some level specific data is also included in the .GOV file.

The Modules

TEST.S – Nothing really, sets up a few labels.

MAIN.C – The top level of the program. Contains the hardware initialization, the level setup, the level playing code and various other little bits and pieces that should really be put in more appropriate modules. Has a fair amount of garbage in as it is the module easiest to put things in for quick testing. Contains the code for loading from CD or PC fileserver. Contains the Flag for switching the TIMING color bars on and off.

GFXLIB.C – Various routines for accessing the hardware and performing various graphics functions. These were all written pretty much from scratch by Dan and are often very inefficient. If you find yourself using a routine from here a lot, it might be a good idea to take a look at what it is doing and write a faster version incorporated with your code.

However, all the functions work and provide an excellent framework for the initial implementation and testing of things. Pat on the back for Dan, without whom, none of this would be possible.

SMP_PAD.C – Various routines for reading the Saturn joypad, very hardware dependent.

GLOBALS.C – All the global variables and a few general function. The use of global variables is appropriate programming practice. However, it is rather slow to implement global variables in SH2 for various reasons, so I may eventually convert some to these to global structures, if needs be.

Contain the variables describing the state of the MAN and the PATH.

MAN.C – Handles the movement and display of the man (Prince Lightstar, Talyn, Guardian or Grimskull – the character that you control). At present this is mostly logic for moving him around and colliding with the path. Also providing the appropriate animation for each action. A lot of work still remains to be done here.

OB.C – Handles movement and display of the objects in the game, specifically the enemy objects, like Skeleton Warriors and little alien things. This is were the bulk of the gameplay will be programmed in terms of enemy AI and basic movement and triggering. the data structure is still not finalized, specifically the issues of collision and animation are not fully worked out. Lots of work here.

DATA.S – Various tables, at the moment, mainly the animations of the main man characters.

LAYER.C – The scrolling of the parallax backgrounds. Updates the character mapped backgrounds and scroll the bitmaps. Also does the line scroll (the wavy effect) on the fog layer. At the moment the maps for the character mapped layers are stored uncompressed. They need to be compressed in the RLE format I was using on the genesis version. This task may fall to Ken if we get a Saturn dev system before a Sony.

PAL.C – The palette. There are 2048 colors to choose from, any pixel on screen can be any on these colors. I have logically divided the palettes into eight 256 color palettes. PAL.C has code for initialize these, setting them up and some provision for cycling them. They will also need fading and more complex cycling, as well as brightness flashes etc.

BUL.C – Primitive setup for handling bullets (sword blast, hand blast, wrist rockets etc.) as separate objects. Still needs a fair bit for work for more complex use of bullets. Still needs proper collision and animation code.

PAD.C – Simple module to remember the state of the joypad in  a more usable format. Remembers if a button has recently been pressed, and if it is pressed now.

START.C – One line to say what the first level is, for ease of changing it with a batch file.

PANEL.C – Some simple routines for putting up a power bar.

PATH.C – Monstrous routines for drawing the path and also doing collision detection with the path.

MATH.C – Simple Sine, Cosine and Rotate a point by an angle.

[Update] Here’s some sample code from MAN.C. Everything is very hard coded, referring to a global “Man” data structure. Lots of hard coded numbers.

/**************************************************************/
/* Trigger jumping if needed, also variable height jump logic */

Man_JumpTrigger()
{
  if ( Man.JumpFudge )
  {
    Man.JumpFudge--;
  }

  if ( Man.Mode != M_Crouch || Man_StandingRoom() )    // ok if not crouched, or there is headroom
  {
    if (Pad_Jump->Pressed)               /* jump button pressed */
    {
      if ((Man.Contact || (Man.Mode == M_Hang) || Man.JumpFudge) && Pad_Jump->Triggered && !Man.Blocking) /* and not already jumping */
      {
        if (Man.Mode == M_Hang && Pad1.Down.Pressed)
        {
          Man.Contact=0;
          Man.Mode=M_Jump;
          Man.AnimBase = LS_Jumping;    /* Change base anim to jumping */
          Man_TriggerSeq(LS_Jump);    /* start the jumping start anim */
          Man.YV.f = 0x10000;           /* and have no YV */
          Man.Y.i += 4;           /* and have no YV */
        }
        else
        {
          Pad_Jump->Triggered = 0;
          if ( !JetPacCheat )
            Man.YV.f = -0x00080000;     /* Initial jump speed */
          else
            Man.YV.f = -0x00008000;     // Initial speed in Jetpac mode
          Man.Contact = 0;          /* not on the ground any more */
          Man.JumpTime = 0;         /* just started jumping */
          Man.AnimBase = LS_Jumping;    /* Change base anim to jumping */
          Man_TriggerSeq(LS_Jump);    /* start the jumping start anim */
          Man.XV.f+=Man.FlyVel;

          if (Man.HangEnd && Man.Mode == M_Hang)  // if hanging
          {                   // and on the end of a path
            Man.HangEnd = 0;
            Man.X.i += 12*Man.Facing; // the move past end of path
            Man.JumpTime = -3;      // bit more fixed v jump time
          }
          Man.Mode = M_Jump;    /* change mode to jumping */

        }
      }
      else                        /* Already jumping */
      {
        if (Man.JumpTime++ < MaxJumpTime) /* Still in initial jump period */
          Man.YV.f -= 0x0005000;        /* So can maintain jump YV */
      }
    }
    else                      /* jump button not pressed */
    {
      Man.JumpTime = MaxJumpTime+1;     /* so can't alter YV again until landed */
    }

  }

}

The file OB.C grew to a 9000 line monster file, which included all the behavior patterns of the individual objects in the game. Also with a vast amount of hard-coded numbers, like this:

Drop_Arac(S_Ob *pOb)
{
  int t;
  if (pOb->Jump==1)
  {
    pOb->yv.f+=0x7fff;
    pOb->y.f+=pOb->yv.f;
    t=Path_GetYZ(pOb->x.i,pOb->y.i,pOb)-15;
    if ((t>pOb->y.i)&&(ty.i+20))
    {
      pOb->Jump=0;
      pOb->y.i+=15;
      Turn_Around(pOb);
      pOb->SeqFile=Sprites[SpriteMap[34]];
      Object_TriggerSeq(Arac_JumpLand,pOb);
    }
  }
  else
  {
    if (pOb->Frame==16)
      pOb->Jump=1;
    if (pOb->AnimStat==AnimDone)
    {
      pOb->t1=0;
      pOb->Mode=&Pattern_Arac;
    }
  }
  Command_Arac(pOb);
}

Nasty stuff. This style of code was appropriate when games were very small, and grew out of the 68K days.

May 28th, 2010

Customizing Home Page Bookmarks on iPhone and iPad

Sometimes you want to set a bookmark on the home page that has arbitrary text (like some search parameters) in the URL, but navigating to that URL forwards you to another URL that strips off the parameters. On the iPhone and iPad you can edit bookmarks after you create them, but if you make you bookmark by “Add to Home Screen”, then there’s no way of editing in the parameters.

Here’s an easy way to fix this:

1) Go to the site, and zoom the screen so you’ll end up with a nice icon

2) Exit Safari, go to Settings->General->Network->WiFi, then tap the blue arrow next to your WiFi network name

3) Under HTTP Proxy, set it to manual, and change the server to a random .com, like  dhfudsnfjuefsdajf.com

4) Back in safari, enter the custom URL.  You’ll get an error, but the URL will be unchanged.

5) Tap the “+” then “Add to Home Screen”

6) back into the settings, and undo the proxy changes.

May 20th, 2009

AURemoteIOServer Error getting default device UID: ‘!obj’

Normally when  get an novel error message I just look it up on the internet, and get some clue as to the solution.  But this was a first, no clue at all.  Even the “AURemoteIOServer” hardly showed up at all.  So I though I’d share my solution, to help the three other people who have similar problems.

Here’s the deal.  Programming on the iPhone using the simulator on a Mac Mini.  I buy a meaty Mac Pro, which compiles incredibly fast, migrate everything over, but when I run code with OpenAL in it, it crashes with the above error and an unclear call stack.

Solution:  Went into System Preferences -> Sound -> Output, and saw the device was set to iPhoneSimulatorAudioDevice, I changed this to “Internal Speakers”, rebooted, and all was well.

(actually, changed it to Digital Out first, but that was no crash, yet silent, so then changed to Internal Speakers after reboot).

What caused this?  Who knows? I suspect it was something to do with XCode not being migrated, possibly because my OSX was 10.5.7 on the Mini and 10.5.6 on the Pro.  Anyway, it was an obscure bug, and the lesson learned was that you have to dig around related things somtimes, especially in a loose system like the emulator – which is not really emulating, just being a narrow window on the Mac’s hardware, hence is calling the Mac’s sound code.

March 20th, 2009

iPhone OpenAL Linking Problem

If you are having linker problems like:
ld warning: in /Library/Frameworks//OpenAL.framework/OpenAL, file is not of required architecture
Undefined symbols:
“_alSourcePlay”, referenced from:
SoundEngineEffect::Start()      in SoundEngine.o
SoundEngineEffect::PlaybackProc(void*)   in SoundEngine.o

Then the problem is that you have an OpenAL framework in /Library/Frameworks and XCode is looking there first.

The simple solution is to delete or rename it.  But that’s not very useful if you need it for something else, or you want you code to compile for other people.   So the correct solution is to change the Framework Search Path.

So, in Project -> Edit Project Settings  select “All Configurations” and “All Settings”, then under Search Paths -> Framework Search Paths,  add:
$SDKROOT/System/Library/Frameworks/
This will show up in the options as something like iphoneos2.2/System/Library/Frameworks/ (depending on the project setting, and at compile time will automatically switch to the correct framework for the target architecture (simulator or device) and OS version.

December 29th, 2008

Some Uses of SQL Databases in Game Development

RELATIONAL DATABASES

Relational Databases are sometimes viewed as being in the domain of business applications and web development. You would use a relational database for boring applications such as inventory, accounting, or implementing a shopping cart system for a commercial web site. Databases can be viewed by game-programmers as old-fashioned, large, slow, and replete with imponderable terminology such as “inner joins”, “foreign key” and “tuple”. However, modern databases are actually fast, easy to use, and can be quite useful in various stages of game development. While relational databases can be immensely complex and powerful, their robustness and ubiquity means they can also be useful for relatively simple tasks. In this article I’ll attempt to de-mystify databases a little, show how they can easily be incorporated into development code, and discuss a few potential usages.

WHAT IS IT?

A relational database is simply a set of tables with named columns where each row is an individual record. Game programmers essentially use many forms of relation databases at run-time to store and organize things like game objects and various resources. Those databases are generally rather ad-hoc, and tuned for a specific purpose and operating environment, and are implemented using custom code. While technically this is a database, the programmer would probably not refer to it as such. The usages of relational databases I discuss below are not intended to replace these custom in-game “databases”, but rather to add new functionality to be used during the development process.

While the examples I give are all for Windows based development, the nature of communication with a database server is basically text based, so a minimum amount of work would be required to implement similar functionality on console platforms.

SETUP

Databases are run by a server, so the first thing you need to do is set up a server. The server can be local, in that it’s part of your code, and you access it directly via a relatively low level API. Alternatively the server can be remote, and you access it via a network connection. This distinction can be blurred a little as you can have a server on your local machine, accessed via the network (using localhost) – but that’s still essentially a remote server, just somewhat quicker. Here we’ll be discussing remote servers.

There are several ways of setting up a database, and what you settle on will vary with your needs and situation. If you have developers in various locations, then you might benefit from having your database hosted by a third party, as this should ensure everyone has sufficiently fast access. If your developers are all on the same network, then you’d more typically have the databases hosted on the network server. If you are a lone developer, then you’d be more likely to have the database on your local machine, to take advantage of the additional speed.

Setting up a database is very easy. If you have remote hosting you are often supplied with a web interface such as phpMyAdmin that allows you to create databases and users. On a local network your network server will often already have some database server software installed, and you can just add a database to that. Lacking this, you can very simply install a database by downloading and installing the MySQL software, which takes only a few minutes to get up and running.

Once you have a database up and running, it’s very important that you have some way of testing your connection and the database, so you can more easily debug problems with your code. A useful tool here is HeidiSQL, a free program that let’s you connect to your database server, and setup, examine and modify databases in a visual manner. There is also more fully featured software such as PremiumSoft’s NaviCat, which performs similar functions. In the examples below I give the SQL query definitions for the database tables. While it’s quite possible to set these up using a command line tool or web interface, it’s generally easier to use a tool like NaviCat, as it allows you to more easily adjust individual parameters in your tables.

A database server can have various users. If you are just doing some initial experimentation, then you can just log in as the “root” user that you set up when you installed the server. However, as you expand the usage of the databases, then you will want to add additional users with fewer privileges to prevent inadvertent modifications to the database.

Once this is set up, you can now connect to your database from your code. The simplest way (from game code) is to use the C API. The code to connect is shown in listing 1. The SERVER_NAME would be the URL or ip address of your server, or “localhost” if it’s on your local machine. This setup needs only be done once when your program runs, and the other examples assume this has already been done, and there is a valid value in the “handle” variable. Error checking is omitted for clarity, but is something you will need to add, especially if connecting over the internet.

LISTING 1 – Code to connect to a database

#define SERVER_NAME “localhost”
#define DB_USER “user_name”
#define DB_USERPASS “password”
#define DB_NAME “db1″
MYSQL *handle=NULL;
handle = mysql_init(NULL);
mysql_real_connect(handle,SERVER_NAME,
DB_USER,DB_USERPASS,DB_NAME,0,NULL,0);

UNIQUE ASSET IDS

A useful example of using a database during development is for the generation of asset IDs. I touched on this briefly in my article “Practical Hash IDs” (Game Developer, December 2005). The idea is that for the purpose of efficiency (both speed and space) it’s best to refer to assets using a unique 32 bit ID. In the previous article, I suggested using 32 bit CRCs for the ID. That approach has a number of advantages, but there is still the problem with collisions, and if you are going to use databases in a broader manner it makes more sense to generate the IDs using the database.

The simplest way to do this is to have a table that consists of an ID and an asset name (as a string). The ID field will be an integer, and set to autoincrement. (See Listing 2) Then whenever you add an asset name to the table, a unique ID will automatically be generated. To find the 32-bit ID number of any string, we simply look to see if it’s in the database, and if not, we add it. Then we just query the database for this string. See Listing 3 for a function that implements this.

Listing 2 – SQL that defines the simple table of IDs

CREATE TABLE `table1` (
`id` int(11) NOT NULL auto_increment,
`name` text,
PRIMARY KEY (`id`)
)

Listing 3 – Get a unique ID for a string identifier

uint32 GetID(const char *name)
{
char select_query[1024];
char add_query[1024];
MYSQL_RES *result=NULL;
MYSQL_ROW row;
uint32 id = -1;
sprintf(select_query,“SELECT * FROM table1 WHERE name=’%s’”,name);
if (!mysql_query(handle,select_query)) {
result = mysql_use_result(handle);
row = mysql_fetch_row(result);
if (!row) {
sprintf(add_query,“INSERT INTO `table1` (`id`,`name`) VALUES (NULL,’%s’)”,name);
mysql_query(handle,add_query);
mysql_query(handle,select_query);
result = mysql_use_result(handle);
row = mysql_fetch_row(result);
}
id = atoi(row[0]);
mysql_free_result(result);
}
return id;
}

These IDs are typically baked into the data as part of the build process, but can also be used directly in the code in the exact same way as was outlined in “Practical Hash IDs”. Note here that what we are doing is not a run-time process. The database is only intended to be used during game development, for the initial creations of ID by the team. If you are using CRCs, then it’s quite easy to modify this code to check for collisions.

TRACKING ASSERTS

A common issue during game development is what to do about asserts and warnings in the code. Warnings are often ignored by non-technical staff, and manual solutions such as “when you see this warning, come and tell me” are not very reliable. The line between asserts and warnings is often blurred in order to facilitate uninterrupted development. Asserts (which should indicate some fatal error which requires immediate attention) sometime have an “ignore” option, which gets switched on by the creative staff, who don’t care so much about tracking down your bugs. In a development environment with a large number of people, there could be a lot of asserts or warnings being fired off by your code, and it swiftly becomes very difficult to separate signal from noise, and to get the information to the correct person. Clearly some automated system would be useful.

Here we can easily use a simple database table to track these things. Listing 4 and 5 show a simple implementation of this.

Listing 4 – SQL Table for storing asserts

CREATE TABLE `asserts` (
`assert` text,
`message` text,
`file` text,
`line` int(11) default NULL,
`machine` text,
`time` timestamp NULL default CURRENT_TIMESTAMP
)

<<LISTING END>>

Listing 5 – Assert replacements that log asserts to a database table

char assert_buffer[1024];

void assert_printf( const char* text, … )

{

va_list a;

va_start(a, text );

vsprintf( assert_buffer,text,a);

va_end(a);

}

void SQLAssert(const char *assert, const char *file, int line)

{

printf (“%s, %s, %d\n”,assert_buffer, file, line);

char query[2048];

sprintf(query,”INSERT INTO `asserts` (`assert`,`message`,`file`,`line`,`machine`) VALUES (‘%s’,'%s’,'%s’,'%d’,'%s’)”,

assert,assert_buffer,file,line,GetMachineName());

mysql_query(handle,query);

if (0 /*don’t ignore*/ ) __asm int 3

}

#define NewAssert( test)\

if( !(test)) { \

assert_buffer[0]=0 ; \

SQLAssert(#test,__FILE__,__LINE__); \

}

#define NewAssertM( test, params )\

if( !(test)) { \

assert_printf params ; \

SQLAssert(#test,__FILE__,__LINE__); \

}

// Usage, note extra parentheses:

// NewAssertM(p==NULL,(“p not NULL (%p)”,p));

// NewAssert(p==NULL);

<<LISTING END>>

The macro NewAssert is a drop-in replacement for the standard assert() macro. The only parameter is a test that must return true. If it returns false, then the macro calls SQLAssert with #test (a string containing the actual test code), and the standard file and line numbers. SQLAssert then formats a string that will add these to the database.

In addition there is a more sophisticated assert macro, NewAssertM, that takes an additional parameter which is actually a list of parameters enclosed in parentheses. These are passed to the assert_printf() function which treats it as a sprintf into the assert_buffer. The assert_buffer is then passed to the database. This allows you to add an arbitrary string to the assert info in the database, usually this would contain the values of various variables involved in whatever you are testing. See the example usage at the end of listing 5.

So what we have now is an assert macro (or a warning macro) that you can track every single instance where it fires, and no longer have to rely on the artists and level designers (or even the testers) to accurately report what is going on. You can even leave it in for beta versions, and gather a large amount of data from a geographically diverse set.

The example shown includes a field for “machine”, which is intended to hold the machine name. Using this you can identify if a particular warning is going off a lot for one particular user. You could quite easily extend the assert logging to hold any additional information that might be useful, such as the IP address, or the current level name. Since this is a standard database, it’s very easy to query, extract reports into spreadsheets, and even generate graphs and web-pages from the information in the database. Interesting metrics can be generated, such as which source files trigger the most assertions, or even what day of the week has the highest rate of problems.

The 'time’ field in the table is set to “default CURRENT_TIMESTAMP”, which means that whenever the assert fires, this field is set to the server’s current date and time. This can be very useful in tracking down bugs, as you can see when an error or warning first occurred, and attempt to correlate that with whatever was changed around that time. This can be useful for prioritizing things. If a particular assert has been triggering for several days (or weeks) and nothing is being done about it, then it might be that it needs to be downgraded to a warning, or an informational message (or you might need to fire someone.) This kind of high level overview of issues can be useful when there are a large number of developers on a team.

OTHER USES

The two examples above make very little usage of the vast power of a relational database server. The data structures are essentially flat, and there is nothing “relational” about them. Normally a database would have multiple tables, cross indexed with each other to avoid data duplication. However, there is nothing wrong with using a database in this simple manner. It might seem like overkill to use it for logging – which you might do to a CSV text file – but it does not cost us anything to do it like this, and you immediately get the benefits of multiple remote connections to a robust repository, and very sophisticated filtering and report generation. The fact that we are barely using any of the features of the database is beside the point.

With that in mind, there might be other obvious areas where a database could be used instead of plain logging. User input could be logged if your database is fast enough. Many gameplay metrics could be logged in early test versions, such as how long it takes to complete each goal in each level. This data could be collated over thousands of runs, and used to fine-tune the difficulty level of a game. Remember that once you have the basic database set up and the connection software in place, it is very easy to add arbitrary new tables, and to start recording whatever you like.

Resources:

MySQL – A powerful open source database server. dev.mysql.com

Paul DuBois – Writing MySQL Programs Using C, MySQL, Third Edition, Sample Chapter: http://www.kitebird.com/mysql-book/ch06-3ed.pdf