Next: , Previous: , Up: Hacker's guide   [Contents][Index]


3.9 Coding guidelines

3.9.1 Project goals reminder

One of the purposes of Liquid War 6 is to make a cleaner implementation of Liquid War than the previous one, namely Liquid War 5. While the latter has achieved the practical goal of providing a playable implementation of the game, it failed at providing an evolutive platform. Network capabilities where finally added to Liquid War 5, but anyone who played on Internet with someone a few hundreds of milliseconds away would agree that it’s far from being perfect. The main reason for this is that it is really had to hack on Liquid War 5, especially when you are not the core developper. The core developper himself, even knowing all the various hacks in the game, is very quickly lost when trying to implement major changes.

To put it short, Liquid War 5 is a global variable hell, a pile of hacks on top of a quick and dirty implementation. Still, it works.

With Liquid War 6, the idea is to take the time to make something stable, something nice which will enable developpers to implement the cool features, and have fun along the way. Of course, this is only a dream, and in the (hopefully "very") long run, Liquid War 6 will also end up as a big unmaintainable mess, like any real-life program, until then, it should remain hackable.

3.9.2 Common sense

Here are a few guidelines which I think are common sense advice, but they are still worth mentionning:

3.9.3 Unitary tests

Each of the internal libraries in Liquid War has a “test” program associated with it. For instance liquidwar6sys-test is associated to libliquidwar6sys, and its purpose is to test the features of this library.

While it is fairly easy to test out unitary functions which require no peculiar context, testing high-level functions which requires files, graphical and possibly network contexts to exist is obviously harder to achieve. There’s no easy way to draw the line, but the idea is to put in these test executables as many features as possible, to be sure that what is tested in them is rock solid, bullet proof, and that one can safely rely on it and trust that code when running it in a more complex environnement.

These test executables are also very good places to see a library API in action, find code fragments, and make experiments.

3.9.4 Memory allocation

Liquid War 6 provides macros to allocate and free memory. One should use them systematically, except when trying to free something allocated by another library, and in very special cases, mostly concerning low-low level operations which are seldom hacked on.

Usage of macros LW6SYS_MALLOC, LW6SYS_CALLOC and LW6SYS_FREE is straightforward, read any random chunk of code, for instance ./src/lib/sys/sys-test.c to see them in action. They are defined in sys/sys.h.

Once used, these macros will track every single call to malloc and free, and if there’s a difference, it will report it. It will also help you by showing what’s in the non-freed memory area, at which line of code it has been allocated, and when. This is very usefull to track down memory leaks. Of course a debugger could tell you some of these informations, but experience shows than when you encounter a memory bug, it’s very often impossible to reproduce it. So you one wastes time trying to reproduce the bug, whereas with this tool you have the information reported just when the problem happens.

3.9.5 Private and public interfaces

Each library exports a public interface and hides its internal. Since Liquid War 6 uses standard C and no C++, there’s no real standard way to handle public/private features. The convention used in Liquid War 6 is to show internal structures as opaque pointers (void *) whenever some function needs to operate on a structure which has possibly private fields. This way the caller function has no way to access the internals, and we are sure that no reference to any internal implementation specific feature will appear.

Here’s a code excerpt from src/gfx/setup.c:

void _lw6gfx_quit(_LW6GFX_CONTEXT *context) {
  /*
   * Implementation here.
   */
[...]
}

void lw6gfx_quit(void *context) {
  _lw6gfx_quit((_LW6GFX_CONTEXT *) context);
}

The function _lw6gfx_quit (note the “_”) is internal, declared in internal.h whereas the function lw6gfx_quit is public, and is therefore exported in gfx.h.

This way, functions in the program using lw6gfx_quit do not know what is in the _LW6GFX_CONTEXT structure, and they need not know it.

This does not mean it is not possible to have public structures, only these structures must reflect some truely public, accessible and safe to access structures.

3.9.6 Commit policy

Basic rules :

To check that a commit does not break everything, a good practice is to run a make check before committing / submitting anything.

Then, once it’s within the main GIT repository, check the Jenkins builds to see if the program still builds correctly.

3.9.7 Audit the code

Liquid War 6 is regularly audited with automated tools. You might need to pass --enable-gcov to --configure if you want to use thes tools yourself. More precisely:

Those tools certainly don’t garantee the code is perfect, but they do help improving the quality of the program. If you hack, it’s recommended you give them a try.


Next: , Previous: , Up: Hacker's guide   [Contents][Index]