• entries
    206
  • comments
    217
  • views
    224469

RPG Anvil: The Lua Tutorial Part 6

Sign in to follow this  

584 views



Today, how to handle errors in your script-called functions.

The previous installments of this tutorial are here:



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 debuggging, 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 simple 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:

        if( pData->m_iLockpickLevel < 0 )
{
throw( std::exception( "logic error" ) );
}


In the tutorial from Part 4, 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:

    try
{
...
}
catch( std::exception const &e )
{
lua_pushstring( luaState, e.what() );
lua_error( luaState );
}


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.

Here's the final tutorial code:

// luatest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include
#include

class CGameData
{
public:
CGameData( int i ) { m_iLockpickLevel = i; }
int m_iLockpickLevel;
};

char *szLua =
"function game_door_check( context ) "
" local x = GetLockpickLevel( context ) "
" return ( x > 7 ) "
"end ";

int lua_GetLockpickLevel( lua_State *luaState )
{
try
{
int nArgs = lua_gettop( luaState );
if( nArgs != 1 )
{
throw( std::exception( "Script Error 1" ) );
}
CGameData *pData = (CGameData *) lua_touserdata( luaState, 1 );
if( !pData )
{
throw( std::exception( "Script Error 2" ) );
}
if( pData->m_iLockpickLevel < 0 )
{
throw( std::exception( "logic error" ) );
}
lua_pushinteger( luaState, pData->m_iLockpickLevel );
}
catch( std::exception const &e )
{
lua_pushstring( luaState, e.what() );
lua_error( luaState );
}
return( 1 );
}

int _tmain(int argc, _TCHAR* argv[])
{
lua_State *lState = NULL;

try
{
lState = luaL_newstate();
luaL_openlibs( lState );

lua_register( lState, "GetLockpickLevel", lua_GetLockpickLevel );

int iStatus = luaL_loadstring( lState, szLua );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

iStatus = lua_pcall( lState, 0, 0, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

CGameData Data( 8 );
lua_getfield( lState, LUA_GLOBALSINDEX, "game_door_check" );
lua_pushlightuserdata( lState, (void *) &Data );
iStatus = lua_pcall( lState, 1, 1, 0 );
if( iStatus )
{
std::cout << "Error: " << lua_tostring( lState, -1 );
throw( std::exception( "script error" ) );
}

int iRet = (int) lua_toboolean( lState, -1 );
if( iRet )
{
std::cout << "Door opened!" << std::endl;
}
else
{
std::cout << "Door still closed." << std::endl;
}
}
catch( std::exception const &e )
{
std::cout << e.what() << std::endl;
}

if( lState )
{
lua_close( lState );
}

return 0;
}


OK, at this point, the tutorial is done. We are:

  • Linking and initializing Lua.

  • Loading a Lua script from a string.

  • Running a Lua script from our C++ code.

  • Calling a C++ function from Lua.

  • Accessing our C++ game data in Lua.

  • Properly handling scripting errors.


Hopefully this has been helpful! I'll have some final thoughts in the next post.
Sign in to follow this  


1 Comment


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now