2011-05-11 by Cheetah
So … as promised in my last post, I’m going to start a series of “how to build your game application server”. The first step is to figure out, in general what you want to make. Then, the next step is to get a sense of your architecture. How are your components going to fit together and communicate with each-other? What interpreted language will you use, and what underlying “lower level” language will you use to support it
For this series of examples, I’m going to take you through building a multiplayer server which would be suitable for running a MUD/MUCK or a multi-player tile based game like EUO or Shattered Moon’s Ultima IV multiplayer.
In truth, the interpreted language and the “lower level” language you choose is largely preference. There are some more suited to the task than others — Lua and Squirrel were mentioned last time as two languages specifically designed to be embedded. Python is another choice, and has the benefit of being a full fledged language on its own but still easily integratable. It’s also down the down side of being a full fledged language on its own; it will tend to be more resource hungry and “slower” than some of the other options.
The underlying language must support your “higher level” language and it must offer high performance as well. The whole reason we’re bothering with this architecture is to combine the ease of programming in a script language with the speed of a more difficult to use language like C or C++. Think of the high performance code as “critical sections” that will be called by your script layer to do things like render graphics and interact with hardware. Good threading support is also important.
So therefore, you may be restricted to C or C++ for the foundations of the application server. Java also has good support for embedded languages (See Jepp, Kahlua, and Mochalua among many others) but it probably won’t have the hardware level performance that you want. Some of the readers of this site and fellow contributors may know even more language options, as I admit my palette is somewhat small in this regard!
For my examples, I’m going to use C and Python. Why C? Because I know it inside and out, it’s high performance, and it integrates easily with Python. Why Python? Because it comes out of the box powerful with many features that you may have to implement on your own in a leaner language. Plus, I don’t think the performance hit is that bad; anything that needs to be speedy we can offload to C, anything that needs to be tweakable can be offloaded to Python. And ultimately, it’s also a heaping helping of personal preference.
Now that we have that settled, let’s draw a little diagram of how this will work:
Please try to forgive my rather ugly MS Paint-built info-graphic! So, basically, only the top box is Python. The rest is C. Each box may optionally be a thread with the lines then being thread-safe queues; if threads are not used, then the lines are merely function calls. Let’s go into that in more detail.
Threads are great things when you use them properly, and they’re nightmares when you use them incorrectly. True of many things, but especially true of threads because they are a very easy way to lock up your program without being able to determine why or how very easily. In games, threads are often avoided; they’re becoming more and more common, but generally speaking in a single player game you can go pretty far without having to use threads. However, once you start building in multi-player or super-heavy graphics or any number of other similar features, you’ll start having issues where parts of your game can block other parts.
That’s where threads come in. Consider the places where your game may get blocked or may need time to wait that should not block the rest of the process, and spin those off as threads. For a multiplayer server, it’s pretty important that the network (the I/O box) be it’s own thread. Also my “bridge” and interpreted language will be, together, it’s own thread. Database and system I/O I will leave as function calls; for this sort of game, I’m not expecting a lot of blocking time there.
If you were making a single player game, you could avoid using threads altogether. In fact, even in this example, threads could be largely avoided. But that actually makes it harder to program in some ways — hopefully I can guide you through it okay!
One more thing to point out; what, exactly, is the “bridge” I just mentioned? Most interpreted language integrations (probably all of them!) work based on callbacks. You provide functions that the interpreted language calls when it wants this or that. I refer to the set of callbacks, which are often wrappers for lower level calls, collectively as the language bridge. The bridge also contains and bootstrapping needed to get the language started and running on it’s own thread (if you’re doing that sort of thing).
So that’s an extremely sweeping, high level look at what we’re going to build. Next article, we’ll actually get into some code. Am I going a little fast? You bet! Feel free to ask questions.