I'm using Lua in a game engine that I'm working on. I'd like to give Lua a way to get and set the position of the entity that the script is sitting on. The script is a component that contains a pointer to the entity that contains it. From Lua, I'd like to be able to type:
print(transform.position.x)
transform.position.x = 10
I've written a getter and setter for position, by I'd like them to be contained under transform.position, and preferably not be getter and setters at all but rather behave more like public members. My current getter and setter looks like this:
int getXPosition(lua_State* L) {
lua_pushnumber(L, Script::currentEntity->get<Transform>().position.x);
return 1;
}
So, how would this be done, if possible at all?
Short answer: it may be done with userdata and metatables.
More details:
Register metatable that will be a "class" for the position object and register "__index" and "__newindex" metamethods for it:
luaL_newmetatable(L, "position_metatable");
lua_pushcfunction(L, &position__index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, &position__newindex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
Write function position__index (it is getter). This is regular C Api function, it takes 2 arguments: the position userdata and a field name. If the field name is "x", the function should return the value of the x field.
Write function position__newindex (it is setter). This is regular C Api function, it takes 3 arguments: the position userdata, field name and a value to write. If the field is "x", teh function should write the value to the x field.
To pass the position object from native code to script, create userdata and set the "position_metatable" as a metatable for the userdata, see 28.2 – Metatables for example.
Access to the field as transform.position.x.
Related
I'm trying to rename some functions in Lua so I can call them more easily.
For example, of.getHours() function should be able to be called by ofGetHours().
So after initializing the Lua state, I loaded a Lua string which assigns these global function variables like the following:
luaL_dostring(L, "ofGetHours = of.getHours ofGetMinutes = of.getMinutes");
This method works fine but I would like to do this using the Lua C API to increase the performance and make my code more readable.
I think I need to use lua_setglobal() but I don't know how to properly do it. Any help would be greatly appreciated.
Provided the os value is a global table, your code might look something like this:
lua_getglobal(L, "of");
lua_getfield(L, -1, "getMinutes");
lua_setglobal(L, "ofGetMinutes");
lua_getfield(L, -1, "getHours");
lua_setglobal(L, "ofGetHours");
lua_pop(L, 1);
How can I make a lua table in C++ map to a lua script where the script defines the table properties and methods, so the resulting lua file looks something like:
Script.test = 5
function Script:Update()
end
So this script could be read in many times but each time it should get its own unique table in C++.
The idea is that from C++ I want to call each unique Update() method and be able to read/set its test variable.
Let's say you are making a video game and I told you if you make scripts structured like this and attach them to models that the game will run the Update() method every frame and that the variables you define are local to that script/model.
This answer is just to elaborate on my comment above. One simple idea to achieve what you're describing is to just create that local table right in the script file and return that table at the end.
local Script = {}
Script.test = 5
Script.foo = "bar"
function Script:Update()
end
return Script
This is the usual approach taken to put lua modules into its own namespace and to avoid global scope pollution.
You can grab and use the returned Script table off the stack with something like this:
// load and execute your script
luaL_dofile(L, "script.lua");
// Run Script:Update()
lua_getfield(L, -1, "Update");
lua_pushvalue(L, -2);
lua_call(L, 1, 0);
You can wrap the above code snippet into a function to make it easier to call as many times as desired. You can even make it more efficient by caching the compiled script.lua somewhere to avoid reparsing and recreating the wrapping function everytime this function is called. For example:
lua_getfield(L, LUA_REGISTRYINDEX, "cached_script");
if (lua_isnil(L, -1))
{
luaL_loadfile(L, "script.lua");
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "cached_script");
}
lua_pcall(L, 0, 0, 0);
// etc.
I'm working on improving the way we handle Lua scripting for robot players in Bitfighter. Currently, each robot gets its own L instance, and we're trying to get them to all share one by swapping out environment tables. Note that bots may be completely different scripts.
I realize that this method is deprecated in Lua 5.2, but we are currently using lua-vec, which still uses Lua 5.1. The game is written in C++.
So...
First we create an environment, and call it :
// Create a table with room for 0 array and 1 non-array elements
lua_createtable(L, 0, 1); // -- tab
// Set the globals table to handle any requests that the
// script's environment can't
lua_pushstring(L, "__index"); // -- tab, "__index"
lua_pushvalue(L, LUA_GLOBALSINDEX); // -- tab, "__index", _G
// Set table["__index"] = _G, pops top two items from stack
lua_settable(L, -3); // -- tab
// Store the new table in the retistry for future use
lua_setfield(L, LUA_REGISTRYINDEX, name); // -- <<empty stack>>
Later, we load some Lua code, and recall the environment table:
luaL_loadfile(L, "luascripts.lua");
lua_getfield(L, LUA_REGISTRYINDEX, name); // -- function, table
lua_setfenv(L, -2); // -- function
Then run the loaded code:
lua_pcall(L, 0, 0, 0);
When the loaded Lua tries to use a basic function, such as print, it fails with the error:
attempt to call global 'print' (a nil value)
But, the script can do the following:
__index["print"](12)
So... why can't we access print directly? What are we missing? Or is there a fundamentally better way to run multiple scripts in the same Lua instance?
Your code is close to correct, but contains several issues - You are attempting to do something that won't work, and your attempt did the wrong thing in the wrong way..
You are setting the function environment of the function to a table which looks like this:
{__index = _G}
Naturally, when you attempt to access print, it is not found in this table.
From your comments, I infer that actually wanted to set the __index field of the metatable of the environment table. That is, you wanted to make the environment tables be like t in the example below:
t = {}
setmetatable(t, {__index = _G})
(The C++ translation of this is fairly straightforward).
Don't do this. It will solve your immediate problem, but it will not provide sufficient sandboxing. Consider for example a script like this:
table.sort = 10
"table" will be found by in _G by the metatable event handler. sort is just an element of the table table, and so can be replaced with impunity. Now, other scripts will be unable to sort tables with table.sort.
One way to perform this kind of separation is to mediate access to all the global values through some sort of (possibly recursive) userdata with hand-written handlers for the relevant metatable events. (This way probably has the most potential for performance and control, but could be difficult to implement).
Another way is to create an environment table for for each script, and to copy the safe/sandboxed elements from the global table into this environment table (so that each script has a totally separate version of all the mutable elements of the global table).
I'm sorry I don't have time to properly explain my suggested solutions to your problem. I hope that I have given you somewhere to start. Feel free to come back and edit this to include the solution that you ultimately use.
I am working on a game engine in C++ using Lua to define NPCs.
I can define a prototypic NPC like this:
orc =
{
name = "Generic Orc",
health = 100
}
function orc:onIdle()
print("Orc idles...")
end
and then spawn an instance of "Orc" with entitySpawn(orc). This is a C++ function that reads values like health and name from the given table, creates an Entity object in C++ with the given values and in addition creates a Lua table for the specific NPC.
Now, I would like to have a direct connection between the orc.health variable in Lua and the mHealth member variable of the corresponding Entity object in C++, so I could assign a value in Lua and instantly use it in C++ and vice versa.
Is this even possible? Or do I have to make use of setter / getter functions? I have taken a look at light userdata and got to the point of storing a pointer to the C++ variable in Lua, but could not assign a value.
This is possible. I'm assuming that entitySpawn returns the table that C++ created for the entity. I'll also assume that you can expose a function from C++ that takes an entity's table and returns the current health, and similarly for setting. (You can use a light userdata pointer to the C++ object as a member of this table to implement this.)
So the ugly way would look like this:
local myOrc = entitySpawn(orc)
local curHealth = getEntityHealth(myOrc)
setEntityHealth(myOrc, curHealth + 10)
To make this prettier, we can have some fun with metatables. First, we'll put the accessors for all the properties we care about.
entityGetters = {
health = getEntityHealth,
-- ...
}
entitySetters = {
health = setEntityHealth,
-- ...
}
Then we'll create a metatable that uses these functions to handle property assignments.
entityMetatable = {}
function entityMetatable:__index(key)
local getter = entityGetters[key]
if getter then
return getter(self)
else
return nil
end
end
function entityMetable:__newindex(key, value)
local setter = entitySetters[key]
if setter then
return setter(self, value)
end
end
Now you need to make entitySpawn assign the metatable. You would do this with the lua_setmetatable function.
int entitySpawn(lua_State* L)
{
// ...
// assuming that the returned table is on top of the stack
lua_getglobal(L, "entityMetatable");
lua_setmetatable(L, -2);
return 1;
}
Now you can write it the nice way:
local myOrc = entitySpawn(orc)
myOrc.health = myOrc.health + 10
Note that this requires that the table that is returned by entitySpawn does not have a health property set. If it does, then the metatable will never be consulted for that property.
You can create the entityGetters, entitySetters, and entityMetatable tables, as well the __index and __newindex metamethods, in C++ instead of Lua if that feels cleaner to you, but the general idea is the same.
I don't have time to give you any code, but keep in mind orc.health is the same as orc["health"]; that is, orc is a table and "health" is an element.
With that in mind, you can change the index and newindex metamethods of your table to have your own specific behavior. Store your "real" orc instance as some private metadata, then use it to update.
In my C++ app, I have a string which contains XML data. Let's say I have an attribute Number1 and an attribute Number2.
I want to send that string to a Lua script and have it return a modified XML string. Let's say it adds a new attribute Product with the value of Number1 and Number2.
Is that easily done using C++/Lua, and if so how?
There are several methods to work with XML data listed at the Lua Users Wiki. The better options involve calls back to C (e.g. LuaXML and LuaExpat) so this only makes sense to do if there are other reasons to use Lua beyond just parsing XML.
Not a Lua user myself...but just browsing the documentation, it seems that you can use lua_pushstring() to put a copy of a null-terminated C string into the Lua stack:
http://pgl.yoyo.org/luai/i/lua_pushstring
Although there is no specific definition for things like lua_popstring() you can define something like that yourself:
std::string lua_popstring(lua_State *L)
{
std::string tmp = lua_tostring(L, lua_gettop(L));
lua_pop(L, 1);
return tmp;
}
With that in hand you should be able to modify the standard example for passing data into Lua and getting a result back for your purpose:
http://lua-users.org/wiki/SimpleLuaApiExample
You could do it similar to this (not this might not be 100 % correct due to being out of my mind and it doesn't include error handling):
lua_getglobal(L, "modifyXml"); // push function on stack by name
lua_pushstring(L, xml); // push the xml string as parameter
lua_pcall(L, 1, 1, 0); // call the function with 1 parameter, 1 return value and no error handler
strcpy(xml, lua_tostring(L, -1)); // get the top of the stack as a string and copy it to xml
lua_pop(xml, 1); // remove the string from the stack
The lua function called could look like this:
function modifyXml(xml)
-- do something with xml here
return xml
end
If you use Luabind it could look something like this in C++:
std::string result = luabind::call_function<std::string>(
"yourLuaFunction", inputXmlString);
You would implement yourLuaFunction in Lua of course, and require that Lua module from within your C++ program.