Home - Forums - Documentation - Gallery - Bugs


Contents

Creating a LUA plugin

Short indtroduction

In this tutorial we will create a plugin for integrating Lua. That means, you can reach the the whole CS API with LUA scripting. Note, you can any SWIG supported language integrate easily into Crystal Space. In that case, when SWIG is new for you, please have a look at the project here:

SWIG homepage

A note about the history of this plugin. The source itself is just partially my work. The original code written by Brandon Ehle in 1999. I started to update the code, but the greater part of the code written by Mildred. Many thanks for her efforts.

Setup your enviroment

The very first step is download, complie and install LUA. You can get it from the official site:

LUA homepage

You can find here also some precompiled version, you can use them, when you want. This tutorial uses the LUA 5.1 version.

The next step is setting up an external CS project within your enviroment. Don't forget to set up your project as a dynamic link library, and the neccessary CS and LUA libs and include director, . The concrete steps depending from your IDE and operating system. ( This is a so called "professional" tutorial. Professional tutorials are not just for C++ masters , but I assume, you can set up this project without any external help.)


Start coding

Crystal Space uses the iScript interface to communicate with scripting languages, so we have to implement this interface to integrate LUA. So we include some CS headers:

#include <ivaria/script.h>
#include <iutil/eventh.h>
#include <iutil/comp.h>
#include <csutil/csinput.h>
#include <iutil/string.h>

#include "lua.h"

Then we introduce a new namespace to avoid name conflicts, in Crystal Space style:

CS_PLUGIN_NAMESPACE_BEGIN(cslua) {

This macro creates for us a new cslua namespace. The namespace will be closed with the

CS_PLUGIN_NAMESPACE_END(cslua) 

macro.

Tha csLua scf class will encapsulate the lua related stuff.

class csLua: public scfImplementation2<csLua, iScript, iComponent> {

public:
    csLua(iBase* iParent);
    virtual ~csLua();

    static csLua* shared_instance;
    iObjectRegistry* object_reg;

    lua_State* L;
    int varTable;

We define the constructor and the virtual destructor first. The shared_instance is a static csLua instance, this object will be used by Crystal Space. We need a know about the ObjectRegistry, object_reg will store a pointer to this. The lua_State* is a specia statment indicator, when you knows Lua, yo knoe, what is it.

The public filed continues with functions:

virtual bool Initialize(iObjectRegistry* object_reg);
virtual bool HandleEvent(iEvent&);

The Initialize function will warm up the plugin. The HandleEvent function will deal with the plugin-related events (these are spcial events, like the command line help event.

Here comes the iScript interface functions:

The iScript allows us to run text form a char arrray (RunText, or from file(LoadModule):

 virtual bool RunText(const char *Text);

    virtual bool LoadModule(const char *name);
    virtual bool LoadModule (const char *path, const char *name);

Then provides us many Store function to store different types of variables.

    virtual bool Store(const char *name, char const *data);
    virtual bool Store(const char *name, iScriptObject *data);
    virtual bool Store(const char *name, double data);
    virtual bool Store(const char *name, int data)
        { return Store(name, static_cast<double>(data)); }
    virtual bool Store(const char *name, float data)
        { return Store(name, static_cast<double>(data)); }
    virtual bool Store(const char* name, void* data, void* tag)
        { return false; } /* deprecated */

The foloowing functions read the stored varibles value by name:

    virtual bool Retrieve(const char *name, csRef<iString> &data) const;
       
    virtual bool Retrieve(const char *name, csRef<iScriptObject> &data) const
        { return false; } 
    virtual bool Retrieve(const char *name, double &data) const;
    virtual bool Retrieve(const char *name, int &data) const
        { double n; bool res = Retrieve(name, n); data = static_cast<int>(n); return res; }
    virtual bool Retrieve(const char *name, float &data) const
        { double n; bool res = Retrieve(name, n); data = static_cast<float>(n); return res; }

Next iScript ensures us many Call() to call stored functions:

    virtual bool Call(const char *name, const char *fmt, ...)
        { return false; } /* TODO */
    virtual bool Call(const char *name, double &ret, const char *fmt, ...)
        { return false; } /* TODO */
    virtual bool Call(const char *name, int &ret, const char *fmt, ...)
        { return false; } /* TODO */
    virtual bool Call(const char *name, float &ret, const char *fmt, ...)
        { return false; } /* TODO */
    virtual bool Call(const char *name, csRef<iString> &ref, const char *fmt, ...)
        { return false; } /* TODO */
    virtual bool Call(const char *name, csRef<iScriptObject> &ref, const char *fmt, ...)
        { return false; } /* TODO */

And finally some special funtion for objects and bool variables :

virtual csRef<iScriptObject> NewObject(const char *type, const char *fmt, ...)
        { return 0; } /* TODO */

    virtual bool SetTruth(const char *name, bool data);
    virtual bool GetTruth(const char *name, bool &data) const;
    virtual bool Remove(const char *name);

The plugin needs an event handler. We hide this handler inside the csLua class:

struct EventHandler :
        public scfImplementation1<EventHandler, iEventHandler>
    {
        csLua* parent;

        EventHandler (csLua* parent) :
            scfImplementationType(this),
            parent (parent)
            {}
        virtual ~EventHandler()
            {}
        virtual bool HandleEvent (iEvent& e)
            { return parent->HandleEvent(e); }

        CS_EVENTHANDLER_NAMES("crystalspace.cslua")
        CS_EVENTHANDLER_NIL_CONSTRAINTS
    };

The event handler simply delegates the events to csLua class. So csLua needs a reference to the handler:

 csRef<EventHandler> eventHandler;

The header is ready, let's go to create the implementation.

Implementation

The cslua.cpp will start some Lua header:

extern "C" {
#include <lua.h>
#include <lualib.h>
#include "lauxlib.h"
}

Don't forget the extern "C" section, lua is written in clean C, so this is neccessary in a C++ project.

Next comes the CS headers:

#include <cssysdef.h>
#include <csutil/sysfunc.h>
#include <csutil/syspath.h>
#include <csutil/csstring.h>
#include <csutil/util.h>
#include <iutil/cmdline.h>
#include <iutil/eventq.h>
#include <csutil/event.h>
#include <csutil/eventhandlers.h>
#include <iutil/objreg.h>
#include <iutil/vfs.h>
#include <ivaria/reporter.h>

#include "cslua.h"
 

Nowe some SWIG-related stuff:

extern "C" {
    int SWIG_luaopen_cspace(lua_State* L);
}

The cspace is the great SWIG interface file. Swig will generate from the CS API the bindings after this file. You can find it in your CS/include/bindings/ directory. We need to modify it later.

The code continues with more macro:

CS_IMPLEMENT_PLUGIN
CS_PLUGIN_NAMESPACE_BEGIN(cslua){

/*****************************************************************************/

SCF_IMPLEMENT_FACTORY(csLua)
csLua* csLua::shared_instance = 0;

The
CS_IMPLEMENT_PLUGIN 
is a neccessary macro to indacte CS, we want to implemment a CS plugin now. The csLua shared_intance pointer is need set to null.

Links

Here is some code you might want to look at: https://code.launchpad.net/~cslua/cslua/CS Mildred 01:33, 2 May 2008 (CEST)

| Article | Discussion | View source | History |