This sample demonstrates communication between an embedded in the game Lua virtual machine and the JavaScript engine in Coherent GT. The communication is in both directions and allows transparent converting between Lua and JavaScript data types.
The sample solution is based on a minimal game framework that provides basic functionality. The sample is already configured and you only have to compile and run it.
The output will be in the Coherent/bin directory.
This sample builds upon the HelloGT sample and assumes that you understand it.
The sample shows a command-prompt for entering a name of a function defined in the Lua universe as well as arguments (separated by space) for the function. When the Run button is pressed, the Lua function is executed and its result is displayed in the output window.
When the view is ready for the registration of C++ call and event handlers Coherent::UIGT::ViewListener::OnReadyForBindings is called
virtual void OnReadyForBindings() override{m_View->BindCall("lua", new LuaEventHandler(m_View, m_Lua));m_View->BindCall("dispatch", Coherent::UIGT::MakeHandler(&LuaCallback));}
This allows Lua functions to be called using ‘engine.call('lua’, ...)and for convenience the sample adds
engine.lua` to be a shortcut for the above.
The HTML and JavaScript code for this sample is in Coherent/Samples/Sample_LuaBinding/uiresources/lua.html.
The Lua functions used by this example are in Coherent/Samples/Sample_LuaBinding/uiresources/game.lua
When the user clicks on the button 'Run' the input line is split by space, and `engine.lua(name, argument1, argument2, ...) is called.
This will call the LuaEventHandler::Invoke
method with the specified arguments. The method does all the steps for executing the Lua function and returning the result:
ReadLuaValue
functionReadLuaValue
peeks the type of the current argument and converts it to the corresponding Lua value, leaving the new value on top of the stack.
void ReadLuaValue(Coherent::UIGT::Binder* binder, lua_State* lua){switch (binder->PeekValueType()){case Coherent::UIGT::VT_Null:lua_pushnil(lua);break;case Coherent::UIGT::VT_Boolean:{bool jsValue = false;binder->Read(jsValue);lua_pushboolean(lua, jsValue ? 1 : 0);}break;case Coherent::UIGT::VT_String:{const char* str = nullptr;size_t length = 0;binder->Read(str, length);std::string nullterminated(str, length);lua_pushstring(lua, nullterminated.c_str());}break;...}}
To return the result back to JavaScript, the following snippet is used.
auto resultBinder = binder->ResultBegin();ToJavaScript(m_Lua, lua_gettop(m_Lua), resultBinder);binder->ResultEnd();
It will set the Lua value on top of the stack as the result of the call.
The ToJavaScript
function is the reverse of ReadLuaValue
- it takes the Lua value at specified index of the stack and exposes it to JavaScript using the Coherent::UIGT::Binder methods.
{switch (lua_type(lua, index)){case LUA_TNIL:binder->BindNull();break;case LUA_TBOOLEAN:binder->Bind(lua_toboolean(lua, index) != 0);break;case LUA_TNUMBER:binder->Bind(lua_tonumber(lua, index));break;case LUA_TSTRING:{binder->Bind(lua_tostring(lua, index));}break;...}}
In the case of an error during the execution of the Lua function the JavaScript promise object is rejected using the Coherent::UIGT::View::SetScriptError method.
The LuaEventHandler
in the sample is directly using the Lua stack-based APIs. It is possible to use Coherent GT with higher level abstractions for Lua, such as luabind, LuaBridge or any in-house made APIs. The sample shows that with a very minimal abstraction for Lua values - LuaValue
and the LuaCallback
function which is exposed to Coherent GT.
In order for LuaValue
s to be sent to and received from JavaScript, Coherent GT has to know how to bind them. The following overloads allow Coherent GT to bind Lua values.
void CoherentReadInternal(Coherent::UIGT::Binder* binder, LuaValue& value){value.State = g_LuaState;ReadLuaValue(binder, value.State);value.Reference = luaL_ref(value.State, LUA_REGISTRYINDEX);}{lua_rawgeti(value.State, LUA_REGISTRYINDEX, value.Reference);ToJavaScript(value.State, lua_gettop(value.State), binder);lua_pop(value.State, 1);}
This allows the LuaCallback
function to be exposed to Coherent GT as any other C++ function.