Tuesday
Dec292009

Remote Debugging for Lua on the iPhone

The biggest problem with Lua, and this probably goes for a lot of scripting languages, is that it is difficult to debug remotely. If you’re running under Windows then you have Decoda, which hooks into a local Lua process to give you a powerful debugger. Outside of that, you’re on your own.

My needs are simple so far:

  1. Ability to log data from device or simulator with or without GDB running
  2. Ability to trace script execution real-time
  3. Minimal native code- try to keep it all in the script
  4. Minimally invasive in the app

Number two is critical for dynamic languages. Compilation only catches syntax errors so scripts can die in unexpected ways at runtime, and when one does you’re left with the option of adding print statements to trace execution and figure out why/where it died. We’ve all done this before in one language or another but it’s far from a state-of-the-art technique.

The requirement for the system to work without GDB and on the actual device points to a network-based solution. The minimally invasive requirement means that we don’t want to add any UI on the client and that we need auto-discovery/configuration.

Autodiscovery

For autodiscovery the obvious choice is Bonjour. It is supported on the iPhone, Mac, and Windows and is very stable. That said, I’m going to go ahead and ignore it. My thinking is this- if I keep this all in standard TCP/UDP socket code then I can share some of the code between the client and server and do the whole thing in Lua. This means I don’t need to write a native OSX app, I only need to run a Lua script. The key getting this done quickly is luasocket, which encapsulates the standard C socket interface into a clean Lua library.

My protocol for autodiscovery is shown below:

autodiscovery.png

The purpose of this sequence of events is:

  1. Broadcast a PING to give the server the IP of the iPhone
  2. Receive a PONG to give the iPhone the IP of the server
  3. Initiate a reliable TCP connection back to the server for the bulk data

Obviously, if the script is running in a simulator then there is no need to discover the remote IP so this applies only to scripts running on the device.

To support the protocol above, only two additional functions are required in Lua, one to determine if the script is running in the simulator and one to find the Wifi IP so the initial broadcast packet can be sent.

These two functions are implemented with the following code:

// code taken from:
// http://stackoverflow.com/questions/1448411/how-to-check-for-local-wi-fi-not-just-cellular-connection-using-iphone-sdk
// http://zachwaugh.com/2009/03/programmatically-retrieving-ip-address-of-iphone/
// and a few other places

static int sfGetWifiIP(lua_State * l)
{
    struct ifaddrs *addresses;
    struct ifaddrs *cursor;
    BOOL wiFiAvailable = NO;
    NSString *address = @"error";

    if (getifaddrs(&addresses) != 0) return NO;

    cursor = addresses;
    while (cursor != NULL) {
        if (cursor -> ifa_addr -> sa_family == AF_INET) 
        {
            // Check for WiFi adapter
            if (strcmp(cursor -> ifa_name, "en0") == 0 ) {
                wiFiAvailable = YES;
                address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
                break;
            }
        }
        cursor = cursor -> ifa_next;
    }

    freeifaddrs(addresses);
    if(wiFiAvailable){
        lua_pushstring(l, [address UTF8String]);
    } else {
        lua_pushnil(l);
    }

    return 1;
}

static int sfIsRunningInSimulator(lua_State * l)
{
    NSString *model= [[UIDevice currentDevice] model];
    NSString *iPhoneSimulator = @"iPhone Simulator";
    if([model compare:iPhoneSimulator] == NSOrderedSame){
        lua_pushboolean(l, true);
    } else {
        lua_pushboolean(l, false);
    }
    return 1;
}

These have been added to the scriptutils.mm file in the included project.

Lua Debugging Hooks

The standard Lua distribution includes a debug library to allow a script to collect basic debugging information. Callback functions can be defined that are called by several events including function calls, and line execution. We can use the “line” callback to build a basic trace function. The code below outputs a trace to stdout:

function trace(event, line)
    local s = debug.getinfo(2).short_src        
    print("TRACE:"..s..":"..line.."\n")
end
debug.sethook(trace,"l")

The documentation for the debug module can be found int he Lua docs or in Programming in Lua. It's worth noting that the code above works well as-is if you just want to dump a trace to GDB when running the app with a debugger running. For many this will be useful enough.

Server Code

The server for this system is also written in Lua. As with the rest of my Lua work, I’ve tried to keep it to the minimum code that will do anything useful. It will only allow one client to connect and runs from a terminal window. It should be cross-platform since it only requires a working Lua installation and the luasocket module.

The included project includes a zip file for CompleteLua. This is just a build of Lua with the luasocket module compiled in by default. It does not require installation of any type- it can be run in-place.

Integration

If you’ve been following the past few posts here then you already have a working Lua installation running in an app. To enable the functionality here the following changes have been made:

Download luasocket and add to project as a new target. Remove the wsocket.c and wsocket.h files as these are only used for Windows

Update the test lua target to depend on luasocket and link against it.

Add the scriptutils.h and scriptutils.mm to test lua to add the two new functions for simulator detection and Wifi IP.

Modify mcLua to initialize the new luasocket library and enable the included debug library

Modify the HelloWorld layer to initialize the script utils functions.

After creating the mcLuaManager object, run the debugsupport.lua script to add the debugging functions to the global namespace. The script object can be destroyed after running it to save memory- the functions will remain in the globals table.

Now you can add the following code to the top of the script to be debugged:

How to Use

To turn on the remote tracing in a script the following lines need to be added:

local ip,port = GetRemoteAddress()
if ip and port then
print("Remote Server: "..ip.." , "..port)
EnableTracing(ip,port)
end

The server is run on the Mac by entering the command ./CompleteLua debugserver.lua . At that point you can run your app on the device or a simulator.

The demo will show the following:


terminal window.png

The numbers after the colons represent the current line number of the script being executed.

Conclusion

The project can be downloaded here. The zip file includes a directory called "server." This directory contains the CompleteLua binary, and a zip with the source, as well as the debugserver.lua file.

As-is, the trace functionality is pretty useful but there are obvious improvements that could be made. I'm going to try this for a while and see what ends up being the most obvious missing element. I'm not entirely sure that keeping the whole thing in Lua on the client is the correct thing to do but it's functional and it will let me work now and see if I need to move it back to the C/C++ realm. My gut tells me that more C/C++ code may be required to get all of the functionality I want in the future but I'd rather avoid a premature decision.

As always, I'd appreciate any comments or suggestions.

Wednesday
Dec022009

Integrating Lua into an iPhone App Part 2

In part one I outlined the mechanics of adding Lua to a Cocos2d iPhone project and getting a very simple script to run. In this post I’m going to show a very simple script manager, mcLua, that allows you to run one or more Lua scripts and to include them in the update loop that runs with every frame. While I’ve titled this series of posts “Integrating Lua into an iPhone App,” this is clearly more suitable for use in a game than a generic business app.

As I mentioned before, the idea for this system was taken from an article in Game Programming Gems 5 titled “Building Lua into Games” by Matthew Harmon. The code is mine so you are free to use this for anything you want.

Programming Notes

As a programming convention, I usually preface my class names with “mc” out of habit- I spend most of my programming time working on MeshCAM. All mcLua code is implemented in C++ rather than Objective C. I am more comfortable programming in C++ and Objective C makes it a trivial task to mix the two languages. Any Objective C purist should be able to port the files here with little effort.

My goal in designing the API is to make it easy to read and use even if I have to make design decisions that I don’t like- friend classes in this case. While I generally dislike using these, I think the benefits outweigh the disadvantages in this case and make the API much cleaner.

The code presented here is the minimum required to do a useful job. With this done, it is a simple matter to extend it to suit a specific app. I have found this to be a better approach than to over generalize a system to the point that it becomes too large to read and understand quickly.

This post does not attempt to explain the low-level details of Lua and how to extend it. There are many good online resources for that including the online Lua manual. The standard book to learn these details is Programming in Lua.

Part 2 files can be downloaded here if you’d like to try the code. It’s still new code so there may be bugs that I haven’t discovered yet. Let me know if you find anything strange.

Design

The goals of mcLua are:

1) Efficient execution of several concurrent scripts.

2) The ability to let scripts share a global environment.

3) Minimal effort to integrate into an existing app.

4) No unnecessary limitations or requirements imposed on the host app- the API should be self-contained.

5) The system should not require the scripts to implement a tick() function called at each frame.

It is item 5 above that drives the design of mcLua. To better illustrate my intention, lets assume that you want to use a script to configure a level and spawn the enemies. Your script might look like the following:

--setup Level
level.placePowerup(10,20)
level.placePowerup(310,20)
level.setAmoCount(100)
--run the level
level.spawnEnemy()
script.waitSeconds(3)
level.spawnEnemy()
script.waitSeconds(2)
level.spawnEnemy()
script.waitSeconds(1)
level.spawnEnemy()
script.waitSeconds(1)
level.spawnEnemy()
script.waitSeconds(3)
level.spawnBoss()

Clearly, this would not be called the “right” way to do it by many people because it’s verbose and unsophisticated. The benefit to this approach is that it’s easy to debug, it reads like a story script, and,because it’s easy to write, you can let someone else work on level design and tuning without involving you, the programmer. While you can certainly add loops, functions, and more complicated structures, it is not required.

The example above is just one use-case. There is nothing to stop you from using Lua in a way that it is more deeply integrated into your game logic.

Architecture

mcLua is based on two classes- mcLuaManager and mcLuaScript. mcLuaManager maintains the parent lua_State for all of the scripts. Child scripts are created as new Lua threads from the parent lua_State. Threads in Lua do not map to operating system threads on the iPhone- they are a child lua_State that shares a global environment with the main lua_State. By default, all multitasking is cooperative unless one were to make significant changes to mcLua and the app.

If a script requires complete isolation then additional mcLuaManagers can be created.

class mcLuaManager{
public:
    mcLuaManager (void);
    ~mcLuaManager(void);

    bool LuaOpenLibrary(const char * name, const luaL_reg * libs);

    void SetGlobalNumber(const char * name, double val);
    void SetGlobalInteger(const char * name, int val);
    void SetGlobalString(const char * name, const char * val);

    mcLuaScript * CreateScript(void);
    void DestroyScript(mcLuaScript *);

    void Update(float elapsedSeconds);

};

There are only a few public functions (there are some private members and functions in the real class that are not shown here) so we can list use of each below:

LuaOpenLibrary(const char * name, const luaL_reg * libs) - Used as a replacement for luaL_openlib since you do not have direct access to the lua_State pointer. This will be the function called to load any custom functions that you need into all mcLuaScripts created from this manager.

SetGlobalNumber(const char * name, double val) - Sets a global number variable in Lua with the name and value given.

SetGlobalInteger(const char * name, int val) - Sets a global integer variable in Lua with the name and value given.

SetGlobalString(const char * name, const char * val) - Sets a global string variable in Lua with the name and value given.

CreateScript(void) - Creates a new mcLuaScript object. The object returned should not be deleted by the user, only by calling the DestroyScript() function. No reference to the mcLuaScript object needs to be maintained- it will be deleted in the mcLuaManager destructor.

DestroyScript(mcLuaScript*) - Call to destroy a child script object.

Update(float elapsedSeconds) - Call every frame to all all child scripts to run. The elapsedSeconds parameter should be set to the time since the last call to this function.

Scripts cannot be run directly in the mcLuaManager, doing this requires the creation of an mcLuaScript object using the CreateScript() function. The memory used for this new object is owned by the mcLuaManager that created it- it MUST NOT be freed by the user.

The public interface for mcLuaScript is even more minimal-

class mcLuaScript{
public:
    void LoadFile(const char * name);
    void LoadString(const char * buffer);

    //used internally by library- not for end user use
    void YieldFrames(int num);
    void YieldSeconds(float secs);
};

There are only two functions that are meant to be called by the user, RunFile and RunString.

LoadFile(const char * name) - Load a Lua file into memory. The name parameter should represent a complete path to the file.

LoadString(const char * string) - Compile string and load it into memory.

Note that both the constructor and destructor are protected since an mcLuaManager instance is responsible for creating and destroying the script objects.

The Painful Details

Skip this section if you just want to know how to add mcLua to your project- it assumes some internal knowledge of Lua’s C API.

First, as a refresher on how functions are added to Lua, any fuction called by Lua must have the following form -

static int FunctionName(lua_State * l){
    return 0; //number of parameters returned by function
}

Since the functions are pure C, they cannot directly know the mcLuaScript object that called them- they only have a lua_State pointer to work with. We can overcome this limitation by adding a variable in Lua that maps all mcLuaScripts to their lua_State members. This is done when the mcLuaScript is created by an mcLuaManager:

mcLuaScript * mcLuaManager::CreateScript(void)
{
    lua_State * s = lua_newthread(mainState_);

    //Add reference to the new thread in the Lua Registry so
    //will not be garbage collected
    int r = luaL_ref(mainState_, LUA_REGISTRYINDEX);

    //Create a new mcLuaScript object to hold the new thread
    mcLuaScript * ns = new mcLuaScript(s,this,r);

    //Add an entry to map the new lua_State to the 
    //new mcLuaScript object 
    lua_pushlightuserdata(mainState_, s);
    lua_pushlightuserdata(mainState_, ns);
    lua_settable(mainState_, LUA_GLOBALSINDEX);

    //insert the new script into the list
    ns->next_ = head_;
    head_ = ns;
    return ns;
}

Light userdata in Lua is a special data type that represents a void* pointer in C. This allows the addresses of both the new mcLuaScript and it’s lua_State to be used as entries in a table. The code above ensures that any C function can find the mcLuaScript that called it by doing the following:

static int FunctionName(lua_State * l){
    mcLuaScript * s;
    lua_pushlightuserdata(l,l);
    lua_gettable(l, LUA_GLOBALSINDEX);
    s = (mcLuaScript *)lua_touserdata(l,-1);
    //s now contains a pointer to the mcLuaScript object that was responsible for this function call
    return 0;
}

You will see this code used below to implement the script.wait* functions below.

As mentioned in the design section, mcLua does not impose a requirement for a script to implement a Tick() function that is called on each frame. Instead, mcLua encourages the files to be laid out more linearly with calls to delay functions when the work on the current frame is done. In this case, the delay functions are called script.waitSeconds(sec) and script.waitFrames(numFrames). Both of these functions end the execution of the script until the appropriate amount of time has passed. Internally, these are implemented using the Lua C API function lua_yield .

mcLua.mm contains two static functions, shown below, that implement the script.WaitSeconds and script.waitFrames behavior:

static int LuaYieldSeconds(lua_State * l)
{
    mcLuaScript * s;
    lua_pushlightuserdata(l,l);
    lua_gettable(l, LUA_GLOBALSINDEX);
    s = (mcLuaScript *)lua_touserdata(l,-1);

    float n = lua_tonumber(l,1);
    s->YieldSeconds(n);
    printf("LuaYieldSeconds %f\n",n);

    return (lua_yield(l, 0));
}

static int LuaYieldFrames(lua_State * l)
{
    mcLuaScript * s;
    lua_pushlightuserdata(l,l);
    lua_gettable(l, LUA_GLOBALSINDEX);
    s = (mcLuaScript *)lua_touserdata(l,-1);

    int f = lua_tonumber(l,1);
    s->YieldFrames(f);
    printf("LuaYieldFrames %d\n",f);

    return (lua_yield(l, 0));
}

When the functions above call either mcLuaScript::YieldFrames() or mcLuaScript::YieldSeconds(), the mcLuaScript sets some internal counters that cause it’s Update(float) function to return immediately until the proper amount of time has passed. Only then will the Lua thread resume execution. This gives the benefit of mcLuaScript objects consuming almost zero execution time while waiting.

Use of mcLua

As you can see from the API above, the interface to mcLua is minimal. To integrate it into an app you are only required to do six things:

1) Import mcLua.hpp in the file that you want to use it.

2) Create an mcLuaManager instance. This instance can be global, in your Cocos scene, or in your Cocos layer.

3) Use the LuaLoadLibrary() function in mcLuaManager to add any custom functions that your application requires.

4) Create one or more mcLuaScript objects from the mcLuaManager and call the LoadFile method to get the scripts into memory.

5) Call the Update() function for every frame with the time elapsed since the last call. If you don’t require per-frame updates then you can call this function on another schedule.

6) Destroy the mcLuaManager object at the end of the app,scene, or layer.

Demo Project

The demo project for this post can be found here. It shows the complete mcLua/Cocos2d integration. The HelloWorld layer in HelloWorld.mm initializes an mcLuaManager instance and creates two mcLuaScripts based on the luascript.lua and luascript2.lua files in the project resources. luascript.lua is responsible for placing a number of penguin sprites while luascript2.lua places a number of polar bear sprites. The Scriptfunction.mm file shows how to add additional Lua functions to let scripts interact with your native code. It can be used as a template to add more functions of your own.

Conclusion

The mcLua system is a simple, lightweight, and powerful way to add scripting to an iPhone application. While powerful in it’s current implementation, it should only be used as a starting point. It is easily extended to fit the needs of your app and small enough to be quickly understood. I expect that you will find more uses for Lua if you start it out with a simple task in your app.

Monday
Nov232009

Syntax Highlighting test

Just a quick test of a syntax highlighting plugin
class test{
public:
 test();
 int testMethod(void)const;

private:

};	 

Lua test
funtion testFunc(param)

 while(1) do

 end

end


Objective C
@interface classname : superclassname {
    // instance variables
}
+classMethod1;
+(return_type)classMethod2;
+(return_type)classMethod3:(param1_type)parameter_varName;
 
-(return_type)instanceMethod1:(param1_type)param1_varName :(param2_type)param2_varName;
-(return_type)instanceMethod2WithParameter:(param1_type)param1_varName andOtherParameter:(param2_type)param2_varName;
@end

Friday
Nov132009

Integrating Lua into an iPhone App

I spent a long evening getting Lua to compile and link in an iPhone project. It turned out to be a very simple task once I figured out some basic things about adding targets in Xcode and remembered one small quirk about Lua that I learned a few years ago and forgot along the way. I’m going to outline the process below using a Cocos2d project although a UIKit project would be the same. Please note that I’m still new to Xcode so this may not be the most optimal approach but it works well. I welcome your comments and corrections.

In order to keep this post as short as possible, I will only describe the process of getting everything to build and function. I'll try to post more later about the how's and why's of Lua.

You can find a zipped version of the project here if you don’t want to follow the process below.

Before starting anything in Xcode, there is the question of how to structure the project: as a single project and target, as a single project with an additional Lua target, or as two different projects. Xcode makes it easy to use any of those approaches but I chose to use a single project with multiple targets. One of the additional targets will be used to build a static Lua library. This gives the benefit of keeping Lua separate from your project code but it keeps all of the configuration and SDK information unified. Incidentally, this is the same approach taken by the Cocos2d library when you create a new project using the included template.

The first task is to create a new project. In Xcode , click File->New Project. Select a Cocos2d Application and name it “Test Lua”.

New Project.png

Next, right-click the “Targets” item on the left pane. Select “Add->New Target…” Select a “Static Library” and name it “Lua

addtarget.png

Download the Lua source from http://www.lua.org and unzip it to your project directory. The current Lua version is 5.1.4 so my project directory now looks like:

filestructure.png

Right click on the “Test Lua” header in the left pane of the Xcode window. Select “Add->New Group ” and name it “Lua

newgroup.png

Right click on the new “Lua” group and select “Add->Existing Files…” Navigate to your Test Lua/Lua5.1.4/src directory and select all of the .h and .c files except lua.c, luacc.c, and print.c . These are used to build standalone Lua interpreters and compilers and are not used in library builds.

addfiles.png

When presented with the options dialog below, be sure to uncheck “Copy items into destination groups folder” and select only “Lua” under the “Add to Targets” section.

addtotarget.png

You should be able to right-click the Lua target and build it without errors at this point.

Under “Targets,” right-click the “Test Lua” item and select “Get info.” Select the “General” tab at the top.


testluainfo.png

Click the “+” button beneath “Linked Libraries

Select “libLua.a” at the top and click the “Add” button.

lualib.png

Click the “+” button beneath “Direct Dependencies

Select “Lua” and click the “Add Target” button.


addluadep.png

Close the “Target Test Lua Info” window.

You can now clean and build your project and you will see the three build steps happen: your project code, Cocos2d, and Lua. It should build without warnings or errors. If you get linker errors, be sure that you did not include lua.c, luac.c, or print.c from the Lua5.1.4 directory. Since they define a “main” function any builds including them will fail.

Now we can add some code to initialize Lua and run a little code. Open the "HelloWorldScene.m" file under "Classes" and add the following code after the #import "HelloWorldScene.h" line:

extern "C"{

#include "lua.h"

#include "lualib.h"

#include "lauxlib.h"

};

 

 

int run_lua(void)

{

lua_State *l;

l = lua_open();

luaopen_base(l);

 

printf("\nAbout to run Lua code\n");

luaL_loadstring(l, "print(\"Running Lua Code...\")");

lua_pcall(l, 0, LUA_MULTRET, 0);

printf("Lua code done.\n\n");

 

lua_close(l);

 

return 0;

}


Now add the following line at the end of the HelloWorldScene "init" function:


 

run_lua();


Finally, rename the "HelloWorldScene.m" file to "HelloWorldScene.mm". This tells the compiler to treat the file as Objective-C++ so the Lua headers will be imported properly.


Build and run the code in a simulator and open the GDB window by clicking the button while the simulator is running.

gdb.png


You should see the window below with all three lines included, two from C and one from Lua.


debugger.png

That pretty much completes the point of this post- to get a working project with Lua compiling, linking, and executing. Obviously this is not very useful yet since Lua cannot interact with your application yet. Once I test my code more I’ll post a small couple of classes to manage several Lua scripts and help integrate them into your game loop.

One final note, if you are doing a more standard app with UIKit then you might find iPhone Wax to be a better solution. In includes both Lua and a library to let Lua and Objective-C coexist as peers without registering functions in Lua before using them. For games you’d probably prefer the lightweight approach above.

Wednesday
Nov112009

Welcome

Welcome to the new blog for GRZ Mobile, the home of iPhone apps from GRZ Software LLC. I've been working on a new app and have noticed that a lot of the information I've been searching for online is missing or limited. I assume the info ends up being a starting point for people who never go back and post their results. Normally this is what I would do but I'm going to try and defy my naturally lazy nature and post whatever material I come up with that I wish I knew when I started this. I hope others find it useful.

My current project is a game and that it will depend on Cocos2D and Lua. My first game, Aquamatch, was based on Cocos2D and it was a great timesaver. Taking on a new OS (I'm a Windows user), a new language (I'm a C++ programmer), and a new engine was tough but Cocos2D made it far easier than it could have been. For this new game I debated just starting from scratch with C and OpenGL but got hooked on Cocos again after I got a demo running in a few hours.

The new addition for this app will be Lua. I've used Lua before in MeshCAM, my toolpath generator for CNC mills, but that's a very different usage based on a more typical scripting scenario, it is not a central part of the program.

There has been a lot of debate about whether or not Apple will allow apps with scripting languages in the app store. There are many already and none seem to break the main rule of not allowing new code to be added by the user or downloaded at runtime. I don't plan on breaking this rule so I'm going to hope for the best.

Lua is popular in the game industry due to its relative high performance and small footprint. It is quick to integrate in almost any way that a developer wants. This downside of this flexibility is that there is no standard approach so a new project has more options that anyone is likely to want. I recently reread an article in Game Programming Gems 5 where the author lays out a simple system using Lua to script the pace of a level (when and where the bad guys are spawned for example). His approach is a thin wrapper around Lua so I think it's a good place to start. If it works well, I can continue to do more of the game in Lua, and if Apple rejects it then I can replace it with some objective/c/c++ code. My hope is that it will get through approval and that I can continue to build the level of Lua dependency over time.

So the next steps are:

  • Get Lua integrated into the project and compiling.
  • Get a good wrapper that allows a long-running script to define the pace of the game.


I'll try to document this as clearly as possible- hopefully I can save someone else a little pain.