The Lua Tutorial

Forgive the grandiose title; this article should really be entitled "A Quick Tutorial on Integrating Lua as a Simple Scripting Language into a Windows C++ RPG Engine". But that's too long and boring, so I've gone with "The Lua Tutorial" even though there are many far better Lua Tutorials (http://lua-users.org/wiki/TutorialDirectory) available.

Why Lua?

So, for those who haven't been following, I'm working on an RPG, which by necessity has a game engine behind it (see my GameDev blog for more details). The game engine is entitled SENG, and supports single-player party-based RPG play. The engine is Windows only, using DirectX and C++ as the foundations. Part of the philosophy of the SENG engine is that most of the story and level logic is data-driven; that is, run from scripts within the level files. For instance, when your party has a conversation with someone, the conversation is all driven through scripts. My initial work on the SENG engine had a LISP-like scripting engine, except minus the parentheses. It was dirt-easy to create and support in the engine, but the power of the scripts was pretty limited, and the authoring of scripts was cumbersome and error prone.

I set out to look for some alternative, such as beefing up my scripting engine to be more LISP-like, or using XML syntax, but I kind of wanted to get out of the business of authoring scripting languages. Not to mention that having a custom scripting language would be a big pain if anyone but me ever wants to author scripts. After some investigation, I settled on Lua (http://www.lua.org/) as my scripting language.

What is Lua?

Lua is an extension programming language designed to support general procedural programming with data description facilities. It also offers good support for object-oriented programming, functional programming, and data-driven programming. Lua is intended to be used as a powerful, light-weight scripting language for any program that needs one. Lua is implemented as a library, written in clean C (that is, in the common subset of ANSI C and C++).

From my perspective, the winning features of Lua:

Small, easy to integrate.

C-like syntax.

Full featured, robust logic.

As I've done the integration, all of these features have been borne out.

This Tutorial

During this work, I've looked through various documentation and tutorials, all of which have been pretty good. However, I was struck by the absence of a simple tutorial or document, "Here are the basic steps to integrate Lua with your C++ engine". So, that's the point of this tutorial. I'm not going to get into the details of writing Lua scripts at all, nor any advanced usage; see the official documentation for that.

The feature I'm going to demonstrate in this tutorial is simple. The game has a script, stored in a string, that it wants to call to determine whether the player is allowed to unlock a door or not. The script, in turn, calls back into the game to see if the player's Lockpick Skill Level is greater than 7, and returns TRUE if it is, or FALSE otherwise.

Notes: How the script has been loaded into the string is completely irrelevant; in my case they are included in XML files but that doesn't matter for this tutorial. Obviously the example script is very simple (comparing a number to 7), but once you've gone through the tutorial, it should be clear how to incorporate more complex logic, like also allowing the player to unlock the door if he has a key, or has completed some quest. Further, it should also be apparent that the script can be called from the game engine for other actions beyond trying to open a door; my engine does this for conversation, object activation, and quest logic.

How to get Lua and link it with your project

Lua is available at http://www.lua.org/. This tutorial is written using Lua 5.1, the latest version of which is 5.1.5 (as of Feb 17, 2012). There are various binary packages available, however, I recommend that you just download the source code (lua-5.1.5.tar.gz for me). Unpack this using your favorite unpacking utility; I put mine into C:\dev\lua-5.1.5.

Note: Lua 5.2 has also been released, and while all of the principles of this tutorial remain accurate, some of the APIs have changed slightly, so you'll have to make a few tweaks if you use Lua 5.2.

Now, we'll make a project for this tutorial:

Fire up Visual Studio. I'm using Visual Studio 2005; your steps will likely vary somewhat if you're using a different version.

You can turn off ATL and MFC if you want to, though it shouldn't matter.

If you build and run at this point, you should get a Console Application that opens a console window, and then exits.

Next, we need to add the Lua files. Open the project properties, and go to "Configuration Properties"/"C/C++"/General. Add the Lua directories to "Additional Include Directories"; something like:

"C:\dev\lua-5.1.5\etc";"C:\dev\lua-5.1.5\src"

One more annoying step in the project properties. Lua uses the C-library string function calls like strcpy, and Microsoft doesn't like those anymore. The easiest way to deal with this is in the project properties, under "Configuration Properties"/"C/C++"/Preprocessor. Under there, add to "Preprocessor Definitions":

_CRT_SECURE_NO_WARNINGS=1

The easiest way to include the Lua sources are just to add the files. You can fool around with building Lua as an additional library (or even DLL), but I wouldn't bother. To do this, add the Lua source files to your project's "Source Files". There is a list of the files in C:\dev\lua-5.1.5\etc\all.c; you want all of those files except for lua.c.

And, even though we're not going to call into Lua yet, let's include the header files just for kicks. To your luatest.cpp, just under:

This should build cleanly. You can run your Lua-enabled app, which should fire up a console window, do nothing, and then exit. Congratulations!

How to call a Lua script from your game

First, we need a script to call. We'll put this into a fixed string, up near the top of our luatest.cpp file.

char *szLua =
"x = 8 "
"return ( x > 7 ) ";

Astute readers may notice that this script always returns true. Later, we'll replace the line x = 8 with code that calls back into C++. But, walk before you run, grasshopper.

We need to initialize Lua before we call into it; something like this:

lua_State *lState;
lState = luaL_newstate();
luaL_openlibs( lState );

This creates a lua_State object, ready to make Lua calls. You could think of a lua_State as a little virtual Lua computer for use by our program. It maintains its state until destroyed, so we can manipulate the Lua virtual computer in our C++ code as much as we want without having to worry about reinitializing variables each time or whatever.

Be sure to check for errors, because if you make an error in your script, Lua will usually give you a decent error message so you can fix the script. (This is a big advantage over my previous, self-designed scripting language, where any syntax errors had to be painstakingly determined by hand).

So, that's all fine and dandy, but our "real" script still needs the Lua script to be able to call back into C++ code to determine the player's Lockpicking level. Let's do that next.

How to call a C++ function from your Lua script

Now, we'll call a Lua script, which will then call back into C++ code to get the player's Lockpicking level. The script will use this value, passed in from C++, to determine whether or not the player can open the door.

First, let's update our Lua script:

char *szLua =
"x = GetLockpickLevel() "
"return ( x > 7 ) ";

Previously, the script just did x = 8; now we call a function to get the player's Lockpicking level. As you'll see below, this is a C++ function.

Lua C++ (or C) functions always get the lua_State as their one parameter; this is the object that allows us to manipulate or examine the Lua virtual machine. As you can see, we use this state to push the return value onto the Lua stack, via lua_pushinteger. (In this case, I'm returning a fixed value of 6). Lua C++ functions return an integer, simply the number of return values. Since I pushed one return value onto the stack, I return 1.

One last step, we need to tell Lua about the lua_GetLockpickLevel function, so Lua knows what C++ function to call. We do this right after initializing the Lua state:

lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );

Build and run the code, and you should get the message "Door still closed", since the player's Lockpick skill of 6 doesn't allow him to open the door (the Lua script requires greater than 7). Here's the complete listing of luatest.cpp:

So, we called a Lua script, and it called C++. We're done, right? Not so fast! Right now, lua_GetLockpickLevel returns a fixed value of 6. In a real game, we would want to look in some structure or class to get that value. Since all it has access to is the lua_State, how can it get the "real" value? Let's do that next.

How to access C++ objects when Lua calls your C++ function

In the previous section, we called the Lua script to make the check, and the Lua script called back into C++ code to get the player's Lockpicking Skill Level. However, just calling into C++ isn't enough; we also need to have access to the game's C++ objects. In this section, I show how to get access to the game's data from the C++ code that Lua calls.

To frame the problem, we have this Lua script:

char *szLua =
"x = GetLockpickLevel() "
"return ( x > 7 ) ";

And, in C++, we have some game data. For this demo, we'll make the data as simple as possible, though the method I'll describe for getting at the data is universal; we can get any class (or classes) into the Lua-called code. Anyways, a super-simple class that describes the player's Lockpick Level:

You could simply store a pointer to the CGameData in a global (or static) variable. However, unless you are writing the simplest of toy game demos, I advise against it. I don't want to launch into a screed against global variables in C++ here, but suffice to say there are good reasons against using global variables in most cases, both stylistic and practical. I'm sure a Google search can turn up more reading than you'd care to do.

A much better approach is to associate the CGameData pointer with the lua_State; every function called by Lua has access to the lua_State (which basically represents the Lua virtual computer that runs Lua code). Lua even has a nice type for this exact purpose; "light userdata". The functions lua_touserdata and lua_pushlightuserdata support this type, and allow our C++ code to put "light userdata" in and out of the Lua state.

One particular place you could store the CGameData pointer (as "light userdata") is the "Registry". The Registry is a Lua concept for storing data for C code, and is distinct from the Windows Registry or anything else like that. Lookup Registry in the Lua documentation, or the LUA_REGISTRYINDEX constant in the headers. However, I've actually chosen a different mechanism; the Lua Registry is ookay, but there are some issues. If you end up using reentrant scripts (where one Lua script ends up calling another Lua script through C++), or re-using Lua states, it's possible you could have conflicts in the Registry, where you end up overwriting one CGameData pointer with another. Even if you aren't doing that now, I'd go a different way, just to keep my code future-safe.

Instead, I prefer the "context" paradigm, common in C code, particularly pseudo-object-oriented C code. Basically, we'll pass a context object pointer to the Lua script on the stack, and when the Lua script calls a C++ function, it will pass the context back to us unchanged. The context contains any data (or pointers to data) that our C++ function will need.

In the case of this demo, I'll just pass the CGameData pointer as the context. However, you can easily imagine more data in the context; if we extended the demo a little further, we might pass a pointer to a CDoor object to the script, with data about the door the player is trying to open. If we had to call Lua reentrantly, we could use a separate context object, with different data in it, so the C++ functions called from Lua can do their work properly.

OK enough chatter. To make the context concept work, we actually have to change our Lua script slightly; here's the new one:

As you see, the Lua script now takes a context parameter, and passes it on to the GetLockpickLevel function. Variables are loosely typed in Lua, so there is no problem passing light userdata around like that, even though the Lua script doesn't understand the type at all.

In this case, we get the light userdata from the stack (with lua_touserdata) in position 1. Then we push the return value, from pData->m_iLockpickLevel, onto the Lua stack, and return the number of return values; 1 in this case.

However, that is actually a bad implementation. A simple typo in your script will crash your program. I highly recommend that you carefully check for errors, so you can quickly diagnose scripting issues (plus my pet peeve is tutorials that ignore error conditions). Something like this is much better:

Now, astute readers will have noticed that my new Lua script has a function statement that wasn't there before. You can read all about functions in the Lua documentation; in this case I needed to do this so I could pass the context variable (the light userdata) in to Lua. This adds a complication that, from C++ code, I need to first run the script to create the function, and then, as a separate step, call the function. To run the script, after the old call to luaL_loadstring:

Now, to call a function, we first have to get the function and put it onto the Lua stack. Note that in Lua, functions are just another kind of object, so we retrieve the function, based on its name, from the Lua global namespace, like so (note that lua_getfield retrieves the function and puts it on top of the stack):

lua_getfield( lState, LUA_GLOBALSINDEX, "game_door_check" );

Next, we need to put our CGameData pointer on the Lua stack as light userdata. (This will be the context parameter in the Lua script):

CGameData Data( 8 );
lua_pushlightuserdata( lState, (void *) &Data );

Then, we tell Lua to run. Since the stack is configured with the parameter on the top, and the function next, we just tell Lua to run with one parameter, and it is smart enough to do things properly:

iStatus = lua_pcall( lState, 1, 1, 0 );

The Lua documentation for lua_call has a more thorough description of how and why this works.

The remainder of the code, retrieving the return value, remains the same. Here is the complete code listing to this point:

(Sorry, the code is getting pretty long at this point. I do think properly handling errors is important, which adds some verbosity to the code).

Well, at this point, we have fully functional code. Yay! There is one more thing I want to cover next, and that is handling C++ exceptions properly.

How to handle errors in your script-called functions

There is one last important point to consider in integrating Lua into your engine. Since Lua scripts exist somewhere in your game data, it is entirely possible that your scripts will contain some errors; in fact, if you do any significant scripting, it is a virtual certainty. In this case, probably the ideal behavior is to abort the script but keep running the game. You may also want to log the behavior or something for debugging, but the most important thing is that if an end-user hits the script error, she has a chance to keep playing the game.

The code presented in previous installments of this tutorial properly handled basic things like simple syntax errors in a Lua script. However, there is also the case where the Lua script causes some error in C++ code. As an example, in the lua_GetLockpickLevel function (called directly from Lua), I might have something like this:

In the previous section, this would cause the program to abort if the exception is thrown. Not so nice for the player! To try this out, you could change the initialization of CGameData in the main function to something like:

CGameData Data( -1 );

Now, I could just handle this exception after calling Lua (by putting the whole Lua-calling code into a try-catch block), and attempt to continue to run. However, Lua is C-code, with its own error handling mechanism, and firing a C++ exception through that is probably not a great idea. I'm not entirely sure whether this approach would work or not.

Instead, my suggested approach is to handle the exception in the Lua-called C++ function, and translate it into a Lua error. Basically, we enclose the whole function (lua_GetLockpickLevel in the tutorial) in a block like this:

Then, your C++ error is just turned into a Lua error, and it appears to the outer code as any other Lua error. This error handling code already exists in the previous tutorial code; basically we check status code when calling luaL_loadstring or lua_pcall.

I should also mention that if your C++ code handles errors in a different mechanisn, say return values (HRESULT, maybe), this same approach applies. Take the error in your Lua-called C++ function, and translate it into a Lua error via lua_pushstring and lua_error. This will enable your code to properly handle this failure as a script error.

Ok, that is what you should have said originally, as it gives a reason to the reader (who as you said could be a beginner and not familiar with debates about coding styles like the use of Hungarian Notation). Your original comment comes across simply as a personal attack on the author's coding style.

Let's not get into that debate here though. Again, the reader can take note

I guess it would be nice to throw-in a link to the documentation about the state machine. I haven't used Lua myself, but I guess a less experienced developer wouldn't understand why you need to push arguments to the stack in order to call a function, show an error message, or return from a function.

And yes, proper error handling in tutorials is a nice thing You always wish you put in more when you have to debug a problem with a client's installation

I tried to bind Lua with C++ using some wrappers to allow Lua work with objects. Code was not easily portable from engine to engine and I have to write "Lua wrapper" functions fro C/C++ functions call eitherway. So there is very little reasons to try add object support to Lua. If you really need it, than first, you should try to think, If your srcipt desing is really correct.

The personal preferences of the author's coding style really don't have any bearing on the focus of the article, so this wouldn't be feedback worth mentioning - at least not here.

I disagree. This is a tutorial meant for beginners and suggesting bad coding practices in the examples can be harmful.

You might think Hungarian notation is bad, and I might even agree with you to a point that it's not ideal...

However, many people still use it and even beginners need to know how to read it and be familiar with it. I've had a few bosses who have required that I use it, sometimes simply because it's an old code base and therefore they want the coding style to match (which is a reason I agree with).

Is there a reason you opted to roll your own rather than using tools such as swig or luabind? And did you have a reason for not mentioning them? Not meant as an attack, but if such tools aren't at least mentioned than one might infer that no such tools exist.

That being said, having tried several Lua binding libraries and swig, this article has inspired me to try a manual approach in order to better understand what's really going on.

While I'm a bit late to the party, I might add that if you do use Lua, be sure to setup a test framework. It will be a very nasty language in case you can't detect trivial spelling errors via running unit tests.