Creating a Plug-in for a Lua framework

Ever want a feature and get stuck? Feel frustrated as to why a simple feature is not available and to add insult to that injury, some help forums would discriminate against you for being a free vs paid user. In this article we shall look at using the Lua C API that is useful to create plug-ins. We shall also look at the plug-in architecture and how Lua registers native code (in C/C++ or Objective-C) functions that are then made available to the Lua code.

Requirements

For this article, we need the following
* Gideros Studio
* xCode

Plug-in structure

If you had created DLL's or worked with external libraries, all of them need something like an entry point, almost like the main.lua that is used as the starting point in most lua framework apps. This entry point in our case for a plug-in is g_initializePlugin and the exit point is as you might have guessed g_deinitializePlugin. These two functions are also what you might call a constructor and destructor, called when initializing and deinitializing.

We also need to register he plug-in for use, for that we use the C Macro REGISTER_PLUGIN

Lua C API

While we are used to the Lua scripting language, there is a whole api that can be used to interface C with Lua and the concept is slightly different than what we are used with Lua. The Lua C API works on the priciples of stacks, where data is pushed or popped on or off the stack.

There is a lot of material on C api if you Google for it. One of the feedback that I found from some of the readers of my book, that was a bit disappointing. The ones that commented would rather have commented game code in a book and Google the rest, so rather than try to explain or describe any of that, let Google be your friend on this one.

Creating the Plug-in

To do so, first open the folder where you have Gideros Studio, this contains a zip file called the GiderosiOSPlayer.zip, unzip it and you will find the folder it created of the same name, this contains the xcode project that would compile to the Gideros player.

When the xcode project opens up, click on the file menu and select a new file of type Empty. Give it a name and an extension of mm, in this case, let's call it test.mm

Locate this file from the project explorer and enter the following code in it.

First the header files that we need to include
#include "gglobal.h"
#include "gideros.h"
#include "gevent.h"
#include "gapplication.h"

after these includes, we need to declare the variables that we will use in this module/plug-in

static lua_State *L = NULL;

in our minimalistic plug-in, we just need a variable to hold the lua state.
Note: that in plug-ins most of the variables and functions are declared static.

Next we add our initializer and deinitializer code
static void g_initializePlugin(lua_State *L)
{
 ::L = L;    //Store a local copy, will work without this too
}

static void g_deinitializePlugin(lua_State *L)
{
 ::L = NULL;    //Free the local copy
}

REGISTER_PLUGIN("mytest", "1.0")

This is supposedly what a minimal plug-in would be, however it will do nothing and nor will it be usable from your Lua code.

Note: At any point now, if you run the app in the simulator or on your device, you will see the Gideros Player running which will display the Gideros Player version and the IP Addresses to which you can connect from Gideros Studio.

Let us add a little code that would allow us to use the require "mytest" in your Lua code. Right now, if you try this, it would spawn errors telling us that the module mytest could not be found.

static void g_initializePlugin(lua_State *L)
{
 ::L = L;

 lua_getglobal(L, "package");
 lua_getfield(L, -1, "preload");

 lua_pushcfunction(L, registerMe);
 lua_setfield(L, -2, "mytest");
 lua_pop(L, 2);

}

and we need to create the function registerMe that would register the functions available for use in the module mytest. these we can define as

static int registerMe(lua_State * L)
{
 static const luaL_reg funcs[] = 
 {
  {"test", test},
  {NULL, NULL}
 };

 luaL_register(L, "mytest", funcs);

 return 1;
}

and we also need to define the function test that can be called from our Lua code

static int test(lua_State *L)
{
 NSLog(@"This is a test");    // Print to the console
 return 0;                    // Number of return values
}

If we run our xcode project now, the Gideros Player will start (either on the device or the simulator). We can start a new Gideros Project and test the our plug-in with following code
local t = require "mytest"
 t.test()

and run it, we would see nothing on the screen or the console in Gideros Studio, however if you look at the console on xCode, you would see the This is a test printed to the console.

Returning Data


Let us change that now to return some data back to our Lua program. We shall for this testing purpose, return the value "This is a test" when we call the function test.

static int test(lua_State *L)
{
 lua_pushstring(L,"This is a test");    // Push a string value to return onto the stack
 return 1;                              // Number of return values
}

Note that the functions are static and the return type is int. The return value tells Lua how many items are we returning from the function. In the first example, we were returning nothing, hence we returned 0, in the new sample, we are returning a single string, "This is a test" and hence we return 1. The return values are pushed onto the stack, which we do with the code lua_pushstring(L, "This is a test");.

local t = require "testme"

print(t.test())

Getting Data from a Lua function

Like we pushed a string to the stack and returned data to a Lua function, we can also get data from a Lua function. For example, consider the code

local t = require "testme"
 print(t.test("OZApps"))

And we can get the function to take the value passed and return a string like Hello +


static int test(lua_State *L)
{
 const char *string = luaL_checkstring(L, 1);        // Get the parameter and check if it is a string
 char res[256];

 snprintf(res, sizeof(res), "Hello %s", string);    // Concatenate the passed value with Hello
 lua_pushstring(L, res);                            // Push value to the stack to return
 return 1;
}

A couple of things to note here, we are expecting a string parameter and by using the function luaL_checkstring we ensure that if the parameter passed is not a string, the function will throw an error in Lua and not crash the plug-in code. Plus also take care of the fact that we are using Objective-C which uses NSString (which is an object) and what we need for these functions is c type strings. This can be easily converted between using the NSString member function cStringUsingEncoding or stringWithCString:encoding

local t = require "testme"
 print(t.test(""))

Now when we run this code, we get an error
main.lua:2: bad argument #1 to 'test' (string expected, got nil)

For more information, Google is your friend. After all once this information is published, it is part of the internet so why would anyone want any collated articles like these when there's information out there on the internet.


Other Frameworks

This article is geared towards creating a plug-in for Gideros Studio.

We have not had the chance to play or work with the Corona Enterprise SDK so not sure on the nitty-gritties, plus there are not many articles out there that explain how to use the Enterprise SDK to add your own stuff.

For other frameworks or code, the concepts are more or less the same, most of the calls we have used are available with the Lua langauge C Api, so all functions that start with lua_ or luaL_ are part of the lua source.

Going Forward

It is that simple, since we included the file in our player, the code would be available to all programs tested on the player, if you build a new version, you will have to include the test.mm (or your custom plug-ins) with each compilation.

When you export the project from Gideros Studio, you will have to add the file to your project or it will spawn errors in your Lua code as that module would not be found.

Continuing in our series, next time we will add the functions to support the clipboard both ways, to copy and to paste text. We could also consider creating this plug-in into a dynamic library that you could distribute to others that could be added to the projects. The other advantage of this is that since the code is using C/C++ or Objective-C code it should be much faster than pure Lua code. It could also offer access to things that were not possible from within the framework.


Image Source : http://www.blogohblog.com/10-new-plugins-for-wordpress-3-0/

Comments

Post a Comment

Popular Posts