< Back

In Praise of the Client Server Architecture

I have recently been thinking about the server/client architecture with regards to designing software. This came about when I was talking to a friend about how eveytime I hear about multiplayer game development, it’s almost unanimous that it’s a good idea to build with multiplayer in mind from the start. Adding multiplayer to games later into development is said to be hard. This lead me to think about games where the multiplayer is well done, vs games where the multiplayer experience is often really poor. Then this lead me to realize I use this style of software everywhere from text editors, to games and even audio backends. Its far more prevelant than I first assumed, but why? First I want to look at what makes a good multiplayer experience because I think it’s just the most obvious first step when talking about server/client architecture in software.

Multiplayer games

So I have claimed that Minecraft has well done multiplayer, but why? Just to avoid confusion, I am only talking about the Java Edition of the game, since I don’t want to think about Microsoft magic in this context to make things more confusing. Minecraft has two parts, no matter what, the server and the client. These two always exist. When starting a standard server, one person must download the server files for the game, run the server.jar using java and bam, you’re server is up. Then everyone else will use a Minecraft client, what we typically call the “game”, and connect to it using a local, public or even loopback ip address. But what is this loopback address. Without going into the weeds, localhost and 127.0.0.1 both point your computer to your computer. But why is this important? We’ll it turns out, because Minecraft is designed with the server/client architecture, the same steps for connecting to a server also happen when playing singleplayer.

When someone pressed play on their world. The Minecraft Client actually starts a seperate process to be the Minecraft server for the client to connect to. Using the computer’s loopback address to find the newly created server. Now you might think “gee wiz, this seems like a lot of work just to play a singleplayer game”. I would like to offer this as a rebuttal. If you have ever hosted a world via LAN, you know how easy it is. Open a signgleplayer world, then pause the game, and select Open to LAN. You don’t have to stop the game, you don’t need to fiddle with menus, and it’s most importantly fast. That’s because the game was designed with multilayer in mind, and since playing in singleplayer already starts a local server, all the game needs to do is allow local ip addresses to connect, not just loopback addresses. You can take this a step further with mods like e4mc or essentials that tunnel your local ip through a public server allowing you to make you’re world publically joinable via a special link without needing a domain name or to configure port forwarding. This is amazing because all of these operations, and the increasing levels of publicity are very quick tasks since they are simple modifications on the games already existing behaviour.

On top of this, the general minecraft multiplayer experience is nice considering lag and poor performance is always the fault of the server, and never the fault of clients. The Binding of Isaac very recently added an online multiplayer mode, very very late into development at this point. The problem being, though I don’t know the spesifics, the game was not designed with multiplayer since the start, and as a result, the experience is very poor. Online games are not bottlenecked by the serverbut instead bottlenecked by the worst connection of the all of the clients connected to the server. This makes no sense at all, leaving players with good connection having the quality of gameplay that only the player with the worst connection. The entire game becomes jittery and unplayably slow, often taking ages to finish. Although I don’t know the spesifics of Super Smash Brothers Ultimate’s multiplayer, they certainly suffer the same effects, where performace tanks for both clients if one has poor connectivity.

More Important Software

Now moving onto other types of software and why the server client architecture is so powerful. Let’s take a theoretical, highly customizable text editor / IDE, which we will call “Tegg” (Text + Egg). This editor has a lot of configuration with extensions, complex config files, and several processes it needs to start. Let’s say for example that we start to see a significant decline in the startup time of Tegg, because of the intensive init routine. One great way to solve this is lazy load features, leaving parts of the previously described init process for until needed, instead of all at once in the beggining. That’s a great solution, but let’s also imagine that this doesn’t entirely solve the issue, since there are too many tasks that cannot be lazily executed. Everytime we open the editor to do something, it takes time, or if we want two instances of the editor, we need to expend double the resources. This can be solved with the server/client architecture, while making the software more flexible and lightweight.

For this, I will use Emacs as an example, since in describing “Tegg”, I was secretly describing Emacs all along. When installing Emacs two binaries become available to the user: emacs and emacsclient. Huh, that’s strange. Surely the existance of an emacsclient implies there exists an emacsserver, no? Yes, it does! Emacs cleverly tucks the emacs server into a flag of the emacs binary, which can be started using emacs --bg-daemon. This allows for the more normal way of starting the program as just emacs, which I’m assuming starts a local emacs server anyways. In doing so, Emacs will launch itself as a background daemon acting as a server, doing all the initial loading of plugins, starting processes and executing the user’s config, until finally waiting for an emacsclient to connect to it. The emacsclient is indistinguashable from the standard binary itself, sans the incredibly swift startuptime. This also opens the door to multiple clients being open at once with nearly no performance cost, clients maintaining state between opening and closing application windows, and far more freedom for use of emacs when, for example, connecting to a running Emacs server remotely. This starting of the Emacs server can be tucked nicely with other necessary system startup like window managers, audio backends and system critical background processes.

This was just a simple look at the client/server architecture model, which I have really come to appreciate recently. There are for more interesting applications that I haven’t mentioned, such as headless instances of popular programs like Firefox and MPV, or language server protocols and debug adapters in the text editing space.