Following the 0.0.1 release of Freekick and an update to 0.0.2, I started noticing some problems in the implementation that showed a need for code refactoring. There were certain issues that implied I should find out about the internal workings of Haskell, as in, trying to figure out how the language and the implementation I use (Glasgow Haskell Compiler) works in general, especially with regards to threading, scheduling and performance.
The first issue I ran into was about my physics engine. I had defined a maximum velocity for my players of 8 m/s, but after some measurements I found out they were instead quite a bit slower. (A tip for the future: implement a debug panel for having such information when you need it. Measuring something like that shouldn’t be necessary.) This implies that the physics engine has serious flaws. I improved the engine slightly and it works slightly better now (but still not as well as it should), but the improvements lead not only to faster players, but also to strange twitching, a flicker-kind of effect on my SDL client.
To figure out what causes the flicker, I needed to check the code for networking. The way my networking code works at the moment is that the server (physics engine) only sends the positions of the players to the clients but no velocity or acceleration, and my SDL client doesn’t calculate the velocity of the player but only places the player where the server tells the client to. This is the simple method used in Quake that lead to problems back in 1996 when 56k baud modems were fast: with a lag of 200 milliseconds and occasional packet loss the client would spend most of the time not knowing where the moving objects (a.k.a. targets) were exactly located. The remedy that id came up with (a technique that was also used in Duke Nukem 3D) was brought to life in QuakeWorld, where the client would “assume” where the players would be located in based on their current/last known position and velocity. This made Quake playable (and a huge online success) even with slower connections (at the time I had a mere 14.4k modem). The technique is called client side prediction and there’s a small section written about it in Wikipedia.
Now, since I don’t expect a crowd of modem using people starting to play Freekick over the Internet, I didn’t implement client side prediction. Since I have the Freekick server and client running on the same computer lag shouldn’t be the cause for twitching anyways. But to make sure the client was receiving updates about the match state fast enough, I raised the server refresh rate, that is, the frequency with which the physics engine sends the match data to the client(s). The delay between refreshes was 40 milliseconds (using threadDelay), which should actually be enough, but I dropped it to 20 milliseconds for testing. Now, with the lightweight threads that Haskell offers (using forkIO) the refresh rate went up indeed, but the flicker stayed. I tried compiling with the “-threaded” parameter, which made the situation only worse, as the minimum thread delay time using “threadDelay” went up to 40 milliseconds on my computer. In the end I stayed with the lightweight threads, which doesn’t really affect my single core anyways, but this points out a problem when optimising the engine for multiple cores.
However, the problem persisted. Since my CPU was still nowhere near 100%, I figured the problem has something to do with how Haskell handles high precision timing. I’ve tried Frag (a 3D shooter written in Haskell) on my computer, and while I was overall impressed by the game, it was hard not to notice how Frag was twitching with regular intervals as well. As I don’t know if there exists a remedy for the problem, nor if the cause indeed is something Haskell specific or just some obscure bug in SDL or in my client, I wanted to go for a completely different approach.
That’s where C++ comes into play. I’ve actually had quite negative opinions about object oriented programming as well as C/C++ in the past, but on the other hand there are some rather impressive and useful libraries available where bindings to Haskell don’t exist and can only be implemented with a lot of work. The library that I thought of first was OGRE, the massively powerful 3D graphics library written in C++. There are actually a couple of articles about calling C++ functions from Haskell (see here and here), but binding a library like OGRE to Haskell using the described methods is not really something I’d like to spend my free time with.
I know that using OGRE for the client side would
a) make the client run faster and with no flicker, and
b) maybe even look a bit nicer
than the current client implementation using SDL, Haskell and 2D pixel-by-pixel drawing methods. The middle way would be to use the apparently very well written Haskell bindings to OpenGL, but I didn’t really enjoy OpenGL very much the last time I used it five years ago and after seeing OGRE in action and realising using OGRE is actually easier than using OpenGL I don’t really have the motivation. Of course, the biggest reason to use OpenGL would be that I could keep on writing the client in Haskell, but in the end it’s probably easier and more effective using C++ and OGRE.
That being decided, the next thing to do was to freshen up my C++ knowledge. I actually never had much of formal education to C++, instead I was using it much like “C with classes”. Even though that was the primary objective of C++ in the beginning, modern C++ is a completely different subject that I somehow had failed to miss until now. So I went and got me the book The C++ Programming Language by Bjarne Stroustrup and studied it through. I can imagine that a lot of people who have no or very little experience with C or object oriented programming find the book confusing, but for me as someone with good knowledge in C and some idea what C++ is about the book was perfect: it explained all the ways C++ can be used to make programming more efficient and less error prone.
I think it’s time for a small analysis about programming languages. After using C and assembly language I’ve gotten a pretty good picture about what the lower-level job of compilers is: shuffling around with the registers, allocating and keeping track of memory, testing for bits and so forth. Languages like C build on top of this, making the whole resource and control flow management easier to write and read.
On the other hand, Haskell approaches the problem from the other side. Haskell is designed from a mathematical point of view while trying to improve the efficiency of the programmer without clinging to the requirements and constraints of the machine, making Haskell code elegant and robust. To me, writing code in Haskell is almost like using a perfect programming language. Between C and Haskell, there’s C++. My impression about C++ was originally (unsurprisingly) that it is not far away from C, but after seeing how C++ improves on C and adds language features that allow the use of completely new programming paradigms (to C), C++ seems more and more Haskell-like to me.
With Haskell I learned to love the fundamental functional programming features that first seemed a little strange to me (like currying, higher order functions, lambdas etc.). I thought with C++ I’d be giving them away in exchange for pointer arithmetic, buffer overflows and unsafe typing, but fortunately I couldn’t have been more wrong. Nearly all of the C features that don’t seem to fit to modern software development are completely unnecessary in C++, and a lot of functional concepts and other features I’d miss from Haskell are brought in either as built-in C++ features (e.g. safer typing, better mutability control), the standard library (lists, currying, higher order functions) and Boost libraries (smart pointers for memory management, lambdas, serialization).
If you check out the Programming Language Shootout, you’ll see that C++ is (as expected) usually faster than Haskell (but the difference is not tremendous). As is also very well known, there exist about ten thousand times more libraries for C++ than for Haskell. In my opinion, those are pretty much all the aspects where C++ is “better” than Haskell. On the other hand, Haskell code is easier to reason about, significantly shorter and therefore easier to read (in my opinion, at the very least) and the concept of type classes is something C++ and the templates still have a lot to learn from, making generic programming far easier and simpler in Haskell than in C++. However, when writing a client for Freekick, performance as well as external libraries do play an important role in the end, and therefore I’ll go for C++. I’m glad I’ve seen Haskell as the finest example on how elegant code can be written, and I’m also glad it’s easy to use the same concepts in C++.
The next thing I’m going to do is read some more about object oriented design (I’ve got Design Patterns waiting for me) and then go and implement a simple Freekick client using OGRE. For that I need to reimplement the Freekick library in C++ as well, which is a nice exercise for OO design (you should plan to throw the first one away anyway, right?). Having the library in C++ would in theory also make it possible to rewrite the server part in C++, in which case I could use a top notch physics library like Bullet as well. But first we’ll see how the client turns out…