Phantasmal MUD Lib for DGD
|
Phantasmal Site > DGD > Writing a Library > Player vs Body Separating Player LPC Objects from Body ObjectsIt's possible to separate a player's in-game presence into many LPC objects, or few. You can have separate LPC objects for the player, the network connection, the mobile (Skotos uses the phrase 'combat brain' to refer to this object) and the material body, or you can combine any or all of them to get fewer objects, or just a single object. Melville separates the player⁄user⁄connection object from the physical body object. Phantasmal goes a step further and separates out the idea of a mobile into its own object. These methods make it possible to do tricks like swapping bodies more easily, and to otherwise reassign network connections, at the cost of more files, and perhaps more complexity, in the player data. Swapping bodies is particularly useful for wizards to test NPCs and impersonate others. The TMI-2 MUDLib uses a similar trick for dead PCs having ghost bodies. Phantasmal separates the mobile object out specifically to more easily allow NPC actions and player actions to share code. By making player actions "more like" NPC actions, the same functions can operate for both. Skotos does a similar thing with their action⁄verb separation. In DGD, it's often useful to separate the player's presence into multiple objects because the connection object is transient and goes away when the user disconnects — DGD will destroy the network connection object on disconnection. However, the player data can be stored in other in-game objects. By simply keeping those objects in existence and attaching them to a new connection object, you can maintain any modifications to them while never saving them to outside-the-MUD storage. Reassigning ConnectionsIn DGD, there is a single connection object that DGD recognizes as representing any single network connection. DGD doesn't have specific support for reassigning input from one connection to another, so you're usually better off separating the connection into its own object and having a way (in the other, non-connection objects) to reassign what connection's input goes where. Why not have the driver do it? Because DGD has the philosophy that nothing that can be easily done in LPC can be done in the driver. The Kernel Library does the reassignment trick, so obviously it can be done in LPC. EfficiencySo does all this dividing stuff into objects waste a lot of space? Well, it'll waste part of a sector for each object, at least if the resulting objects are small. So that will probably cost you on the order of a kilobyte for each connected player in your MUD. So if you have one of the most popular MUDs on the internet, with about a thousand players connected at peak, this will cost you about... a megabyte of memory. And that's assuming you're doing a fair amount of dividing up objects. Consider whether it's worth the difference in features and maintainability, but the memory usage isn't a significant difference. From: DGD Mailing List (Par Winzell) Date: Wed Mar 24 09:18:01 2004 Subject: [DGD] Aliases & Stacked commands Bart van Leeuwen wrote: > I think you really want something like this_player() because 'users' are > not the only livigns that can use 'commands' on your average mud, which > means to me that commands should never ever depend on this_user() since > they will fail when used by a living thats not linked to any user. > Of course there can be exceptions for security reasons, but those are > cases where runnign such a command from an alias would not be a good idea > either I believe. I certainly agree that a living object should not depend on having a connected user for any of its in-world actions. In Skotos we separated commands and actions entirely -- commands belong to the text interface layer, actions are in the virtual world. Commands cause actions. NPC brains cause actions. We deliberately rejected this_player(), though. As I recall, half of the code in the old LPC driver is concerned with making sure this_player() has the right value at all times. It needs to be switched in and out constantly, depending on which function is being called where. Instead, we simply pass along an explicit 'actor' variable. As concerns this_user(), I ended up implementing a query_originator(), which is a version of this_user() that is persisted across call_outs. Originally this change was motivated by the fact that LPC is single threaded, and thus anything that touches the network has to use a callback approach to waiting for responses. Obviously, DGD's idea of this_user() does not survive that kind of 'wait', and so we wrote a bunch of infrastructure to bridge the gap. The main purpose was for runtime error reporting to continue arriving at the originating connection even after the callback. Later, we've had to spread more and more processes out into callout-driven background processes. It's nice to have runtime error reporting continue to function there, too. Zell |