This is part of a series of posts revolving around mod support, the introduction and links to the other posts can be found here.
For our current game, we’ve decided to provide as much mod support as possible. One large part that will contribute to this is providing a scripting language both on the server and client.
So… what exactly is a scripting language? A scripting language is a programming language used to control another application. In the case of games, and depending on how they are implemented, they can be embedded and used to control the game logic layer. There are advantages and disadvantages of embedding a scripting language in your game, these are:
Good: Mod Support
By pushing as much as possible of the game logic into the scripting layer you allow for a mod community to grow around your game. Over the last few years mod support in games has been very lacking and if you provide mod support you’ll be greatly increasing your game’s shelf life and differentiate yourself from the crowd. Modders can become a very loyal and technically helpful addition to your community. Also, who wouldn’t like to see what other people can do with the world you’ve provided them? You make a game because you love the idea and world (hopefully!). Seeing what others can craft with the tools you give them is a great feeling and can even rekindle your original feelings if you’ve unluckily burnt yourself out.
Good: Fast Development Iterations
When developing, if you can do something faster without sacrificing quality – you’ll want to do that. By pushing the game logic out to the scripting layer you’ll be able to reload the scripts without restarting, or recompiling, the game. This is especially useful for developing artificial intelligence and if you’re working with C++. Recompiling your project for every minor edit is exhausting and, after the thousandth time, can send a developer to the edge of insanity and then past the gates of Oblivion.
Bad: Initial Integration
The initial research, selection, embedding and binding of the scripting language to your game core can take a while and is not the easiest thing to do. It may seem like too much effort for developers who are on a very tight schedule and are not experienced enough to get through the process.
Bad: Slower Processing
Any embedded scripting language will almost certainly run slower than the core language you’re running it from. This can eventually have an effect on the end game system requirements but there are steps that can be taken to mitigate the worst cases. Any heavy duty, performance critical code should be run in the core language and then this can be provided to the scripting language as an API call. By doing this, you’re correctly breaking the core logic and the game logic apart and keeping them in their respective places too.
Bad: Exposure of Source
Whilst some scripting languages allow you to pre-compile your scripts to shield your code from prying eyes – you may actually want to share the uncompiled scripts with your community and modders. Your scripts can be the best teaching tool for how to use your scripting API and provide a great start to mods. This matter really depends on how you view your game logic source code and if you’re willing to provide it for the greater good of your game and the modding community, or not.
If you expose a way for the community to mod your game you will always get someone who will try and do malicious things with it. You will need to sandbox your scripting language runtime and / or whitelist libraries to prevent this. It’s very hard to maintain a complete sandbox but in the end you have to try your best. No one likes a rogue mod deleting save files… or worse.
After deciding that we want to use a scripting language for the above reasons, and feel the above disadvantages aren’t too bad, we need to decide on what language to use. There are lots of choices out there for game scripting languages. For a good game scripting language a few prerequisites need to be met.
Since we aim to provide extensive mod support we are developing as much of the game logic in the scripting layer whilst keeping any processing intensive code and generic client / server logic in the core layer. Due to this the scripting layer needs to be as fast as possible. This is a very important point as stacked inefficiencies will eventually bite you.
Ease of Use
We want modders to be able to get up and running creating mods as quickly as possible. For this we need a scripting language that won’t take new users too long to get into so the lowest possible barrier to entry is required. My early days of development was modding newly released Fallout 1 and 2 so I’d like to give people an easier ride than what I had to do with installing compilers and editing huge files of identifiers.
Since we plan for mods to be client and server side then we need to ensure nothing terribly bad can happen when running mods. Crashing games from bad mods can’t usually be avoided, however, preventing unrestricted access to the end user’s computer is very important. Either a sandbox or whitelisting of functionality is a must here.
Power / Extensibility
Game logic can become very complex so we want to use a language that is either powerful enough to do what we want or can be extended to do what we want. There is no reason to handicap ourselves.
Easy to Embed
At the end of the day, Rogue Vector is a two person company and adding mod support is a huge undertaking that can equally provide huge benefits. We need a language that will be fast for us to embed without spending months trying to integrate and bind it.
With the prereqs all agreed on, we need to select which language will be a good fit for us. There are too many scripting languages for me to go into them all, however, they fall into two main categories: dynamically or statically typed. Here are a few languages and the considerations to take into account.
As a rough generalisation, statically typed languages are high level languages that, at compile time, execute extensive checks to ensure type safety and are bound to a data type and an object (or null). Compiler optimisations are also applied in statically typed languages.
Mono is a .NET framework runtime that supports embedding and running of many other languages, even those not from the .NET family such as Java. While Mono is rather large to include in your game it’s surprisingly nimble when it comes to the benchmarks and code execution. In many of the benchmarks it comes out as the fastest scripting system of them all. The main issue is that to use Mono embedded in a commercial product you have to pay a licence fee. This was a deal breaker for us so we stopped investigating it but it has been used amazingly well by Unity and Second Life as their scripting base.
AngelScript is a mature language that is very close to C++ in syntax. It hides and handles pointers and memory automatically so that reduces the complexity, however, for a scripting language to remain so similar to C++ may raise the barrier to entry for modders. On the other side, it allows for developers to swap between the scripting and core language without any worries on having to mentally swap development modes. If you want to have C++ style scripting and your core game is C++ then AngelScript may be your winner here. It has a very clean integration and so that reduces the need for a binding library, which is a big plus. It’s used successfully in games such as SOMA, Amnesia and Overgrowth.
Again as a rough generalisation, dynamically typed languages are high level languages that, at runtime, execute many common programming behaviors that statically typed languages perform during compilation. They are not bound to a data type but only to the object (or null).
Lua is generally seen as the games industry scripting language of choice. It’s very lightweight, fast and very portable. Lua becomes even faster when using the JIT compiler called LuaJIT. While it’s only a procedural language it does have a very flexible data structure that allow for object oriented programming techniques to be used. Extension libraries can bring that support even closer to full object orientation. The language itself has a few things to get used to, such as indexes starting at 1 and not 0 and missing expected keywords like ‘continue’, but it’s not too bad to get used to. All in all, it’s easy to see why Lua is used frequently for embedded game scripting and has been used in games such as World of Warcraft, Freelancer, Garry’s Mod and Natural Selection 2.
Python is another popular procedural language similar to Lua. It’s a lot heavier due to it including all the nuts and bolts that Lua does not. It also runs slower than Lua so this is something to keep in mind. I have an issue with the white space nature of Python but that’s more a personal dislike more than anything else. In the end, Python’s focus is wider than Lua for better or worse. Python has been successfully used for game scripting, like in Civilisation 4 and Battlefield 2, so that speaks for itself.
We decided to back Lua (LuaJIT) for our scripting language of choice. The main reasons are already mentioned above but also we needed a scripting language that is consistent between the server and client. While Unity has no native scripting support, there are assets that help integrate LuaJIT in nicely. Other scripting languages would be harder to integrate into our game client. At the end of the day most of the scripting languages highlighted have been used very successfully so personal preference is also a key deciding factor.
For a more indepth look at some of these languages and a set of benchmarks see the blog post at upvoid.com. A wider set of benchmarks are found here and here. My next post will go into more details of LuaJIT embedding and binding into a C/C++ game core.