How to directly map a Lua variable to a C++ variable? - c++

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.

Related

How to add namespaces to Lua?

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.

Can't call a sub class function, in C++

What I was doing before was that I was calling a function of my interface and it determinate in a switch condition thanks to a parameter what to do with the data. What kind of specialization they have.
But now, what I am trying to create a local object, treat it, and then add it to my containers of the interface.
In order to do that I have to copy all the value of my local object (which have been treated) in my container of the interface.
So I created a copy_cell function in the interface, a virtual one, and one in the subclass. But whenever I try to do it the interface function is called and not the subfunction.
GridCell_voxel * local_cell;
local_cell = new GridCell_voxel(m_grid_map( cell2matindex_x(cell_index_x), cell2matindex_y(cell_index_y))->getVoxelResolution(), m_grid_map( cell2matindex_x(cell_index_x), cell2matindex_y(cell_index_y))->getVoxel().size());
local_cell->process_points(relevant_points, m_mapping_type);
//This is the line I need to change
local_cell->copy_cell (m_grid_map( cell2matindex_x( cell_index_x), cell2matindex_y( cell_index_y))) ;
Do you have any idea on the way to go? What am I missing here?
Sorry for the lack of information, i will try to expose how i managed and what i was actually looking for.
So i have a container of IntefaceCell, called m_grid_map, full of cell that has been specialized. In my case m_grid_map is full of GridCell_voxel which is a sub class from InterfaceCell.
What i want to do is, create a new local GridCell_voxel, copy the information in it. Then process the informations, then copy the local cell in the container.
The important part is the dynamic_cast in the copy cell function, which allow you to take an InterfaceCell as argument and then treat it as GridCell_voxel.
//Main.cpp
GridCell_voxel * local_cell;
local_cell = new GridCell_voxel();
local_cell->copy_cell (m_grid_map( cell2matindex_x( cell_index_x), cell2matindex_y( cell_index_y)));
local_cell->process_points(relevant_points, m_mapping_type);
m_grid_map( cell2matindex_x( cell_index_x), cell2matindex_y( cell_index_y))->copy_cell (local_cell);
delete local_cell;
//GridCell_voxel.cpp
void GridCell_voxel::copy_cell(GridCellInterface* cell) {
GridCell_voxel* voxel_cell = dynamic_cast<GridCell_voxel*>(cell);
this->m_voxel_start_height = voxel_cell->m_voxel_start_height;
this->init = true;
this->m_ground_voxel_position = voxel_cell->m_ground_voxel_position;
}
I hope it will help someone.

v8::ObjectTemplate::SetAccessor and v8::Template::Set - Difference

I'm confused by the difference between V8 ObjectTemplate's Set and SetAccessor methods. My question has a simple context and 4 concrete sub-questions.
Context
Let's say I have code snippet that wants to provide a global object global to the targeting JS context. global has a property x, whose value takes int value. global also has a property log, which is a function. All the snippets are taken from V8's source, process.cc and Embedder's Guide to be precise.
HandleScope handle_scope(GetIsolate());
// Create a template for the global object where we set the
// built-in global functions.
Local<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
global->Set(String::NewFromUtf8(GetIsolate(), "log",
NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(GetIsolate(), LogCallback));
So this code snippet provides function log to the global. Then from the Embedder's Guide to accessors, it says
An accessor is a C++ callback that calculates and returns a value when an object property is accessed by a JavaScript script. Accessors are configured through an object template, using the SetAccessor method.
The code snippet follows:
void XGetter(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
info.GetReturnValue().Set(x);
}
void XSetter(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<Value>& info) {
x = value->Int32Value();
}
// YGetter/YSetter are so similar they are omitted for brevity
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "x"), XGetter, XSetter);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "y"), YGetter, YSetter);
Persistent<Context> context = Context::New(isolate, NULL, global_templ);
As I understand this code snippet, it's providing some integer value x to the global as the description goes.
Now,from the source of V8, I see ObjectTemplate doesn't have a Set method, instead, it's inherited from parent class Template. From Template's source code, it says:
/**
* Adds a property to each instance created by this template.
*
* The property must be defined either as a primitive value, or a template.
*/
void Set(Local<Name> name, Local<Data> value,
propertyAttribute attributes = None);
Questions
Template's Set method says it can set a primitive value to the instance of the template, then can I use Set to set x in the second code snippet instead of using SetAccessor?
If the answer to question 1 is true, then what's the difference for setting x between using SetMethod and Set? Is the difference being that any modification in JS to the property set by Set will not be reflected in C++?
If the answer to question 1 is false, then why can't I use Set on X?
From the description of accessors, it says it computes and return value. So does it mean we don't use SetAccessor to return functions? I'm confused because I mainly write JS and Haskell. Both languages spoils me to take functions as values.
Now I know it should be easy to verify all my assumptions by actually building the samples, but I have difficulties compiling the V8 source, hence I'm asking for any help.
Thank you in advanced for any effort!
1. Yes.
2. Set is the C++ equivalent (modulo property attributes) of:
Object.defineProperty(global, "x", {value: 3})
SetAccessor is the C++ equivalent of:
Object.defineProperty(global, "x", {get: function XGetter() { return ...; },
set: function XSetter(val) { ... }});
As you suggest, a consequence is that in case of Set, the C++ side has no way of knowing whether the value was changed from the JavaScript side.
3. n/a
4. The getter can return any value you want; in particular the value can be a function.

Lua, metatables, and global variables

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.

How can a script retain its values through different script loading in Lua?

My current problem is that I have several enemies share the same A.I. script, and one other object that does something different. The function in the script is called AILogic. I want these enemies to move independently, but this is proving to be an issue. Here is what I've tried.
1) Calling dofile in the enemy's constructor, and then calling its script function in its Update function which happens in every game loop. The problem with this is that Lua just uses the script of the last enemy constructed, so all of the enemies are running the same script in the Update function. Thus, the object I described above that doesn't use the same script for it's A.I. is using the other enemies' script.
2) Calling dofile in the Update function, and then calling its script function immediately after. The problem with this is that dofile is called in every object's update function, so after the AILogic function runs and data for that script is updated, the whole thing just gets reset when dofile is called again for another enemy. My biggest question here is whether there is some way to retain the values in the script, even when I switch to running a different one.
I've read about function environments in Lua, but I'm not quite sure how to implement them correctly. Is this the right direction? Any advice is appreciated, thanks.
Edit: I've also considered creating a separate place to store that data rather than doing it in the Lua script.
Edit2: Added some sample code. (Just a test to get the functionality working).
-- Slime's Script
local count = 0;
function AILogic( Slime )
--Make the slime move in circles(err a square)
if count < 4 then
Slime:MoveDir( 0 );
elseif count < 8 then
Slime:MoveDir( 2 );
elseif count < 12 then
Slime:MoveDir( 1 );
elseif count < 16 then
Slime:MoveDir( 3 );
else
count = 0;
end
count = count + 1;
end
The lua interpreter runs each line as its own chunk which means that locals have line scope, so the example code can't be run as-is. It either needs to be run all at once (no line breaks), without locals, or run in a do ... end block.
As to the question in the OP. If you want to share the exact same function (that is the same function at runtime) then the function needs to take the data as arguments. If, however, you are ok with using the same code but different (runtime) functions than you can use closures to hold the local/individual data.
local function make_counter()
local count = 0
return function ()
local c = count
count = count + 1
return c
end
end
c1 = make_counter()
c2 = make_counter()
c3 = make_counter()
print(c1())
print(c1())
print(c1())
print(c1())
print(c2())
print(c3())
print(c2())
print(c3())
print(c2())
print(c3())
Alternatively, you could play with the environment of the function each time it is called, but that will only work correctly for some cases (depends on what the internals of the function are).
The canonical reference for this is link text. Explaining this briefly we'll work off the following code from the site:
a = 1
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G})
setfenv(1, newgt) -- set it
The first line sets up the (global) variable "a". You can view this as setting default values for your code. (Keep in mind that in Lua all variables are global unless you declare them with "local".)
The next line creates a table that will be your new environment. It is local to the function/chunk you're executing in so it won't be trashed by anything else that runs now or later.
The third line is the beginnings of the magic. To understand it you're going to have to understand metamethods In essence, however, you're using Lua's metamagic to ensure that any global names that aren't defined in your soon-to-be function environment get resolved in the context of your old global environment. Basically it means if you use a name that's not in your function environment, Lua will automagically hunt in the global environment you used to have to find the name. (In a word: inheritance.)
The fourth line is where you get what you're looking for. Setfenv(1,...) means that this changes the environment for your current function. (You could use 2 for the calling function, 3 for the calling function's caller, etc. on up the line.) The second parameter is the table you just set up, complete with inheritance of the old behaviour. Your function is now executing in a new global environment. It has all the names and values of the old environment handy (including functions and that global variable "a" you put in). If, however, you WRITE to a name it will not overwrite the global state. It will overwrite your local copy of it.
Consider the following subsequent code:
a = 10
b = 20
What you have done now is made your function environment table look like this:
{a = 10, b=20}
Your "global" environment, in short, contains two variables only: a (value 10) and b (value 20). When you access "a" later you'll get your local copy with 10 -- the old global value stored in your metatable is shadowed now and is still set to 1 -- and if you access "b" you'll get 20, despite the original global state likely not even having a variable "b" to access. And you'll still be able to access all the functions, etc. you've defined before this point as well.
Edited to add test code to debug OP's problem.
I put the following code into "junk.lua":
a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt)
print(a)
a = 10
print(a)
print(newgt)
The output of it is as follows:
$ lua junk.lua
1
10
table: 0x976d040
This is using Lua 5.1.4. What is the output you're seeing?