10 Nov, 2009, JohnnyStarr wrote in the 21st comment:
Votes: 0
I have a question about the stack and indices.
The following code is from the Lua 5.1 ref guide:
The following example shows how the host program may do the equivalent to this Lua code:

a = f("how", t.x, 14)
Here it is in C:

lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how"); /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
lua_remove(L, -2); /* remove 't' from the stack */
lua_pushinteger(L, 14); /* 3rd argument */
lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */


On this line: lua_getfield(L, -1, "x");, you index the stack with -1, is this because you push a value on top, that it moves everything down,
thus resulting in a negative number? Then the next line: lua_remove(L, -2);

I don't really grasp the idea.
10 Nov, 2009, kiasyn wrote in the 22nd comment:
Votes: 0
staryavsky said:
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how"); /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
lua_remove(L, -2); /* remove 't' from the stack */
lua_pushinteger(L, 14); /* 3rd argument */
lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */


1. Get function "f" and push to the stack					(stack pos 1: f)
2. Push "how" onto the stack (stack pos 2: "how")
3. Get table "t" and push to the stack (stack pos 3: t)
4. Get member x of "t" and push to stack. Our current iterator is (stack pos 4: t.x)
4, so -1 is 3, which is table "t"
5. Remove "t" from the stack (stack pos 3: t.x)
6. Push "14" onto the stack (stack pos 4: 14)
7. Call "f" using stack pos 1, with 3 arguments (next in stack) (stack pos 5: result of "f")
8. Set "a" to last item on stack: result of "f"


This is my understanding of it.. maybe slightly flawed
10 Nov, 2009, David Haley wrote in the 23rd comment:
Votes: 0
In Lua, index -1 means the top of the stack. More generally, -x means the x^th position counting from the top. So, -1 means top, -2 means right below the top, etc.

Kiasyn's description is generally correct, however when calling a function, its arguments are popped as is the function itself. Here is a full stack trace. The stack is initially "…" to represent whatever was on there before we start calling stuff.

// stack: …
lua_getfield(L, LUA_GLOBALSINDEX, "f"); // stack: f, …
lua_pushstring(L, "how"); // stack: "how", f, …
lua_getfield(L, LUA_GLOBALSINDEX, "t"); // stack: t, "how", f, …
lua_getfield(L, -1, "x"); // stack: t.x, t, "how", f, …
lua_remove(L, -2); // stack: t.x, "how", f, …
lua_pushinteger(L, 14); // stack: 14, t.x, "how", f, …
lua_call(L, 3, 1); // stack: f("how", t.x, 14"), …
lua_setfield(L, LUA_GLOBALSINDEX, "a"); // stack: …
10 Nov, 2009, JohnnyStarr wrote in the 24th comment:
Votes: 0
Ok, so positive is bottom up, negative is top down?
Stack:
|[ 5][-1]|
|[ 4][-2]|
|[ 3][-3]|
|[ 2][-4]|
|[ 1][-5]|
10 Nov, 2009, David Haley wrote in the 25th comment:
Votes: 0
Yes, that's exactly correct. Incidentally, this follows the normal Lua one-indexing convention; I think that in this instance it makes it easier to think about things. stack[-0] would look kind of weird, not to mention that -0 == 0. :wink:
11 Nov, 2009, JohnnyStarr wrote in the 26th comment:
Votes: 0
When working with the global state approach, how exactly would you reference the actor
or character calling the script? would you store a pointer or 'id' to the character
structure into a global variable called 'ch'?
eg:
call lua script, set global 'ch' = ch, run script.
– script
ch.send("testing 1, 2, 3")
– end

The only problem I see with changing the global 'ch' value is what if you have an error
in your C code that sets the 'ch' "pointer" to some other player or something to that
effect?

Also, is it better to implement game objects in C? An example might be a script that loads an
obj to the room:
– you could do
ch.load_obj('bronze-sword', 15)

– or
o = obj.new('bronze-sword', 15)
o.addaff('fire') – add an affect for fun
ch.room.add_obj(o)
11 Nov, 2009, David Haley wrote in the 27th comment:
Votes: 0
Well, it depends on how you structure your scripts. If your scripts are all to be run as "top-level" scripts expecting a main actor, then what you suggested seems reasonable enough. I have my scripts set up as functions that you call with an actor (my term for character) as the first argument – or not, depending on what exactly the script is there for.

staryavsky said:
Also, is it better to implement game objects in C?

That depends on what you mean by "implement". Judging from your example, it looks like you're talking about manipulating object state from Lua. Well, why not? I would suggest that exactly one language be the "master language" for a structure. Then, expose this information to the other language via binding, functions, etc. It gets extremely complicated to have the entire structure replicated in both languages ('complicated' should be read as "needless and arguably kinda dumb").

So, I think it would be perfectly reasonable to have something like telling C to load up some object, and then poke at it from Lua using well-known interface functionality (such as "addaff" or whatever). It would be fine if obj.new talked to C to get you a handle to a C object. But I think it would be very dangerous if both C and Lua were creating items in their own memory spaces, replicating a lot of work.
11 Nov, 2009, JohnnyStarr wrote in the 28th comment:
Votes: 0
I've got a weird issue.
It seems as though g++ might be giving me the fits. I have created a file called lua.test.c to start playing around with stuff.
Naturally, I need to #include "mud.h", but when I do, it gives me this error that is not there when I include Lua's libraries:
code:
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include "mud.h"

void load_lua(void)
{
lua_State *L = lua_open(L);
lua_close(L);
}

OUTPUT:
$ make
g++ -Wall -O -g -DNOCRYPT -c -o obj/lua.test.o lua.test.c
lua.test.c: In function `void load_lua()':
lua.test.c:6: error: expected primary-expression before '*' token
lua.test.c:7: error: invalid conversion from `int' to `lua_State*'
lua.test.c:7: error: initializing argument 1 of `void lua_close(lua_State*)'
make: *** [obj/lua.test.o] Error 1

It's almost as if it devalues the Lua headers.
11 Nov, 2009, David Haley wrote in the 29th comment:
Votes: 0
Are you sure that mud.h is also including the Lua libraries in extern "C" mode? If this is the case, you can either fix mud.h to use extern "C", or try including mud.h before the Lua libraries in lua.test.c.
Also, if you mean to be compiling in C++ mode, you should probably name the files .cpp or something so that g++ knows to use C++ mode.
12 Nov, 2009, JohnnyStarr wrote in the 30th comment:
Votes: 0
Ok, I've changed all source files to .cpp, I've tried to include the lua libraries in mud.h, before mud.h, after mud.h, not in mud.h, extern "C", not extern "C", to no avail.
Is there something in mud.h that could be throwing off the lua libraries? Just to test it, I made a fake header file called foo.h, put a few structures in there to test, and
included foo.h into lua.test.cpp, and it worked just fine. Any suggestions?
12 Nov, 2009, Davion wrote in the 31st comment:
Votes: 0
Try not using a capital letter to represent your lua state. I know in ROM 'L' refers to a bit value (specifically 1 << 12)
12 Nov, 2009, David Haley wrote in the 32nd comment:
Votes: 0
Well, sure, if mud.h is defining "L" to mean something else… that could very well cause problems. (Geez, that just seems like a bad, bad thing for ROM to be doing. :thinking:)
12 Nov, 2009, Davion wrote in the 33rd comment:
Votes: 0
David Haley said:
Well, sure, if mud.h is defining "L" to mean something else… that could very well cause problems. (Geez, that just seems like a bad, bad thing for ROM to be doing. :thinking:)


Well, this is Smaug, so it might not just be ROM!
12 Nov, 2009, Tyche wrote in the 34th comment:
Votes: 0
Macros are your fiend. :devil:
12 Nov, 2009, JohnnyStarr wrote in the 35th comment:
Votes: 0
Davion said:
David Haley said:
Well, sure, if mud.h is defining "L" to mean something else… that could very well cause problems. (Geez, that just seems like a bad, bad thing for ROM to be doing. :thinking:)

Well, this is Smaug, so it might not just be ROM!

Actually, my project is a ROM. I changed merc.h to mud.h because I got used to it on an older project. :wink:
12 Nov, 2009, JohnnyStarr wrote in the 36th comment:
Votes: 0
Ok, so everything's good now, however, there's something even weirder!
I've got everything to run, but when I attempt to run a lua script, it doesn't go through:
static int send(lua_State *L)
{
CHAR_DATA *ch;

for (ch = char_list; ch != NULL; ch = ch->next)
{
if(!IS_NPC(ch))
send_to_char(luaL_checkstring(L, 1), ch);
}
return 0;
}

static const struct luaL_reg mudlib [] = {
{"send", send},
{NULL, NULL}
};

void load_lua(CHAR_DATA *ch)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_register(L, "mud", mudlib);
luaL_loadfile(L, "test.lua");
lua_close(L);
send_to_char("Lua closed.", ch);
}


When it gets to lua_L_loadfile(L, "test.lua") it doesn't do anything. Now, there's no errors mind you, it just doesn't work.
I've tested this code (with some modifications in the send() function) and it works fine, so i'm wondering if it has to do with linking Lua?
12 Nov, 2009, David Haley wrote in the 37th comment:
Votes: 0
This is because luaL_loadfile only loads the file, it doesn't run it. See also the luaL_loadfile documentation.

After loading, the file is placed as a function on the stack; you then need to run it, i.e. call the function.

This is a fairly common pattern; here is an example. (Never mind the other stuff – I will be expanding this example later.)

TileCollection* TileCollection::FromLua(const std::string& filename)
{
lua_State* state = LastBastion::luaState();

int startSize = lua_gettop(state);

TileCollection* tc = new TileCollection();

int err = luaL_loadfile(state, filename.c_str()); // state: loader, …

if (err) {
const char* msg = lua_tostring(state, -1);
LastBastion::getSingleton().logError(msg);
lua_pop(state, 1); // pop the error message
return NULL;
}

lua_newtable(state); // state: env_table, loader, …
BIND_FUNCTION(luatc_Name, "Name"); // state: env_table, loader, …
BIND_FUNCTION(luatc_Image, "Image"); // state: env_table, loader, …
BIND_FUNCTION(luatc_Tile, "Tile"); // state: env_table, loader, …

lua_setfenv(state, -2); // state: loader, …
err = lua_pcall(state, 0, 0, 0);

if (err) {
const char* msg = lua_tostring(state, -1);
LastBastion::getSingleton().logError(msg);
lua_pop(state, 1); // pop the error message
}

// state: …

assert(lua_gettop(state) == startSize);

return tc;
}
12 Nov, 2009, JohnnyStarr wrote in the 38th comment:
Votes: 0
ok great, thanks for the info.
so if lua_pcall returns 1, then there is an error on the top of the stack? Nice.
David, I've got things going now, and I realized that it would be simple to store a character table in the global lua state.
I figure for now I can store the character's name + a static number that increments each time a new char is loaded. EG: (in pseudo code)
room resets mobs:
static count is at 28
load_char(1000) // ch->id = fat_orc29
load_char(1000) // ch->id = fat_orc30
load_char(1001) // ch->id = slimorc31


So, the 'key' in the table is the name + count = 'id', the value of the table will be the pointer to the character stored with lua_pushlightuserdata.
You had mentioned in Darkstone, you had an entry point to where you send the 'ch' or 'actor' as an argument to the function. I figure when a trigger is fired,
I could hook in to the Lua state, pull the character from the table with the 'id' as the key, then use lua_pcall to enter the script.

Any suggestions?
12 Nov, 2009, David Haley wrote in the 39th comment:
Votes: 0
If you have a globally unique id for the character, I would just use that instead of name+id – you can always infer the name from the id.

Your approach of a global table seems fairly reasonable; you'll just have to make sure that:
- when a C character is deleted, the global table entry is removed
- you gracefully handle missing keys (e.g., if Lua has an id to a character that doesn't exist anymore)

What I do is to associate a special "binding" object with each character that I store in the Lua registry (basically, a global table but only used by the C API). The character object destructor takes care of cleaning that up when the character is destroyed. The binding object has a metatable and stores the id of the character. This way, you can call methods on the binding: e.g., ch:sendText("hello"). Internally, the method bindings get the ID, look it up in the global character table, and if the character no longer exists it complains.
12 Nov, 2009, JohnnyStarr wrote in the 40th comment:
Votes: 0
when doing say "ch:sendText("hello")", when you hook into Lua, would you send the actor variable into the function from
the binding itself? As in, does Lua find the 'ch' object each time on call, or do you push it from C?

EDIT:
Can you recommend documentation so that I can see how this works, or better yet, can you share a quick example?
20.0/125