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, 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.
 Entire forum ➜ SMAUG ➜ Lua ➜ Lua interface

Lua interface

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


Posted by Darwin   USA  (125 posts)  Bio
Date Mon 24 Mar 2008 02:47 AM (UTC)
Message
I'm using Nick's Lua interface for SMAUG. I've adapted it to work with my heavily modified SMAUG 1.4a source. I love that I can use Lua in coding new things. There is, however, a point that I'm not too happy with which I'm not sure how to rework: The fact that there is one global mud state (which is good) and one state per character (not so good.) It's a not-so-good thing because it redundantly loads the same data for each character that logs in whether it is relevant to that character or not. It would be more efficiant to load it once and have it available to all characters. I don't believe I'm alone in this thought.

I've been going over the open_lua function in lua_scripting.c trying to figure out how to avoid this by somehow placing the character metadata into the global mud state without breaking how all the current functions work.

Are there any suggestions? Is there any plans to release an updated version that would include this setup?
Top

Posted by Nick Gammon   Australia  (23,068 posts)  Bio   Forum Administrator
Date Reply #1 on Mon 24 Mar 2008 06:06 AM (UTC)
Message
Well it won't be easy, because a lot of the routines assume a current character. For example:


mud.gain_exp (n)

Adds 'n' experience to the current character.


Quote:

... trying to figure out how to avoid this by somehow placing the character metadata into the global mud state without breaking how all the current functions work


But what character? With one state per player, the character is the one the state "belongs" to. To have a single state you need to have some way of telling each routine who the current character is. So for example for this:


If omitted, the current character (player). eg.


if mud.fighting () then --- is the current character fighting?


This won't work any more because there is no "current" character with a global state (although conceivably you might set a "current character" userdata just prior to calling the Lua routine, based in which character is is for).

Something like this might do it, but I haven't tested it in any way:


  lua_pushstring(L, CHARACTER_STATE);  /* push address */
  lua_pushlightuserdata(L, (void *)ch);    /* push value */

  /* this makes environment variable "character.state" by the pointer to our character */
  lua_settable(L, LUA_ENVIRONINDEX);


Now if you did that code every time you did a call_lua (rather than once during initialization) then for a particular call_lua the current character would be the "current character". This might work.

I would read up on the lua_settable function, you might need to pop 1 or 2 items from the stack afterwards.

- Nick Gammon

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

Posted by ThomasWatts   USA  (66 posts)  Bio
Date Reply #2 on Mon 24 Mar 2008 08:02 AM (UTC)
Message
The quick and easy way to solve this is to ditch the per character Lua states, then move all of that information into the global state.
A character light userdata can be passed into the Lua state whenever a call is made. On the Lua side it can be identified with a metatable which allows all kinds of cool tricks (see www.lua.org for more).
Once you have a light userdata and an associated metatable, it can be used as a table key where you store all Lua only data.
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #3 on Mon 24 Mar 2008 11:32 PM (UTC)
Message
Well, this might not be useful for your immediate needs, but I plan on releasing (at some point) a version for SmaugFUSS that has only one Lua state. But here's the general idea...

The thing is that there are some issues involved in this, which is why Nick used just one state in the first place.

1. How do you get character pointers to Lua?
This is relatively easy to solve, just use light userdata. But this creates the much more serious problem #2.

2. How does the Lua state store pointers to characters?
It is not safe to simply store the pointer; for that to work, you would have to clean the Lua state of all references to the character when the character is destroyed. This problem exists in C, of course, but is exacerbated by Lua's dynamic nature. The whole point of using Lua is that you can do things quickly and easily; somebody might store something without even thinking that it is an issue, causing later on a crash. If you have to regiment all of the Lua code extremely closely on a C pointer level, you have lost much of the benefit Lua was supposed to bring you.



My solution is to use a C++ solution that solves the above problem (in C++), but in Lua.

A while ago, Nick wrote an ID system where you store references by a unique reference number and not a memory pointer. When you want to access that reference, you ask the reference manager to give you the corresponding pointer. If that number has no associated pointer -- for instance, because the pointer was destroyed -- the manager returns NULL. You still must (obviously) check for NULL, but at least you have the option to do so, rather than having to simply trust that the pointer you have is still valid.

Shortly after Nick made his code public, I adapted it for use on my own game, managing rooms, objects and characters. It worked very nicely.

When I want to give a character reference to Lua, I pass in a special userdatum that encodes not the pointer but the ID number. When you use that userdatum to talk to the C++ side, you simply go through the pointer manager, and if it yields NULL, you can raise a Lua error to be safely handled.

Well, I described it a fair bit in this thread, if you're curious I would recommend it:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=8000

You can skip down to "well, your ID system solves this problem" to get to the relevant bit.

The main thing to note is that this requires using C++ instead of C. But for FUSS, this is not really much of a change given that it already uses g++ as a compiler.



I should note that all of this actually works, in that I have Lua embedded on my game using this system; the command interpreter is now Lua-based as are some other things. I just need to clean things up and sort them out before releasing it as a FUSS thing.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Darwin   USA  (125 posts)  Bio
Date Reply #4 on Tue 25 Mar 2008 12:18 AM (UTC)
Message
Quote:
But what character? With one state per player, the character is the one the state "belongs" to. To have a single state you need to have some way of telling each routine who the current character is.
I don't particularly have a problem with having a state per character, but rather that there is so much redundant stuff loaded for each character.

Is there a more efficiant/elegant way to have this data, such as the tasks, loaded only once but still available to all character states?
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #5 on Tue 25 Mar 2008 02:40 AM (UTC)
Message
You could, I suppose, write C bindings for functions in the per-player Lua state that call Lua functions in the global state and push the results back onto the calling per-player Lua state. Note that you don't have states for the NPCs.

Nonetheless it might be better to actually move to a global state given that to do truly interesting things with manipulating characters -- and even players -- you need to start having proper references to them. For example, how are you going to write mudprogs for NPCs?

There has also been work by Lua folks on having states share data, meant for multithreading, but I don't know too much about it. You could try looking at Lua Rings: http://www.keplerproject.org/rings/. The master would be the global state, and it would create slave per-player states.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Gammon   Australia  (23,068 posts)  Bio   Forum Administrator
Date Reply #6 on Tue 25 Mar 2008 10:14 AM (UTC)
Message
Quote:

Is there a more efficiant/elegant way to have this data, such as the tasks, loaded only once but still available to all character states?


One idea is to load the tasks into the global state, but have it queried from player states by a new interface. For example, given a task name, supply only that task's data.

This would be fine for when you knew the name, it might be harder if you were in a particular room and needed to know all available tasks. Still, you could make another call that returned the names of all tasks in a particular room.

Another approach might be to load all tasks initially, but then go through and discard ones the player is unlikely to need in the near future (eg. all ones much higher or lower in level).


- Nick Gammon

www.gammon.com.au, www.mushclient.com
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.


23,882 views.

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.