Next: Compilation tips, Previous: Designing levels, Up: Top
Liquid War 6 is developped on GNU/Linux and other platforms are not supported yet. However there is no reason that would prevent it from running on other platforms, such as Microsoft Windows, Mac OS/X, FreeBSD and in a general manner any *NIX system.
Liquid War 6 uses libraries which are cross-platform (OpenGL, Guile...) so porting it is merely taking the time to fix makefiles and build tools for the given platform. This does not mean it is immediate, but it's a feasible task.
There's a balance to find between on one side the temptation to link against every possible library just to get access to a little trivial feature, and on the other side the temptation to re-invent the wheel, either because one thinks one can code it better than others, or because one simply ignore the existence of some convenient library.
Liquid War 6 tries to use external libraries when needed. It makes no sense to try and code using X11 calls directly today, unless you are developping something very X11 related, such as a window manager for instance. Therefore Liquid War 6 uses libraries like SDL to access video hardware.
But to only load maps, and because the map loader has no reason to be linked to the final renderer, Liquid War 6 uses libpng directly, without using the SDL_image interface. It might appear akward at first sight, but it makes sense: there should be no need to link against a complete graphical library when the purpose is just to read a simple PNG file. It is true this has some cost it terms of coding, but the advantage is that it is easier to get rid of third party libraries. Freedom has a price.
If look at the liquidwar6
binary you'll find out it's
fairly small, but it is linked against (many) convenience
libraries like libliquidwar6sys
or
libliquidwar6ker
for instance. One side effect
that Makefiles are complex, but experience shows that if
one does not force oneself to actually truely separate
different modules in a program, that program ends up
in a big mess of code. Yes, experience does show this.
This is an important point. From the packager's point
of view, and also from the user of a GNU/Linux
distribution which relies on prebuilt packages,
handling different versions of the same executable binary is
much less convenient than having an identical
core executable binary, which can be enhanced
at will by simply putting a .so
file
in a directory.
Ideally, Liquid War 6 would be a hudge script, without any compilation whatsoever.
However, because of obvious performance issues, and some libraries having C interfaces but no Guile equivalent, a significant amount of C code need be written. But the idea is to provide just enough fast C hardcoded functions to keep most of the game logic in the script world, which means scheme code handled by Guile in the case of Liquid War 6.
We describe here the libraries which are directly
linked against the main liquidwar6
binary.
There are 9 or them:
libliquidwar6sys
: various low-level tools. Depends on no
external library but libc.
libliquidwar6dyn
: a simple wrapper to load/unload shared
libraries dynamically. Was not
put in libliquidwar6sys
because it is linked against ltdl
.
libliquidwar6con
: console related functions. Linked against
readline.
libliquidwar6cfg
: tools to read/write config and ressource
files. Was not glued in libliquidwar6sys
because it is
linked against expat.
libliquidwar6map
: map reading, that is transforming
PNG
and XML
files into a usable in-memory
C structure. Has been separated from libliquidwar6ker
to keep the core algorithm free of any dependency, including
libpng
and libjpeg
.
libliquidwar6ker
: the core algorithm, shortest path finding
and the likes. The only interesting
module after all, the rest is necessary but not fundamental.
Note that this module requires the headers of libliquidwar6map
but it is not linked against it.
libliquidwar6net
: all the network stuff.
Not implemented yet.
libliquidwar6gfx
: all the graphical stuff.
This is where most of the code rely,
which is not very surprising. Note that this module is also
responsible for keyboard and mouse input.
libliquidwar6snd
: all the sound stuff.
Not implemented yet.
Here's some ASCII art to summarize the dependencies.
+-----------------+ | scheme scripts | +-----------------+ | +-------------------+ | liquidwar6 binary | +-------------------+ | ----------------------------------------------- | | | | | | | +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ | gfx | | snd | | net | | con | | cfg | | map | | ker | +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ \ / +-----+ | dyn | +-----+ +-----+ | sys | (used everywhere) +-----+
Not everything is represented. For instance, map
and gfx
both use cfg
directly.
TODO...
(describe the internal convenience libraries of this module)
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.
Here are a few guidelines which I think are common sense advice, but they are still worth mentionning:
strcpy
or sprintf
anywhere in the code,
use their equivalent strncpy
and snprintf
systematically,
as they are part of the glibc and are an order of magnitude safer,
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 much features as possible, to be sure that what is tested in them is rock solid, bullet proof, and that one can safety 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.
The libliquidwar6sys
provides macros to allocate and
free memory. One should use them systematically, except when
trying to free something allocated by another library.
See the documentation for module libliquidwar6sys
for
more information on how to use the macros.
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.