Monday, February 28, 2005

Note this has been updated see here


Aside 1 : Scripting with Lua


Contents


  • What to get?

  • Installing

  • Basic Use of Lua

  • A notch, a crank, knarc, ahctona!

  • But wait! There's more amazing where that came from!



Lua allows us to easily add scripting to our game. It's a well known widely used standard (it was used in Balders Gate). Once added it can process both compiled and uncompiled Lua scripts giving a good combination of flexibility and strength. The greatest advantage to Lua is how simply we can insert it into our C# programs. Well, hopefully now your convinced so let's get cracking.


What to get?



LuaInterface 1.2.1



Installing



Unzip LuaInterface 1.2.1 (or later version that you may have been able to download). Put it somewhere safe such as in the Visual Studio directory or some place your unlikely to accidentally delete.



Now boot up visual studio. Create a new C# Console Project.



Now go to the Solution Explorer window.


Play with my options!


(If you can't see it use this
Press me! Press me! and it should pop up!) Take your mouse over to the References icon and right click.



Choose add reference. This should all be pretty standard fare for starting a new project. We're just recovering the basics here in case you're reading this tutorial as a seperate chunk.


Press my buttons


Carefully survey your options and then choose "Browse". Then browse to where you unzipped LUA and go into the bin folder ... and select LuaInterface.dll. My copy of this .dll file is at the following location C:\LuaInterface\bin\LuaInterface.dll.



Once you have chosen this .dll file it will pop up in the the "Selected Components" part of the window. Confirm the selection and we're ready to go. I'm sure there's a method of playing with Visual Studio where you can avoid the "Browse" button all together and have the reference preloaded in, ready to select but after a few minutes fiddling I couldn't find it :(


Your game and your game players are going to need access to the Lua dll files. So you need to have them in project directory. My version of the console application executable that we're working on is currently located here: junk\Visual Studio Projects\ConsoleApplication3\bin\Debug



Open up that directory. Also open up the directory of were you installed Lua. Go into the bin\ directory and copy across (to project directory):


  
lualib-50.0.dll
luastdcall-1.1.dll
lua-5.0.dll


Your working directory should end up looking a little like this:


Press my buttons


That concludes setting up Lua to work with this project. Not too painful.


Basic Use of Lua



Let's write some code. We want to make a C# program that's interfaces with Lua and we want to do it soon, very soon indeed.



using System;
using LuaInterface;

namespace ConsoleApplication
{
class Class1
{
static void Main(string[] args)
{

}
}
}


The above is our class skeleton. The only thing worth noting at the moment is that we're using the LuaInterface reference. We'll concentrate on the main method an make a small program that uses Lua.


  static void Main(string[] args)
{

Lua lua = new Lua();


Here we create a Lua interpreter. We can create as many as we wish and they will all be independant. But for now we'll only have one.



Next we create some Global variables. Remember we're programming in a script language so we're creating Lua global variables not C# global variables. In actual use this "programming" would be done ahead of time and then loaded in from a file. Curently we're doing it directly in the code just to get a feel of what's happening.



lua["num"] = 2;
lua["str"] = "a string";


This creates two Lua global variables. One is called num the other is called str. Note that the variables both hold different types. This is a feature of Lua it is not a strongly typed language in fact it's dynamically typed. Variables can be anything (though Lua only has a few data types). But I don't want to get pulled in to a distracting explanation of how Lua works as a programming language quite just yet. First I'd quite like to get it hooked up to C#.



Now we made these Lua varaibles let's get C# to read them. Now as you really should know by now C# is a strongly typed language. So we need to cast the variables.



double num = (double) lua["num"];
string str = (string) lua["str"];


Note that we must cast to a double here. An int won't cut it as Lua is storing double information. Now we've read this information out from the Lua interpreter, next let's read it out to the screen to see if everything is agreeable.



Console.WriteLine("str:" + str +"\tnum:" + num);
Console.ReadLine();

Agreeable indeed!


Okay so far not very exciting but we're still only getting a feel of what's going on. So let's crank it up a notch next and do something cool.


A notch, a crank, knarc, ahctona!



Okay grip the arms of your chair tightly and get ready to script!



First let's create two functions we might like our brand new scripts to make use of.


  public void DanSays(string s)
{
Console.WriteLine("Dan>" + s);
}

public void ThorSays(string s)
{
Console.WriteLine("Thor>" + s);
}


Feel free to insert your own name instead of Dan or Thor. So now we have two cool functions bursting with game playing potential.



Let's hook them into Lua - so out main method will look like below:


 class Class1
{
static void Main(string[] args)
{
Class1 c1 = new Class1();

Lua lua = new Lua();

//Register our C# functions
lua.RegisterFunction("DanSays" , c1, c1.GetType().GetMethod("DanSays" ));
lua.RegisterFunction("ThorSays", c1, c1.GetType().GetMethod("ThorSays"));



This lets the Lua scripting language make calls to our two C# functions. Incredibly easy isn't it? So in the Lua language we just make calls to DanSays("with a nice string here") or ThorSays("with an equally nice string here") and they will call the C# equivilents!


The RegisterFunction function



The RegisterFunction's first argument is what you'd like to name the function in Lua. Here we chose the same name because it's so simple. But in future function registration we might want to make the Lua name more simple or descriptive than our C# name.



The second argument is the object where the method is stored. Note the word object. That's why we have to instantiate our Class1 class. So if we have a class called people "Class People" and it has a method talk then it allows us to do something like.


   
People pete = new People();
People jeff = new People();
pete.talk("Hello");
jeff.talk("Hello");


We can the register these talk functions seperately.



The third parameter uses reflection - something I currently know nothing about. All I know is that's it's magic like pixey dust and elevators. It magically gets all the knowledge about the method - it's arguments and so forth. Then Lua can call it effectively. You have to pass in a string of the methods name and that's it. Great!


The DoString function


Add this to the end of our main method.



lua.DoString("DanSays('Hello'); ThorSays('Hi! Dan')");
Console.ReadLine();


DoString executes a line of Lua code in string form. Wonderbar!


Dan and Thor have a bit of a chin-wag

But wait! There's more amazing where that came from!



So we've got a pretty groovy scripting language all by doing relatively little.



But now let's arrange it into a bit more of a game like usefullness. Go to your working directory for this project. The place where you copied all those dll files (mine is: My Documents\Visual Studio Projects\ConsoleApplication3\bin\Debug).



Okay create a new directory all call it "scripts"


Behold the new directory!


Now in the scripts directory I've created a simple text file called "Thursdays"



It has the following not-so-witty-banter.


DanSays("Hey, Thor!");
ThorSays("Hi Dan! You know Thursdays . . . ");
DanSays("*sigh* yeah, Thor, I know Thursdays.");
ThorSays("Named after me you know!");
DanSays("Yeah, I know.");


Okay. So in scripts\Thursdays.txt with have some future-award-winning dialogue. It's written in Lua but only using custom functions designed by us because we're cool and groovy designers.


Back to El Code



Make your main method match mine pictured below:


  
static void Main(string[] args)
{
Class1 c1 = new Class1();

Lua lua = new Lua();

//Register our C# functions
lua.RegisterFunction("DanSays", c1, c1.GetType().GetMethod("DanSays"));
lua.RegisterFunction("ThorSays", c1, c1.GetType().GetMethod("ThorSays"));


lua.DoFile(@"scripts\Thursdays.txt");
Console.ReadLine();
}


The little '@' sign means take this all literally. Because now and again '/' are control characters and they mess me up. But this fine piece of programming hereafter now as "ScriptRunner3000" will run any script we call Thursdays - without recompling - yes you heard me correctly - without recompiling!



Of course if we want to get really fancy we could write some code to enumerate all the files in scripts and then produce a menu allowing you to choose which you'd like to use. But this is much more of a proof of concept deal. Anyway the output:


The extended edition


Now the tinker can be tinkered with to our hearts extent and the exutable will show those changes without having to be run. Of course it's a lot more powerful than it is here. We can uses Lua's code constructs like loops and structures and all those niceities - wasn't putting a scripting engine in really simple? :o



Now you should brush up your Lua!



Globals



Lua is simple and powerful I just wanted to show persistancy of globals then maybe we'll end this tutorial here. But later come back to really really groovy things like coroutines



  
static void Main(string[] args)
{
Class1 c1 = new Class1();

Lua lua = new Lua();

lua.DoFile(@"scripts\Thursdays.txt");

string b = (string) lua["name"];
Console.WriteLine("Name:" + b);
Console.ReadLine();

}


If in Thursdays.txt we now include the line name = "rabbits" then the above code will outpt rabbits. See the scope isn't lost in the file which is super groovy and allows us to easily insert scripts into the game loop - yay!


References used



  • LuaInterface: User’s Guide by Fabio Mascarenhas

  • LUA 5.0 Reference Manual (avaliable from www.lua.org)

11 comments:

Anonymous said...

Hi. I have been searching everywhere for somon who could explain how to use Lua within C#. Thanks for blogging it. It's going to make my gaming engines stronger.

Eric C. Tomlinson
http://www.imaginetgames.com

Anonymous said...

YES!!!!
I have been looking for something like this! Lua sure beats any scripting engine I could write!

Anonymous said...

BTW, I'm putting some of your example code into one of my c# projects as a comment, to demonstrate the use of LuaInterface. The project is going to be released under the GPL. Is that OK with you? Please tell me at adam@comsmart.org.

Anonymous said...

please help me, i need execute a lua file as line.

for example, in your program:

Lua lua = new Lua();
lua.RegisterFunction("DanSays", c1, c1.GetType().GetMethod("DanSays"));
lua.RegisterFunction("ThorSays", c1, c1.GetType().GetMethod("ThorSays"));
lua.DoFile( @"scripts\Thursdays.txt");

///

I wish that:

//2 is line number
lua.DoFile( @"scripts\Thursdays.txt", 2);

ignore No.1 line in Thursdays.txt:"DanSays("Hey, Thor!");"

how do it?

thank you!

balaam said...

You could use a lua if-statement :D

or you could read each line in the text file into an array of strings and then call lua.doline() on each. (apart from the first one)

it may not be called "doline" but there's another function in there for running lua code from a string.

Anonymous said...

okey, I code it according to you

System.IO.StreamReader sr = new StreamReader( @"scripts\Thursdays.txt" );
string [] rows = sr.ReadToEnd().Split( Environment.NewLine.ToCharArray() );
ArrayList list = new ArrayList();
for ( int i = 0, count = rows.Length; i < count; i++ )
{
if ( rows[i].Trim().Length > 0 )
list.Add( rows[ i ] );
}
StringBuilder output = new StringBuilder();
lua.DoString( list[1].ToString() );
// lua.DoFile( @"scripts\Thursdays.txt");

Thank you!

balaam said...

yah that should do it. You could just split it after one line too so you'd only have two lines in the array.

array[0] the first line
array[1] everything else

both these should work.

Anonymous said...

Did anyone ever think to write a
C# scripting engine via reflection?

There are some good tutorials for it
around the net.

The thing that worries me about Lua
is that its under the MIT license
and if I am correct the MIT license
doesn't say anything about not being
able to change it unlike the GPL
thus they could make it licensed
software anytime.

balaam said...

Lua is completely fine to be in any kind of project (commerical or not). It's also fine to edit the source code for your own purposes - i.e. rewriting Lua. -> but then you're not allowed to use Lua's name anymore.

I believe in one of the SDKs Microsoft did show how to use C# as a scripting language - but if you notice the title of this post "Scripting with Lua" there is no place for it here :D



Lua is licensed under the terms of the MIT license, which is compatible with GPL and also approved by the Open Source Initiative.


So I think the lesson for today is:
Don't believe the rumours you hear about licenses in the pub.

Anonymous said...

Search the web a bit for the scripting thing via c#.

I used this library to use c# or vb scripting for various events. The event handler looks up what it needs to do in a sql server database. The record contains the language of choice (vb or csharp) and the script to execute.

For my purpose I created a ScriptMethods.dll file with all the external methods that would be accessible.

It worked great even when I ran it multi threaded executing each script every time a stock price changed. Only thing I had problems with was when I tried to get it running as a windows service.

I am going to be using this lua stuff to parse the table information from a World of Warcraft addon. Thanks guys this article is a huge help.

Anonymous said...

Sorry I forgot to post the url to do a similar thing with c# scripting.

http://www.west-wind.com/presentations/DynamicCode/DynamicCode.htm