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 ➜ MUDs ➜ MUD Design Concepts ➜ Questions about starting a new MUD codebase from scratch using Lua...

Questions about starting a new MUD codebase from scratch using Lua...

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


Pages: 1  2 3  

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #15 on Fri 10 Aug 2007 05:19 AM (UTC)
Message
Quote:

What do you assign IDs to every object?

How would assign a unique number to every copy of a prototype?


It was about at this point where my design bogged down - plus I was running out of energy for what seemed a very large project.

Assigning IDs to every object is certainly a possibility, another one would be to simply make them without ID, but simply exists as something that is contained by their parent. For example, a sword has to exist somewhere (in a room, in your inventory) so it could just be part of that inventory list.

However you have a problem then, if you have 5 swords in your inventory, how do you unambiguously refer to one of them?

- Nick Gammon

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #16 on Fri 10 Aug 2007 05:31 AM (UTC)

Amended on Fri 10 Aug 2007 05:44 AM (UTC) by Nick Gammon

Message
To help anyone who is planning to try something similar, this is the code I found for my Lua-based MUD. It is laughably incomplete, but it fires up, runs, accepts connections, lets you create new characters, and implements a couple of commands. If nothing else, it demonstrates how you can use luasocket to implement non-blocking IO.

The Lua files are in this directory:

http://www.gammon.com.au/files/muds/luamud/

They are small enough that I didn't bother to zip them.

I also had a 'players' directory, in which I placed subdirectories A, B, C ... Z, for saving player files.

You can download luasocket from:

http://luaforge.net/frs/download.php/1589/luasocket-2.0.1.tar.gz

Untar it, and then edit the "config" file and comment out the lines referring to Lua 5.0, and uncomment the lines referring to Lua 5.1, then do "make all" and "make install" (as root), then it should work for you.

To run the MUD, just type: lua main.lua

You should see:


$ lua main.lua
LuaMUD version  1
Initializing ...
Loading sockets library...
Socket library version: LuaSocket 2.0.1
Binding server port ...
MUD ready, on port      5000
Running ...


Then if you connect to port 5000 with your client, you should see something like this:


Welcome to Nick's Lua MUD!

Your name? (Enter 'new' to create a new character) ...
new
What would you like to be called? ...
Nick
You want to be called Nick, right? ...
yes
Please choose a password for Nick ...
xxxxx
Please re-enter your password to confirm it ...
xxxxx
Please choose all sort of other things ...
Welcome to LuaMUD
look
Huh? Try typing 'help'.
help
quit........... Logs off from the MUD
help........... Get help on commands
say............ Say something to people in the same room as you


Meanwhile in the server window you see:


Connection from 10.0.0.3        port    1618
Nick    has entered the game.


The file logon.lua in particular shows how you use coroutines to ask questions and yield while awaiting a reply.

- Nick Gammon

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #17 on Fri 10 Aug 2007 05:34 AM (UTC)
Message
I think I had a more elaborate Lua MUD somewhere, but can't remember where I put it. :(

- Nick Gammon

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

Posted by Sofakng   (6 posts)  Bio
Date Reply #18 on Fri 10 Aug 2007 12:47 PM (UTC)
Message
Thanks for this! I'll take a look at it...
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #19 on Fri 10 Aug 2007 05:17 PM (UTC)
Message
About the ID assigning:

I'm not sure I would assign IDs to object instances. Not only are there several complexities regarding how to differentiate prototype from instance IDs, but also because in the vast majority of cases you don't need them to be uniquely identified beyond their "pointer value" in the code. Players don't talk to objects by their IDs but by keywords, etc. And if, for some reason, you needed to give them a unique ID, it could just be the pointer value or something like that (or the ID from Nick's ID system, if the object is implemented on the host language, or if the ID system is reimplemented in Lua).

And, if you do need to reference an object precisely, most of the time it will be in the context of a single session, like I want *that* object from the storekeeper, or I want to drop *that* object from my inventory.

Besides this is really an interface problem anyhow, because if you have several *identical* objects it doesn't matter which one you drop, and if they are different somehow you should be able to communicate that difference to the interface and not rely purely on ambiguous keywords.



Now, very occasionally, you might actually *want* to give an instance a particular ID. For example, a script in one room might want to control two instances of an object in another room, and for some reason it might need to do that separately. (For instance, it could be moving each instance on its own pattern.) This might seem contrived but hey, it happens sometimes. :)

In this case, I would consider being able to just assign IDs to the object instances. That is, a builder comes along and says "henceforth this instance shall be known as XYZ". The system could complain at you if that is already taken, and otherwise would then assign the ID to the object.



Another thing to note is that IDs don't have to be numeric; you could use strings (I think it's better, actually). That way you could explicitly state whether you want a prototype or an instance by just adding that to the ID. For instance, your sword could be weapon.longsword.17, but then the instances of that would be weapon.longsword.17:1, weapon.longsword.17:163, etc. (as in: 17, instance 1 etc)

Actually that's another thing Nick and I had talked about: whether prototypes should inherit from each other. Does the steel longsword inherit from the longsword, which in turn inherits from the sword, which in turn inherits from the weapon? Then when you code things up, do you add code to the weapon itself, or does the code exist somewhere outside the weapon? Like where does the 'wield' action sit -- in the weapon? Outside? If it lives outside, does it need to "ask" the object if it is wieldable? Does that property exist in every prototype? Or in proto-prototypes?

Sometimes I wonder if there is use to all that; I mean, I think there is a little bit to some extent; for instance all weapons really do share some behavior. But then I start thinking that you get into very, very big design questions, like whether all of your code is truly object based, or if you have these procedures lying around that take care of code. The wielding stuff is a good example of this. Does the notion of 'wielding' exist outside the notion of weapons?

Well anyhow -- that's neither here nor there...



There's another MUD out there that uses Lua; it's called MusicMUD: http://www.evilmagic.org/musicmud/
It seems to be using C++ as a host language. I haven't looked at it in ages so I don't remember to what extent it uses one over the other. But this one is designed with SMAUG in mind so it might give you other ideas about how to set this stuff up.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #20 on Fri 10 Aug 2007 09:37 PM (UTC)

Amended on Fri 10 Aug 2007 09:39 PM (UTC) by Nick Gammon

Message
Quote:

I'm not sure I would assign IDs to object instances. Not only are there several complexities regarding how to differentiate prototype from instance IDs, but also because in the vast majority of cases you don't need them to be uniquely identified beyond their "pointer value" in the code.


I would like to be able to agree with you, because IDs are fiddly. However I keep thinking of cases where, without them, you get into trouble. Without an ID, the only way of disambiguating one item from another is by its internal table reference (value), assuming we store objects (rooms, mobs etc.) as tables with their values being items inside that table.

Now, clearly table references are going to be unambiguous at a given moment, because otherwise they would refer to the same table.

Let's take for an example, there are five mobs in the room, all with the same vnum, but different (ie. 5 instances of the mob). Now the players have decided to target a particular mob, to kill it first. Better to have one dead mob and 4 left alive, than 5 slightly dead mobs. ;)

So, we need to know which mob each one is attacking, and like I say, we probably need to store its table reference. Other methods (like the "4th mob in the room") are likely to change if more mobs enter or leave.

Now say a player puts a curse on a mob, that lasts for quite a while (like, 15 minutes). So we make a timer, that says every minute or so, the mob of that address gets a downgrade to its health.

However the mob gets killed quite quickly, its corpse looted, and is removed from the world. The server then creates a new mob somewhere, and the Lua interpreter happens to re-use that table address (as it is now free). However we still have a timer for that curse, tied to that memory address, so our new mob starts getting damaged for no obvious reason.

You could probably construct a similar example for objects (eg. a sharpened sword, where the sharpening lasts for an hour, but which is subsequently destroyed).




While I was writing all that, a thought occurred to me ... maybe this wouldn't happen after all. This is because, in my example of the curse, the timer with the curse in it amounts to a reference to the table, and thus it can't be garbage collected, and thus the address can't be reused.

You still potentially have the problem of a curse affecting something that is dead, but perhaps when the mob is released from the world, as it were, its table is marked as "dead object", so that any other references to it see that it is dead, and do nothing (things like timers).

- Nick Gammon

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

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #21 on Fri 10 Aug 2007 10:30 PM (UTC)
Message
Yes, I think that if you had the curse example, you could do it in several ways: either (1) your reference is a hard reference, and so the table wouldn't get garbage collected. Or (2) it's a weak reference, and so when the object does get collected, you lose that reference and the key is removed from the table.

Another option would be to make the curse a property of the mob, and so when the mob is destroyed all of its properties go away at the same time.



I think your first example problem is a little harder to solve (the one where players want to coordinate on which mob to attack). Using the "pointer value" as a unique ID works but it could be slightly clumsy. But at least it works... :-)

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #22 on Fri 10 Aug 2007 11:09 PM (UTC)
Message
It's not too bad - assuming the players can specify which mob somehow (custom client perhaps?).

Given that the room list contains a list of mobs, each one will be a table, presumably. Thus the table "reference" is simply the thing you put on your "attack list".

The big problem with references is that they are hard to serialize. Perhaps you simply don't serialize things like the mobs you are currently targetting.

I can still imagine other problems, say you bless your sword for an hour, so that it is stronger or something. Then after 30 minutes you log out for two hours. Now players may expect that the remaining 30 minutes of blessing should still be present, so as part of saving the player you need to save the "30 minutes of blessing remaining" as part of the sword's table.

- Nick Gammon

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

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #23 on Sat 11 Aug 2007 12:05 AM (UTC)
Message
Quote:
It's not too bad - assuming the players can specify which mob somehow (custom client perhaps?).

Well, you could just have a special "look" mode where you see things annotated by their unique id (in this case table reference). Not perfect but doesn't require a custom client, which would be an unfortunate selling point for Lua MUDs. :-)

Quote:
The big problem with references is that they are hard to serialize. Perhaps you simply don't serialize things like the mobs you are currently targetting.

Well, serialization code already handles this, right, because it can handle things like cycles. If you were to serialize the entire game state, *exactly* as is, everything would still work. (Which I think is really cool -- it makes copyovers a lot nicer.)

And if you're not serializing the exact state but instead saving the character to disk, presumably you wouldn't be saving any references to stuff outside the player. So in particular you wouldn't be saving targeting data.

Quote:
I can still imagine other problems, say you bless your sword for an hour, so that it is stronger or something. Then after 30 minutes you log out for two hours. Now players may expect that the remaining 30 minutes of blessing should still be present, so as part of saving the player you need to save the "30 minutes of blessing remaining" as part of the sword's table.

Hmm, yes. I guess the solution to this depends on how you modeled it in the first place. If you model the timer as a property of the sword object, it'll get saved naturally. But if you modeled it as an external property, it won't get saved unless you do extra work. (I think that the first solution is more natural, though, right?)

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

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

Posted by Isthiriel   (113 posts)  Bio
Date Reply #24 on Sat 11 Aug 2007 04:47 AM (UTC)
Message
A lot of these design decisions are the same ones facing lpmud mudlib implementers. So it might be faster to just pick a mudlib and port it from lpc to lua.

(Though I think most (all?) lpmuds have access-control built in to the driver (C/C++ code) so you can hire untrusted builders.)
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #25 on Sat 11 Aug 2007 05:13 AM (UTC)
Message
This raises an interesting point, and one that probably has to be designed in from the start. If you are planning to add in untrusted areas (from untrusted builders) then presumably you want to isolate them from your mainstream data.

This is probably a good idea anyway, because it might force you to develop an "events" model that makes it easier to handle things anyway.

You can do this in Lua fairly easily by using setfenv to set an environment for some code (eg. a function). This environment can hide the "main" data (eg. a list of all players, or all rooms), and force you to get them via function calls.

For example, given a certain player p, if a MUD script wants to add 5 to his HP, one way is to simply access the data structure directly:


p.hp = p.hp + 5


However this lets you (presumably) access other things as well, and possibly corrupt the entire structure. Instead, you might give the build a function, like this:


ChangePlayerAttribute ("nick", "hp", 5)


Thus, you "expose" to the scripters (builders) the ChangePlayerAttribute function but not the underlying data. Now this function can check stuff, like does "nick" exist, is this builder allowed to modify the "hp" field, and is 5 a reasonable value to modify it by.

The advantage of such a model is that you can then automatically generate an "event" from the function call. For example, notify each avatar in the room that Nick's HP has gone up by 5. A side-effect of this might be that a mob is annoyed to see this happen, and attack the person who caused it to happen (for example, if a cleric heals someone they get aggro from a mob).

Now this really requires that you deal in IDs and not direct table structures. If the builders have the actual structure (table) it is hard to stop them doing whatever they want. But if they only have the ID, then the low-level routines convert that into a lookup to find the structure.

- Nick Gammon

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #26 on Sat 11 Aug 2007 05:30 AM (UTC)
Message
I want to point out again some questions raised in this thread:

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

There are a heap of decisions to be made before you write a line of code, otherwise it might all be wasted. Some that have arisen so far (in this thread and elsewhere) are:


  • Language (C++, Delphi, Lua)

  • Do you support Unicode? If to, are you going to use UTF-8? If so, you need to allow for things like testing the length of names against Unicode code lengths, not straight byte counts.

  • Are you going to use untrusted builders? If so, you need to have some way of isolating the damage they can do (eg. sandbox environment, access control).

  • Are you going to save everything at once (like PennMUSH does), or individual things (like player files, as SMAUG does)?

  • What are your plans for instancing things (objects, rooms, mobs)? Do you refer to them by some sort of ID system, or by reference?

  • Are you going to support multiple languages (eg. French, German, Japanese, Korean)? If so, multiple ones in the same session, or just allow for the MUD to be translated? This can ramp up the complexity considerably if you do.

  • Are you going to use a custom client? If so, have you set aside time to write it?



That is about all for this list, there are lots of other questions in the other thread.

- Nick Gammon

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

Posted by Quixadhal   (4 posts)  Bio
Date Reply #27 on Fri 05 Sep 2008 10:44 PM (UTC)
Message
Hey all, sorry for necro'ing the thread...

I just wanted to toss in a couple of reasons supporting giving object instances their own ID.

I know many people treat instances as simple clones of their prototypes, but I expect every instance to be modifiable by events in the game. Equipment gets damaged, monsters gain experience by killing players, etc. Ideally, the state of everything gets saved and restored. Not having unique ID's makes it impossible to do this UNLESS you rely on state dumps exclusively.

I'm also a database fanatic, and ideally want to store every bit of data in an RDBMS. As such, I tend to think of relationships between game objects in terms of how I'd build a schema for them.

There's nothing that says you have to have a SINGLE unique ID for every object in the game. You can have unique ID's for every prototype, and then within each prototype, you issue unique ID's for every instance.

So, perhaps you have a sword (ID: 37), and this particular sword has a big chip out of the edge (23/40 hp, -1 damage), so that sword happens to be instance 12. You go to the store and sell it, buying another one because some silly admin decided it was cheaper to buy a new one than to repair the old one. Now, you have a sword (37), but this one is instance 20.

You have have 5 swords in your inventory, and the code that shows you the inventory can stack them if it wants (by ID), but it can also distinguish them by instance ID. If I say drop 2.sword, it could just sort the list of swords by instance ID and drop the 2nd one in the list.

Since ID's are permanent, the order will always remain the same. If I pick it back up again, it will become 2.sword again.

Using 32-bit integers, you can have 2 billion kinds of objects, and 2 billion instances of each. If you really needed more than that (perhaps every coin is a real object -- shudder), you could decide if you want to recycle unused id's, or duplicate an object ID to get another 2 billion instances of it -- dealing with the inconsistencies.

Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #28 on Fri 05 Sep 2008 11:29 PM (UTC)
Message
Why would you need to give every object its own unique, persistent ID? Most of the time, you either don't need an ID to begin with, and when you do, you need one "just now". In these cases, the pointer of the object should suffice. Only in relatively infrequent cases would you need to always be able to uniquely identify something.

The scheme of having prototype.instance id is pretty workable (I'd still just use a string, probably) but I still wonder at why you would need to. I guess that if it's just two integers, it is almost "free" to give every object an id, but still...

But it's not actually free in terms of memory: only having IDs when you actually need them will make it easier to cheaply index your objects by id. If you have prototype.instance, then your index needs to contain every instance (or you need two levels of indexing), even the ones you really don't care about.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #29 on Sat 06 Sep 2008 03:39 AM (UTC)
Message
Quote:

Why would you need to give every object its own unique, persistent ID? Most of the time, you either don't need an ID to begin with, and when you do, you need one "just now". In these cases, the pointer of the object should suffice.


Well I believe Lasher has just implemented unique IDs for all objects on Aardwolf, as part of our attempts to make the inventory plugin work smoothly. We need to know, especially with cached object details, that we are still referring to the same object we had 5 minutes ago. A pointer might not cut it, as conceivably if the object was destroyed, and another allocated using the same memory - not altogether unlikely - then we could be referring to the wrong object.

To allow for growth he is using 64 bit integers, which we calculated should last well beyond our graves, even if objects are created at quite a fast rate (like 1000 per second).

- 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.


100,924 views.

This is page 2, subject is 3 pages long:  [Previous page]  1  2 3  [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.