iOS - Calling Lua from my .mm module ?

Hi,

I am stuck with one problem. I’m currently implementing IAP with the server-side validation. I could purchase successfully and got all informations needed (transationId, receipt,…). Now I would like to pass all of this to my Objective-C module, so it can send securely information to the server for validation. When I say securely, I mean put it in a FIFO if the network was cut, and queue all requests to send it later.

What I’m wondering is, when my FIFO has got a response (OK/KO), how can I call a Lua function ? I’ve seen in the doc (http://docs.coronalabs.com/native/enterprise/ios/index.html#TOC) that Runtime events are the best way to do. The manual proposes the following :

*- (void)didLoadMain:(id)runtime {
lua_State L = runtime.L;
… // put values in that stack (L) then call RuntimeEvent
}

… But this (id)runtime object is only accessible from my CoronaDelegate delegate, not from my Lua modules. How can I do to access the runtime object ? And is this runtime object the same from the launch to the death of my application ? [import]uid: 188126 topic_id: 32964 reply_id: 332964[/import]

When initializing your module you can store the lua_State* locally within the module.
The lua_State* object is the core for all accesses between Lua and C/ObjC/C++. While it can’t be guaranteed to persist as long as you reference it, since Corona can shut it down when it’s done, it’s very difficult to get into a situation where Corona has shut down it’s Lua environment and you are still running stuff with it.

Once you have that Lua_State available, you have a few solutions: Callback, Global Event, Local Event(Like Runtime:DispatchEvent, but to a specific object in the _G table). I prefer the Callback registration method. However, since the C-side Lua library doesn’t have a structure for Lua Function (Only Lua Function declared in C), Storing specific callbacks can take a little more than expected. Here is a quick example of registering a Lua-side callback for use with a C-side function.

Obj C

static lua\_State\* state; //This is all static because I am very lazy  
static int callbackIndex = 0;  
static int errorIndex = 0;  
  
//Call this to register functions for callbacks  
int  
FakeModule::registerCallback(lua\_State\* L)  
{  
 if (errorIndex \> 0)  
 {  
 luaL\_unref(L, LUA\_REGISTRYINDEX, errorIndex);  
 }  
 errorIndex = luaL\_ref(L, LUA\_REGISTRYINDEX);  
  
 if (callbackIndex \> 0)  
 {  
 luaL\_unref(L, LUA\_REGISTRYINDEX, callbackIndex);  
 }  
 callbackIndex = luaL\_ref(L, LUA\_REGISTRYINDEX);  
 return 0;  
}  
  
//This is where you recieve an ObjC callback and forward it into LUA (via your registered Callback)  
void  
FakeModule::callbackFunc(NSString name, int index)  
{  
 if(callbackIndex \> 0)  
 {  
 lua\_rawgeti(state, LUA\_REGISTRYINDEX, errorIndex);  
 lua\_rawgeti(state, LUA\_REGISTRYINDEX, callbackIndex);  
 lua\_pushstring(state, [name UTF8String]);  
 lua\_pushinteger(state, index);  
 lua\_pcall(state, 2, 0, -4); --Calls callback function with error function as error handler  
 }  
}  
  
const char \*  
FakeModule::Name()  
{  
 static const char sName[] = "MyModule";  
 return sName;  
}  
  
int  
FakeModule::Open( lua\_State \*L )  
{  
 state = L;  
  
 const luaL\_Reg kVTable[] =  
 {  
 { "registerCallback", registerCallback },  
  
 { NULL, NULL }  
 };  
  
 // Ensure upvalue is available to library  
 void \*context = lua\_touserdata( L, lua\_upvalueindex( 1 ) );  
 lua\_pushlightuserdata( L, context );  
  
 luaL\_openlib( L, Name(), kVTable, 1 );  
  
 return 1;  
}  

Lua

local file = {}  
  
local function onError(errorStr)  
 print("ERROR IN PLUGIN", errorStr)  
end  
  
local function onCallback(name, index)  
 print("Called Back", name, index)  
end  
  
--This is a neat trick for wrapping plugins so that they silently fail in Simulator.  
--It allows you to attempt to load the module, but if it's not there, simply don't forward calls  
local plugin = nil  
local pluginFunc = package.preload["MyModule"]  
if pluginFunc then  
 plugin = pluginFunc()  
 plugin.registerCallback(onCallback, onError) --Registers your Callbacks with the C-side  
end  
  
--Add functions here as   
--function file.pluginFunction(parameters)  
-- if plugin then plugin.pluginFunction(parameters) end  
--end  
--To allow for easy disabling of plugin detection for Simulator builds (and if you port to Android you just need to load the Android plugin module and POW it works  
  
return file  

The Lua_Ref and RegisteryIndex may be new, but essentially Lua_Ref/Lua_Unref adds the variable at the top of the stack to a specific table index. It takes the lowest available integer index, and returns you that index. RegistryIndex is a Lua table that is only accessible from the C-side library. It allows you to store Lua values in a Garbage-collection safe way on C-side, that is not just sitting around in _G.

As well, if you store the lua_state you can setup the proper stack variables to call RuntimeDispatchEvent.

This is written pretty hastily, let me know if you have more questions.

ADDED: Also if you need to process a callback response, you should be able to pull it directly from the lua_State after pcall (using Lua’s to_[Type] functions). [import]uid: 134101 topic_id: 32964 reply_id: 130938[/import]

When initializing your module you can store the lua_State* locally within the module.
The lua_State* object is the core for all accesses between Lua and C/ObjC/C++. While it can’t be guaranteed to persist as long as you reference it, since Corona can shut it down when it’s done, it’s very difficult to get into a situation where Corona has shut down it’s Lua environment and you are still running stuff with it.

Once you have that Lua_State available, you have a few solutions: Callback, Global Event, Local Event(Like Runtime:DispatchEvent, but to a specific object in the _G table). I prefer the Callback registration method. However, since the C-side Lua library doesn’t have a structure for Lua Function (Only Lua Function declared in C), Storing specific callbacks can take a little more than expected. Here is a quick example of registering a Lua-side callback for use with a C-side function.

Obj C

static lua\_State\* state; //This is all static because I am very lazy  
static int callbackIndex = 0;  
static int errorIndex = 0;  
  
//Call this to register functions for callbacks  
int  
FakeModule::registerCallback(lua\_State\* L)  
{  
 if (errorIndex \> 0)  
 {  
 luaL\_unref(L, LUA\_REGISTRYINDEX, errorIndex);  
 }  
 errorIndex = luaL\_ref(L, LUA\_REGISTRYINDEX);  
  
 if (callbackIndex \> 0)  
 {  
 luaL\_unref(L, LUA\_REGISTRYINDEX, callbackIndex);  
 }  
 callbackIndex = luaL\_ref(L, LUA\_REGISTRYINDEX);  
 return 0;  
}  
  
//This is where you recieve an ObjC callback and forward it into LUA (via your registered Callback)  
void  
FakeModule::callbackFunc(NSString name, int index)  
{  
 if(callbackIndex \> 0)  
 {  
 lua\_rawgeti(state, LUA\_REGISTRYINDEX, errorIndex);  
 lua\_rawgeti(state, LUA\_REGISTRYINDEX, callbackIndex);  
 lua\_pushstring(state, [name UTF8String]);  
 lua\_pushinteger(state, index);  
 lua\_pcall(state, 2, 0, -4); --Calls callback function with error function as error handler  
 }  
}  
  
const char \*  
FakeModule::Name()  
{  
 static const char sName[] = "MyModule";  
 return sName;  
}  
  
int  
FakeModule::Open( lua\_State \*L )  
{  
 state = L;  
  
 const luaL\_Reg kVTable[] =  
 {  
 { "registerCallback", registerCallback },  
  
 { NULL, NULL }  
 };  
  
 // Ensure upvalue is available to library  
 void \*context = lua\_touserdata( L, lua\_upvalueindex( 1 ) );  
 lua\_pushlightuserdata( L, context );  
  
 luaL\_openlib( L, Name(), kVTable, 1 );  
  
 return 1;  
}  

Lua

local file = {}  
  
local function onError(errorStr)  
 print("ERROR IN PLUGIN", errorStr)  
end  
  
local function onCallback(name, index)  
 print("Called Back", name, index)  
end  
  
--This is a neat trick for wrapping plugins so that they silently fail in Simulator.  
--It allows you to attempt to load the module, but if it's not there, simply don't forward calls  
local plugin = nil  
local pluginFunc = package.preload["MyModule"]  
if pluginFunc then  
 plugin = pluginFunc()  
 plugin.registerCallback(onCallback, onError) --Registers your Callbacks with the C-side  
end  
  
--Add functions here as   
--function file.pluginFunction(parameters)  
-- if plugin then plugin.pluginFunction(parameters) end  
--end  
--To allow for easy disabling of plugin detection for Simulator builds (and if you port to Android you just need to load the Android plugin module and POW it works  
  
return file  

The Lua_Ref and RegisteryIndex may be new, but essentially Lua_Ref/Lua_Unref adds the variable at the top of the stack to a specific table index. It takes the lowest available integer index, and returns you that index. RegistryIndex is a Lua table that is only accessible from the C-side library. It allows you to store Lua values in a Garbage-collection safe way on C-side, that is not just sitting around in _G.

As well, if you store the lua_state you can setup the proper stack variables to call RuntimeDispatchEvent.

This is written pretty hastily, let me know if you have more questions.

ADDED: Also if you need to process a callback response, you should be able to pull it directly from the lua_State after pcall (using Lua’s to_[Type] functions). [import]uid: 134101 topic_id: 32964 reply_id: 130938[/import]

Hi Ntero, I really thank you for such a complete answer.

I indeed didn’t know about lua_ref/unref and RegisteryIndex, and that’s quite a good way not to pollute lua’s _G table. I tried your solution, it’s clean and everything works perfectly. I have a couple of questions :

  • Where does the “-4” comes in the lua_pcall call ? The error function will always be at the -4 index of the stack ?
  • Is the errorStr argument from the lua function onError automatically given by lua_pcall?
  • Is this solution really safe ? What happen if we have a phone call, memory warning, close application, all that stuff ? Do we still keep the reference to the lua_state? Because I have other Obj-C classes which will call functions from this module, is there some use cases where everything will mess up ?
  • Is the lua_state common for all modules or are there as many lua_state as modules ?
  • Typically, when is a lua_state closed ?

That’s many questions, sorry :).

PS: I had a dirtier solution for checking if we have access to a module, I was doing : local libraryAvailable, err = pcall (require, “InAppPurchaseLibrary”) and then check the value of libraryAvailable. But I prefer your solution :). [import]uid: 188126 topic_id: 32964 reply_id: 131043[/import]

Hi Ntero, I really thank you for such a complete answer.

I indeed didn’t know about lua_ref/unref and RegisteryIndex, and that’s quite a good way not to pollute lua’s _G table. I tried your solution, it’s clean and everything works perfectly. I have a couple of questions :

  • Where does the “-4” comes in the lua_pcall call ? The error function will always be at the -4 index of the stack ?
  • Is the errorStr argument from the lua function onError automatically given by lua_pcall?
  • Is this solution really safe ? What happen if we have a phone call, memory warning, close application, all that stuff ? Do we still keep the reference to the lua_state? Because I have other Obj-C classes which will call functions from this module, is there some use cases where everything will mess up ?
  • Is the lua_state common for all modules or are there as many lua_state as modules ?
  • Typically, when is a lua_state closed ?

That’s many questions, sorry :).

PS: I had a dirtier solution for checking if we have access to a module, I was doing : local libraryAvailable, err = pcall (require, “InAppPurchaseLibrary”) and then check the value of libraryAvailable. But I prefer your solution :). [import]uid: 188126 topic_id: 32964 reply_id: 131043[/import]

I can give these a shot:

  1. -4 comes from the index of the error function on the Lua Stack. pcall has the signature (# of Parameters, # of return values, Index on stack of error function). Because things are stacked on as: error function, callback function, parameters, this means that your error callback index is always 2 more than the number of parameters. Using a negative number allows you to calculate from the top down, rather than the bottom up.
    2)The ErrorStr is created internally by LUA, as an error such as “Filename.lua: line 204: Attemtping to index upvalue “?” (value is nil” or other common errors. You can generate your own with _G.error(string) as well that will get caught by the error handler.
    3)The Lua_State holds the entirety of Lua inside it. It’s the state of the entire Lua environment, and so Corona generally only has one(You can change this, but it would probably get quite complex/messy). It also persists from App Start, right up until App exit. Since iOS is predominantly multitasking aware, and you can’t exit an active app, it’s been quite reliable so far, and I’ve never seen it go stale. On Android it’s a bit different, however the RuntimeTaskDispatcher, helps you maintain Lua_State much more easily on that platform anyway. One other thing if you want multithreading, is that the Lua_State CAN be thread-safe, but since Corona doesn’t do locking on their end, it means that you generally want to access Lua from one and only one thread.
    4)It’s a singular State for all modules.

Edit: Reading this over, It definitely shows that I wrote this before my coffee, I’ll clean this up shortly (when I’m less busy) [import]uid: 134101 topic_id: 32964 reply_id: 131077[/import]

I can give these a shot:

  1. -4 comes from the index of the error function on the Lua Stack. pcall has the signature (# of Parameters, # of return values, Index on stack of error function). Because things are stacked on as: error function, callback function, parameters, this means that your error callback index is always 2 more than the number of parameters. Using a negative number allows you to calculate from the top down, rather than the bottom up.
    2)The ErrorStr is created internally by LUA, as an error such as “Filename.lua: line 204: Attemtping to index upvalue “?” (value is nil” or other common errors. You can generate your own with _G.error(string) as well that will get caught by the error handler.
    3)The Lua_State holds the entirety of Lua inside it. It’s the state of the entire Lua environment, and so Corona generally only has one(You can change this, but it would probably get quite complex/messy). It also persists from App Start, right up until App exit. Since iOS is predominantly multitasking aware, and you can’t exit an active app, it’s been quite reliable so far, and I’ve never seen it go stale. On Android it’s a bit different, however the RuntimeTaskDispatcher, helps you maintain Lua_State much more easily on that platform anyway. One other thing if you want multithreading, is that the Lua_State CAN be thread-safe, but since Corona doesn’t do locking on their end, it means that you generally want to access Lua from one and only one thread.
    4)It’s a singular State for all modules.

Edit: Reading this over, It definitely shows that I wrote this before my coffee, I’ll clean this up shortly (when I’m less busy) [import]uid: 134101 topic_id: 32964 reply_id: 131077[/import]

Even before your coffee, you’re still completely clear and a born teacher :). Thanks a lot Ntero ! [import]uid: 188126 topic_id: 32964 reply_id: 131208[/import]

Even before your coffee, you’re still completely clear and a born teacher :). Thanks a lot Ntero ! [import]uid: 188126 topic_id: 32964 reply_id: 131208[/import]