Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ SMAUG ➜ Lua ➜ Designing a Nice Lua Implementation like Aardwolf's

Designing a Nice Lua Implementation like Aardwolf's

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1 2  

Posted by Kasji   (35 posts)  Bio
Date Wed 05 Sep 2012 07:33 AM (UTC)
Message
I've been wanting to add Lua into SWR (a branch of Smaug) for years now, but after seeing what Aardwolf has done I think it's time to quit stalling.

I am not very good with Lua's C API as of yet, but I am getting better. The whole stack thing was confusing at first, but I think I've got a solid grasp of the mechanics now. However, I still don't know enough. I would like to write an implementation similar to Aardwolf's, but I am unsure of how to do something like:

name = ch.name
level = ch.level
...

Where it doesn't keep 2 copies of the data, but rather retrieves the data from C on the fly. That seems cleaner than Nick's original method of something like:

level = mud.getlevel(ch)
...

Initially I intend to use just one Lua state. So the other issue I'm up against is how to separate scripts properly, such as having say, two functions called "on_enter" for two different room vnums. The Aardwolf fellow mentioned something about using a map to track all the various scripts. Would that work with "luaL_loadbuffer"?

Also, just as an FYI, my intent is to use Lua for not only hooks in various places, but also as mob spec_funs, and localized commands attached to rooms, objects, mobs.

So I guess to get to the point: What C code is needed to allow you to do something like "level = ch.level"? Do you have any recommendations on tracking individual scripts? Is "luaL_loadbuffer" good for loading separate scripts stored in "const char *"? Though it might be better to just load files instead. Not sure.
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #1 on Wed 05 Sep 2012 01:28 PM (UTC)
Message
Quote:
how to do something like:

name = ch.name
level = ch.level
...

Where it doesn't keep 2 copies of the data

metatables?

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #2 on Wed 05 Sep 2012 09:02 PM (UTC)
Message
Indeed. Example here:

http://www.gammon.com.au/forum/?id=4904

That uses a table called var which uses metatables to both read to and write from MUSHclient variables without keeping two copies.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #3 on Thu 06 Sep 2012 01:49 AM (UTC)
Message
Thanks guys. Would this be right for doing a metatable in C?

lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
lua_pushcfunction(L, lua_chGetter);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "ch");

I am just guessing, but in "lua_chGetter", it would accept the "lua_State" variable as an argument, and you would have to get the property being accessed from the Lua stack?

My next question will be, what is the best way to know what character needs to be accessed? Should it be pushed onto the Lua stack as a userdata item, and included in the hook function's arguments? Such as this:

// Call function 'on_enter', pass 'ch' as argument.
lua_getfield(L, LUA_GLOBALSINDEX, "on_enter")
lua_pushlightuserdata(L, (void *) ch);
lua_call(L, 1, 1);

I don't think this is right since in this case ch wouldn't be a global? I am just not sure how to make Lua keep track of who ch is.
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #4 on Thu 06 Sep 2012 03:24 AM (UTC)
Message
Kasji said:
My next question will be, what is the best way to
In my experience, questions about "the best way" are always misguided.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #5 on Thu 06 Sep 2012 04:41 AM (UTC)
Message
Well, 'best' may be subjective rather than objective.

Let me elaborate. What is a way to pass Lua information that will result in fewer headaches and less bloated code?

I am also concerned about how to keep track of what character/room/object/mob is involved in a script if I were to use co-routines. What would happen if the entity were destroyed while the co-routine is paused? If the metatable does not hold the information, but just the method of looking up the information, then the script could obviously raise an error.
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #6 on Thu 06 Sep 2012 01:03 PM (UTC)

Amended on Thu 06 Sep 2012 01:16 PM (UTC) by Fiendish

Message
Quote:
fewer headaches and less bloated code
Than what?

Quote:
What would happen if the entity were destroyed while the co-routine is paused?
You only have a problem if you let it resume.

Quote:
If the metatable does not hold the information, but just the method of looking up the information
If it held the information rather than a method for looking up the information, then it would just be called a table.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #7 on Thu 06 Sep 2012 07:33 PM (UTC)
Message
I think I've got how I want to do it. Rather than looking up a Lua function name to call as a hook, I am thinking I will do the name of the hook as a string separate from the script itself.
struct lua_prog_trigger
{
  char * hook;
  char * filename;
}
struct lua_prog_data
{
  char * filename;
  char * script;
  char * byte_code;

  ROOM_INDEX_DATA ** room;
  CHAR_DATA ** self;
  CHAR_DATA ** ch;
  OBJ_DATA ** obj;
};

So basically, lua_prog_trigger will be a list on rooms, objects, and mobs to be iterated over in the same way mobprogs does. lua_prog_data will be either stored as a list globally, or a list per area. I will probably do some indexing at load time to make fast access possible, or just use an STL container.

I will do something like this to open the mud to Lua:
prog->ch = (CHAR_DATA**)lua_newuserdata(L, sizeof(CHAR_DATA**));
(*prog->ch) = ch;
luaL_getmetatable(L, "ch");
lua_setmetatable(L, -2);


Checking that the involved entities still exist whenever there's a possibility of extraction. Paused co-routines, functions that destroy entities, etc.
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #8 on Fri 07 Sep 2012 12:12 AM (UTC)
Message
Kasji said:

What would happen if the entity were destroyed while the co-routine is paused? If the metatable does not hold the information, but just the method of looking up the information, then the script could obviously raise an error.


I think you have to pass some sort of identifier that remains valid. That is why, in my Lua implementation, I passed down things like a room vnum, or a character name. Now the vnum may not exist, in which case you raise an error, but it can't be "invalid" in the sense that a pointer might be if it used to point to something and now points to something else.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #9 on Fri 07 Sep 2012 01:47 AM (UTC)
Message
Yeah, definitely want to avoid accessing invalid memory.

My knowledge of pointers isn't great, so can you tell me if this works? If you have a pointer to a pointer, when the object is deleted and (presumably) the pointer is set to NULL, if you dereference the pointer to the pointer, can you check to see if it's been set NULL, thus being able to know that the object has been deleted? Again, presuming the pointer has been set to NULL. Forgive me if I am completely wrong about how that works. I've never had a reason to use those ever.

I read a little bit about how David Haley did his system, and he seems to have implemented a fairly sophisticated memory management layer. I wonder, maybe smart pointers would work well for this kind of situation?

My other thought would be to kill off any running/paused script whenever a related entity is destroyed, though that might be pretty expensive. Not sure.

Your way definitely makes memory management easy, having a lua_State for each player, but due to my wanting to use Lua in the mud's main update loop, it probably isn't a good way to go.
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #10 on Fri 07 Sep 2012 05:31 AM (UTC)
Message
Kasji said:

My knowledge of pointers isn't great, so can you tell me if this works? If you have a pointer to a pointer, when the object is deleted and (presumably) the pointer is set to NULL, if you dereference the pointer to the pointer, can you check to see if it's been set NULL, thus being able to know that the object has been deleted?


Absolutely not. That doesn't happen.

The operator:


delete foo;


Does not alter the contents of foo. It now points to memory that has been freed and it is an error to dereference foo now.

You might do this:


delete foo;
foo = NULL;


But that doesn't help if you made a copy of foo previously (the copy won't be altered).

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #11 on Fri 07 Sep 2012 05:34 AM (UTC)
Message
It's a non-trivial problem. There is a thread about it here:

http://www.gammon.com.au/forum/?id=3079

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #12 on Fri 07 Sep 2012 07:13 AM (UTC)
Message
I could almost kiss you. Once I get these pieces put together, I will release the basic Lua implementation. It should be enough for others to follow along.
Top

Posted by Fiendish   USA  (2,534 posts)  Bio   Global Moderator
Date Reply #13 on Fri 07 Sep 2012 12:35 PM (UTC)
Message
Nick Gammon said:

It's a non-trivial problem. There is a thread about it here:

http://www.gammon.com.au/forum/?id=3079


Does that just implement C++11's weak_ptr?

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Kasji   (35 posts)  Bio
Date Reply #14 on Fri 07 Sep 2012 08:39 PM (UTC)
Message
I've implemented your solution on my CHAR_DATA struct so far. It's working like a charm.

File: objectmap.c
#include <stdio.h>
#include <string.h>
#include "mud.h"

extern tPointerOwner<CHAR_DATA> char_map;

template <class ITER, class F>
F safe_for_each_ch (CHAR_DATA * ch, ITER first, ITER last, F func)
  {
  while (first != last)
    func (ch, *first++);
  return func;
  }

void list_char_map(CHAR_DATA * ch, const std::pair <tId<CHAR_DATA>,CHAR_DATA*> item)
{
   if (item.second->pIndexData)
      pager_printf(ch, "&g| &W%-10d &g|| &W%-30s &g|| &W%-10d &g|\n\r", item.first.Value(), item.second->name, item.second->pIndexData->vnum);
   else
      pager_printf(ch, "&g| &W%-10d &g|| &W%-30s &g|| &W%-10s &g|\n\r", item.first.Value(), item.second->name, " ");
   return;
}

void do_objectmaps(CHAR_DATA * ch, const char * argument)
{
   pager_printf(ch, "&g##############################################################\n\r");
   pager_printf(ch, "&g|     &WID     &g||              &WName              &g||    &WVnum    &g|\n\r");
   pager_printf(ch, "&g##############################################################\n\r");
   safe_for_each_ch(ch, char_map.begin(), char_map.end(), list_char_map);
   pager_printf(ch, "&g##############################################################\n\r");
   return;
}


The command do_objectmaps yields output like so:
##############################################################
|     ID     ##              Name              ##    Vnum    |
##############################################################
| 1          || The Coruscant Mall             || 3          |
| 2          || Puff                           || 1          |
| 3          || supermob                       || 3          |
| 4          || supermob                       || 3          |
| 5          || supermob                       || 3          |
| 6          || supermob                       || 3          |
| 7          || demon imp                      || 2          |
| 8          || demon imp                      || 2          |
| 9          || demon imp                      || 2          |
| 10         || demon imp                      || 2          |
| 11         || Grotans                        || 131        |
| 12         || Protocol Droid                 || 126        |
| 13         || General Rock                   || 116        |
| 14         || Lowbacca                       || 117        |
| 15         || An Ewok                        || 118        |
| 16         || Engineer                       || 127        |
| 17         || Engineer                       || 128        |
| 18         || Drall                          || 120        |
| 19         || Dorsk                          || 119        |
| 20         || Commander Targ                 || 121        |
| 21         || Contraband Weapons Dealer      || 122        |
(C)ontinue, (R)efresh, (B)ack, (Q)uit: [C] 

| 22         || Barlan                         || 123        |
| 23         || Molbakka                       || 124        |
| 24         || Dorak                          || 129        |
| 25         || store manager                  || 132        |
| 26         || Colonel Jentric                || 150        |
| 27         || Admiral Coran                  || 125        |
| 28         || astromech droid r2             || 209        |
| 29         || astromech droid r2             || 208        |
| 30         || technician                     || 206        |
| 31         || technician                     || 206        |
| 32         || technician                     || 206        |
| 33         || technician                     || 206        |
| 34         || spaceport security guard       || 204        |
| 35         || spaceport security guard       || 204        |
| 36         || begger                         || 210        |
| 37         || begger                         || 210        |
| 38         || human                          || 207        |
| 39         || human                          || 207        |
| 40         || spaceport security guard       || 203        |
| 41         || storekeeper hopkeeper droid    || 201        |
| 42         || toll droid ticket token        || 200        |
| 43         || HG Guard                       || 228        |
| 44         || hunters guild trooper          || 236        |
| 45         || Ammuntion Dispenser            || 230        |
(C)ontinue, (R)efresh, (B)ack, (Q)uit: [C] 

...


Now I just need to add checks into the Lua interface.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


78,643 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.