Building incredible multiplayer games since 2012.

Choosing a development stack for World Zombination

Choosing a development stack for World Zombination

This is the first in a series of technical posts about our development choices and process at Proletariat. TL; DR: If you want to write code once and run it on multiple platforms, check out Haxe. We’ll also be releasing many of our internal tools and source code on the Asset Store and GitHub in the coming months and giving detailed examples of how to achieve similar things. We can’t wait to share them with you!

Before we could start building World Zombination, we had to decide what engine to use. Like lots of developers these days, we’re using Unity for our project — it has a great workflow that allows for quick iteration when trying out new gameplay ideas. There was one problem: we wanted to build an asynchronous massive multiplayer game, and Unity isn’t built for that out-of-the-box.

image

One of the difficulties with any multiplayer game is making the game secure; that is, protected from hackers, cheaters, and other ne’er-do-wells. One of the best ways is to make the game server-authoritative. This means that the servers are in charge of performing all the game logic, and the clients — the hardware you actually play on — are merely showing stuff that’s happening on the server. It’s much harder for a hacker to trick a server located in a protected datacenter on the other side of the country to do something shady than it is to trick the device they’re holding in their hands.

image

Sadly, due to pesky Internet latency, it could take a few seconds for data to get back and forth between the server and your device. Imagine tapping on the screen in a game and waiting a few seconds for a response! That clearly won’t work for most games. To get around this, multiplayer game clients will run game logic ahead of the server, to give instantaneous feedback to the player. The server essentially just plays catch-up with the game clients and verifies the client is doing the right thing. If the client and server disagree, the server can log the error for later review or, in more extreme cases, log the player out before any real damage is done.

One typically writes Unity games in C#, but the Mono C# runtime does not perform well in server environments. What many web and mobile games have done in the past is write the gameplay logic twice — once in a programming language appropriate for the server (like Java, Javascript, or PHP), and once in a language appropriate for the client (like C#, Objective C, or ActionScript). Obviously, this is not ideal since it means writing twice the amount of code. Even worse, since the server is authoritative, the client needs to do exactly the same thing the server will do. Otherwise, the server might incorrectly decide that player is doing something dastardly and log them out. Even tiny differences in the game logic on the client and server can cause huge headaches for engineers and, more importantly, our players!

image

Which leads us to how we are writing our code once and running it on both the client and the server: the Haxe programming language. Haxe has been around for a while now, but is still not very well-known, which is a shame. It’s an open-source programming language and compiler where the central idea is that you can write Haxe code once and compile it to several other languages and platforms.

We’ve used Haxe in one of our previous games, Letter Rush. We used NME (now OpenFL), which is a great 2D game engine that allows you to write Haxe code that runs on several different platforms: Windows, OSX, iOS, Android, Flash, HTML 5, and many more. We were very happy with how that turned out, and OpenFL is definitely worth checking out if you’re writing a 2D game for multiple platforms.

image

Since we wanted to make World Zombination as a 3D game in Unity, we needed a different solution. Fortunately, Haxe also generates C# code! Now, we write virtually all our code in Haxe, and then compile it to both C# for the client and Javascript for the server (running node.js — more on this later…).

image

Aside from the huge advantage of having the same code run in both places, using Haxe has plenty of other benefits:

  • Open-source: The Haxe compiler and runtime are all open-source with an active and friendly development community. There are tons of libraries available for things like physics, math, file formats, and unit testing.
  • Performance: The C# code generated by Haxe can actually be faster than hand-written C# code! How is this possible, you may ask? First of all, Haxe can inline functions, so small routines that are called often can be inserted into the calling function directly. This avoids the (considerable) function-call overhead that the Mono runtime incurs. This alone gives us a 20% framerate boost in our code running on iOS! Furthermore, the Haxe runtime includes typed container classes that are often faster than their .NET counterparts.
  • Ease-of-use: The Haxe language includes state-of-the-art features like type inference, array comprehensions, and anonymous objects that make it easy to write code that is safer, more readable, and does more in fewer lines.
  • Extensibility: One of the more advanced features of Haxe is its macro system, which actually allows you to extend the language with Haxe code itself! This allows you to do things like write shaders in Haxe or even make a high-level scripting language for designers to use that is compiled just like regular Haxe code.

That said, Haxe is not without its areas for improvement:

  • Documentation: This has recently improved, but there is still much work left to do. While the syntax should be familiar for those used to C#, ActionScript, or Java, there are plenty of unique parts to the language that take some time to learn. Fortunately, the community forums are helpful and friendly to newcomers.
  • Debugging: Haxe generates code in other languages, which means that it can be tricky or impossible to debug using the original source code. The Haxe-generated code is not always easy to follow, which can make debugging painful. We’ll be doing a post on making this easier in the future.
  • Unity interaction: Getting Unity and Haxe to communicate is no easy task. First of all, Haxe doesn’t know about the Unity and .NET APIs, so getting Haxe to talk to Unity (or any third-party package from the Asset Store) requires some work. Secondly, getting the Haxe-generated C# code to play nice with the Unity editor is a challenge.

In a future post, I’ll talk more about how we’ve overcome some of these limitations, and introduce our open-source HUGS library, which allows Haxe to talk to Unity directly!

Dan Ogles, CTO

On Twitter