[MUD-Dev] [adv-mud] Spellbound Hierarchy and Keys (fwd)

J C Lawrence claw at kanga.nu
Thu Jan 20 20:39:29 New Zealand Daylight Time 2000


------- Forwarded Message

From: "phlUID" <phluid at mindless.com>
To: "Advanced Mud Listserv" <adv-mud at egroups.com>
Date: Thu, 20 Jan 2000 22:31:48 -0500

   I would like to share something small I've been working on
  and see if anyone has any opinions on perhaps a better way to
  go about doing this:

   On Spellbound, every object in the game is given it's own
  database number. Help files, races, spells, objects, rooms,
  mobiles, exits, worlds, areas, classes, populations, etc.
  There are generically two types of database numbers: Pure,
  which is just a simple unique number given to every object,
  and Hybrid, which is used in the case of Object's and
  Mobs. For example, a room is object #43, while the sword
  in my inventory is #45:1 and the same type of sword in
  my friend's inventory is #45:2. The second number is
  referred to as an Entity ID because they refer to specific
  Entity's instead of Entries. There is an Entry class for
  #45 called ObjectEntry, and there is an associated Entity
  class for ObjectEntry called Object. This concept is the
  same as DIKU's use of OBJ_INDEX_DATA and OBJ_DATA.

  Both Entities and Entries are inheriting from the Savable
  class. Everything in the game is Savable more or less,
  allowing for the game to remain persistent even across
  reboots. Anything that is Savable can be editted in OLC
  by using the special attribute manipulation commands built
  into the server. Every savable has Attributes, such as
  Name, Size, Weight, Owner, and these attributes have
  a table of rules that define valid ranges for setting the
  actual variables underneath.

  The overall hierarchy looks something like this:

                              Streamable
                                  |  |
   HIERARCHY OF SPELLBOUND        |  +---------------------+
 (Inheritence Relationships)      |           Eventable    |
                                  |             | | |      |
             Savable------------- |  +----------+ | +----+ |
             |                   \|  |         System    | |
       +--Entry--+            +--Entity-+                | |
       | | | | | |            | | |  |  |          Descriptor
    +--+ | | | | +---*Room----+ | |  |  +--+
    |    | | | +-----*Area------+ |  |     |
    | +--+ | +-------*Exit--------+  +--+  |
    | |    +------------+               |  |
    | +-*ObjectEntry    |     #Object---+  |
    +---*CharacterEntry |     #Character---+
                        |         |
       * - Perminent    +-----*Player
       # - Persistent

  The Streamable class is how general communication is done
  between objects, such as sending text to a player or echoing
  text to a room. The code just does something like this:

  PlayerBob << "{RDon't Panic{x\n\r" << flushl;

  The flushl is detected and a subsequent flush( ) is called
  on the Streamable class, which flushes the stream buffer.
  The actual flush( ) must be defined to make Streamable no
  longer an abstract class. The flush( ) for player's empies
  out the text to their descriptors (any entity may have
  multiple descriptors, ala MUSH.)

  I've been using this hierarchy for quite a while, and I've
  never had any problems with it.

  Anyways.

  One day I was coding and thinking about a generic MUD coding
  problem:

  Currently, Spellbound uses pointers directly to objects
  like normal DIKU mud. A room has pointers to the people
  that are in the room. An event has a pointer to it's
  target. A descriptor has a pointer to it's Incarnation
  (Entity). What started to irk me was the fact that these
  pointers require a lot of messy cleanup procedures for
  when you are destroying the object that they are all
  pointing too. This is made worse by the object orientness
  of C++, so I began to think of alternate ways of storing
  pointers.

  The first thing that I wanted to be able to do is validate
  pointers. This way I can leave it up to the code that
  references these pointers to deal with invalid objects.
  Example time.

  Bob is in a room. Joe is attacking bob, and has a pointer
  to him as a victim. Fred is casting 'Poop Breath' on
  Bob. There is an event in the room that is about to make
  Bob explode. There is also three mobs using pathfinding
  throughout the world to find Bob and hunt him down. However,
  Bob somehow upset the admins and they suddenly decide to
  @nuke him (destroy him). Now, it is possible for the code
  to inform the ENTIRE game that Bob is no longer in existence,
  but this would be the most bloatedly laggy thing I can think
  of, expessially when we are dealing with objects that are
  very temporary, such as Spell Affects.

  To give a little more backround, my database saving routine
  has a method of converting pointer's into their Database
  Numbers and optional Entity IDs so that loading and saving
  does not become a problem. The database saving/loading
  mechanism also has 4 stages, in which it loads plaintext
  data first, pointer data next, entity plaintext data, and
  then entity pointer data. This is so that the world can
  be reassembled without worrying about object's that don't
  exist when you are trying to get pointers to them.

  This started my thinking into the idea that it would be
  possible to replicate this system to make sure that what
  I am referencing is actually there. My database uses
  a regular array to return pointer's to DB numbers, so
  there is no CPU time involved looking through tables or
  linked lists. If I want object #43, I just call 
  Database.getEntry( 43 ) and it returns a pointer to
  that entry.

  At this point I made the Key object. A Key is a very
  small class that stores 3 number's about the pointer we
  want to save. Instead of using a raw pointer to what
  I need, I just store the database number, the entity ID,
  and a unique identifier. Every savable in the game needs
  to have a unique identifier to make sure that we are
  actually talking about the right object. This identifier
  consists of a count of exactly how many nano or milliseconds
  from the epoch. Assuming that no two object's can be created
  at the exact same time, this gives each object ever created
  a unique identifier.

  Lets say I am Fred and I'm attacking Bob. Now, instead of
  having a pointer directly to Bob, I have a Key which is
  holding Bob's database number, entity ID, and time identifier.
  Lets say this is #23, 0, and 48942809324932723. Whenever I
  need to get the pointer to Bob to send a message or do
  damage, I just ask the database to get me Entity #23:0. I
  then need to compare my time identifier with his, because
  it is possible that #23 was destroyed and then recreated
  as a Fruit Cake, and I'll suddenly be attacking a Fruit Cake
  instead of Bob. After a quick validation that our time
  identifiers are still the same, I know that my pointer is
  valid and I can safely attack Bob.

  If the database couldn't find #23, entity 0, or the time
  identifier was wrong, I would end up with a NULL pointer,
  which will just stop my action and inform Fred that Bob
  is no longer there. Simple, effecient error control.

  The neat thing about this is that it is also very simple.

  The only real question that I had is if anyone can see
  ways to improve this process. On the technical side, I
  need a way of grabbing the time( ) in an extremely
  precise manner, so that even if I create 100 object's in
  the same cpu cycle I will end up with unique identifiers
  for every object ever created.

Amos Wetherbee,
The Spellbound Project

------- End of Forwarded Message



_______________________________________________
MUD-Dev maillist  -  MUD-Dev at kanga.nu
http://www.kanga.nu/lists/listinfo/mud-dev



More information about the MUD-Dev mailing list