A few weeks ago I wrote I’d try and write a new Freekick client using OGRE3D and C++. The reasons for this seemingly dramatic language switch from Haskell were manifold; the top one being the jittery feeling in the old SDL client, apparently resulting from some timing issues regarding my thread management in Haskell I wasn’t able to sort out. Another thing that greatly influenced the decision was that I had split the whole project in multiple processes already during the design; since the processes communicate with each other via sockets, reimplementing one part (even in a different programming language) is relatively easy.
The first version of the OGRE client is now finished, and it has about the same functionality as the very first version of the SDL client: the user is doomed to be a spectator as there is no way to control a player, the only controls available are merely for moving the camera around. I personally found it very interesting to see the functionality of both the server and the AI through a different client; of course the rest of the functionality has stayed the same and the AI is as stupid as usual, but now it’s robots instead of red and blue squares kicking the ball.
Oh, here are some screenshots of the new client. Obviously, there a lot to be done (more than the pictures can describe), but it’s a start.
Another thing I find rather exciting is how easy it is to produce software these days that has features I could only dream about five years ago. Back then, programming multi-threaded networking or 3D scenes with custom meshes and lighting (including shadows) was a nightmare (for me at least), but nowadays (especially with the aid of such terrific libraries as Boost or OGRE) I can spend more time programming the essential things.
A few words on the design of the new OGRE client. I wanted it to have three distinct parts which communicate with each other via simple interfaces (true to Rule of Modularity) – networking, graphics, and match state. The basic idea is that the networking part receives information about the match state from the server and writes it in the state part. The graphics part renders the scene and reads the information for this from the state part, so the state acts as a kind of broker between networking and graphics. As only one part writes and the other one reads the mutable, shared data there shouldn’t be big problems arising with multi-threading, either (I hope).
The question is, then, is the jittery feel from the SDL client gone with the OGRE client? The answer is (to my surprise) no. Last time I wrote about the timing problems in the networking code between the server and the client; specifically, even though the server was set to send the player positions every 20 milliseconds and the client should draw them every 20 milliseconds, the jitter was clearly noticeable. My conclusion at the time was that the client wasn’t updating the graphics fast enough and that I’d need to write a new client in C++ as a remedy.
Now, as the new client is functional enough to show the jitter, I investigated the problem more and, using the Date_time library from Boost I measured the frequency with which the server actually updates the client. It turned out that instead of sending an update every 20 milliseconds, the client receives a scene update only every 250 milliseconds, which obviously is much too slow.
I actually measured the interval between updates before I started writing the new client and came up with the said 20 milliseconds. The difference is, back then I measured the interval on the server side, not on the client side – which means that some obscure bug in either the thread management code or in the network code kept the data on the server before it was actually sent.
I also tried implementing a very primitive client side prediction on the OGRE client as a test, but the interval of 250 milliseconds is still too much for a smooth scene. I also came across some very interesting resources by Valve regarding multiplayer network programming, and while I’m not so sure I’m willing to implement entity interpolation like they did or if doing that would even work so well in a soccer game, the article still gave me some hints what could be done better on the server side.
The last time I wrote I’d consider rewriting the server again using Bullet as the physics engine, and now that I’ve discovered the problems regarding the network code on the server side as well my next task seems pretty clear. I could try to fix the issues in the Haskell server code, but it seems a bit too much at the moment, and I’m already looking forward to getting my hands dirty on Bullet. That means (to the disappointment of the fellow Haskellers) that soon the only part written in Haskell (not counting the older, yet very usable client) will be the AI, which also needs to be improved a lot in the future. I still think Haskell is a great language though and I’ve learnt unbelievably much from it (as Simon Peyton Jones says, “Even if you’re going to go back to C++, you’ll write better C++ if you become familiar with functional programming”), but I’m afraid it’s not the best tool for this job.
For those of you who are interested, I haven’t yet uploaded the code for the new client in any repository. It wouldn’t be suitable having the code on haskell.org, and I don’t know of any other public source code repositories for darcs, which makes me look for other revision control systems. After my experiences with darcs I’m determined to stick with a distributed RCS, and git seems like a good option, but before making the final decision I’ll check out the other options (e.g. Mercurial). Once I have the URL I’ll let you know.