This hack week I wanted to demo the use of Mono to program the logic of a game.
This is an area where Mono can be really useful, and we want to increase its adoption even more (we also had a booth at the GDC last year), so a good technical demo can be useful.
First of all I had to select a game to work on. There were a few prerequisites I had:
As you can see, many prerequisites conflict with each other.
One way to reconcile the "real world" thing and the "redistributable" one would be to choose commercial games that have been released as Free Software, like many ID Software ones.
In this sense, any of the Quake 3 derivatives would be really nice. They also are conceived for being "modded", so doing some extension in C# is definitely doable.
Unfortunately, the amount of work required to do (or port) a game mod to C# is (IMHO) too much.
They tend to weight in at 100k lines of code, which is a bit too much for the demo I had in mind.
Also the AI of a single bot (fighter) can easily exceed 20k lines of code, and what's worse is that there's no clear interface for that in the game, so drawing the line between Mono and the rest of the engine would be an additional work.
In the end I selected the TORCS racing simulator.
It is not a commercial game, but its level of realism is adequate for the demo: the drivers must take into account all sort of parameters to race effectively, including basic aerodynamics, ground effect, spoiler angles, tire friction and temperature, engine response, fuel load...
Moreover, there is a clear API to write the AI of drivers as plugins in separated loadable modules.
So, this is what I did: I took the TORCS driver API, wrote a SWIG interface file for it, and used it to produce a C# wrapper. I then selected the best of the TORCS drivers, which seemed to be one of the "berniew" ones by Bernhard Wymann, with the intention to port it to C#. However, it was "too good", with 2700 real lines of fairly complex c++ code (reported by sloccount), so I selected the "next good" driver seeing how well the others raced against berniew. It seemed to me that "inferno" (apparently an evolution of "tanhoj") was a very good one, so I decided to port that: with only 800 lines of code it raced almost as well as berniew.
The porting was easy, and now I have a working TORCS driver implemented in C#.
I then sow a subtle error, similar to a difference of precision in the calculations (but likely just a porting bug), which causes stupid driving mistakes in the bot, I'm going to debug that soon.
The next planned steps were to reimplement the same driver in Lua, and benchmark that comparing it with the C# and C++ implementations, measuring both execution time and memory usage, and also to comment on the "implementation experience" (C# vs Lua).
However, in the process I found out that SWIG, as a tool for interfacing C# with native code, does the job but has serious deficiencies.
One is that it does not work with arrays (at least the version I have, 1.3.35).
Another one is that it produces lots of native code, and invokes it practically for everything, so even a simple field access becomes a call to a native C function (which involves a managed-umnanaged transition and all). I really believe that a wrapper generator tailored specifically for the .NET world could use the CIL much more effectively (the SWIG C# backend is almost a copy of the Java one...).
Finally, SWIG encourages the use of "interface files" distinct from the native C header files of the API it is working on. While this is understandable, I think a more direct approach would be more practical.
My impression is that the Windows crowd does not feel the pain because when they need to interoperate with native code they have managed c++. At first I just dismissed it as a "language abomination", something taking the already complex C++ and adding all sort of quirks and different ways of doing things on top of it. But now, thinking better, it is the perfect tool to interoperate managed and unmanaged code, and in the free software world we lack something with that power.
I am not advocating that we implement managed C++ right away... I'm only saying that we are still using primitive tools to do this kind of job, and quite often we just say that "doing wrappers by hand is the best approach, anyway it's easy", because none of the tools we have is good enough.
But this (and the rest of my activity on TORCS, and Mono inside games) could be the subject of my next blog post :-)