1.0 Identification

1.1 Name: Relations Explained

1.2 Author: Brian Jones brianj@otterspace.com

1.3 Purpose: describe the concept of places in detail.

1.4 Creation: April 24, 2004

1.5 CVS $Id: relations_explained.html,v 1.1 2004/06/24 21:22:47 brianj Exp $

1.6 Change history

2.0 References

1.1 http://lists.oasis-open.org/archives/topicmaps-comment/200109/msg00093.html, an article on the net

1.2 many logs

1.3 See the Core Concepts document for definition of terms used.

1.4 Game Developer magazine, series of articles by Jonathan Blow about the language LERP.

3.0 Content

3.1 Subject

In NMC 1, there were a few specific relationships between the various types of objects.

For instance, rooms could hold anything, things could hold things and players, players could hold things. Exits could be attached to rooms, things, or players. Programs could be held or linked to, but not exited from.

In NMC2 a relation is a role an object uses to describe how two or more other objects allow interaction.

3.2 Examples

Below are some examples of the types of issues where an object can act in the relation role.

3.2.1 Dude carries a stone.

This example was used by Jonathan Blow (see the reference in 1.4 above) to explain the problem and a candidate solution to referential integrity.

In traditional game programming (and in traditional object oriented programming, and in NMC1) the idea of "dude carries stone" is typically represented by creating a set of data-types or classes.

For instance, the dude is an example of a Character, the stone is an example of an Item. And the program has hard-coded data structures and rules to enforce the following key facts:

  1. Every character can have zero or more items.
  2. Every item can be carried by at most one character at any given time

To hold the relationship information, typically every Character object has a "list of item identities carried." And every Item object has an attribute that holds the Character identity that is carrying it.

Now, the hard part. When the dude gets the stone (in NMC1 terms, the player of Dude enters "get stone") two crucial things have to happen:

  1. The system adds the stone's identity (the "dbref" in NMC1) into the list of items carried for that character.
  2. The system sets the character's identity (the dbref of "dude") into the stone's "carried by character" property.

Notice that it requires two separate and distinct actions to ensure that the single relationship be stored? Updates to the character and to the item.

The system requires that every item that is carried hold the character's identity that carries it, and that the carrying character must have the item in its list. If the list shows the item in the character's inventory, but the item claims its not being carried, we have an error in the database itself. This is one of the reasons that NMC1 requires "@sanity" to be run. Any bug in the code can lead to only half of the relationship being stored, or the item claiming to be carried by one character but in the list of another character.

The name for these types of errors, where the places we store data conflict, is referential integrity.

Conceptually, we think "the dude carries the stone" and "the stone is carried by the dude" are really the same thing: a relationship that involves both, not a bit on one and a bit on the other. Why not, then, setup the database so that instead of dividing the fact into two places, we store it only once? That's the idea behind making a relationship into an object all its own.

3.2.3 The nature of an object that holds relationship data

A similar problem exists anytime we try to store arbitrarily complex information for use later in query engines such as Google or expert systems in artificial intelligence research.

A post by Steven R. Newcomb (see reference 1.1) described a demonstration he'd seen by Michael Sperberg-McQueen about "extreme markup."

The demonstration used ribbons and people. The people represented facts, the ribbons represented the means to get to one fact from another. I'm going to convert his example to "dude carries stone" instead of "tom buttered bread" only for consistency in my presentation.

The basic approach to storing relationships is to have a relationship as knowledge held on both the objects. We could show that like this:

    dude <---"carries"---> stone

The implication is that stone points at dude ("somehow", perhaps by pointer as above) and that dude points at the stone (perhaps by stone being in the list on dude, as above). The problem is still referential integrity, but there's more problems than even that.

I'd like to be able to remember when things were picked up and dropped. This way, I can track historical events in the database. Further, I'd like to be able to annotate objects with details so that new software works with older objects over time without changing the objects. Thus, I'd like to be able to hold more complex statements, like "dude picked up the stone on Friday and dropped it three days later." Or even, "Dude picked up the stone Friday, dropped it three days later, where it was picked up by Bill and put into a bag."

Instead of making the relationship held as a side-effect in the properties of the two objects involved, let's make the relationship an object of its own.

      +------+      +-------+     +-------+
      | Dude |------| Carry |-----| Stone |
      +------+      +-------+     +-------+

Now, there are three objects: the dude, the relationship "carry", and the stone.

How about all the other details? How about the being picked up on friday?

      +------+      +-------+     +-------+
      | Dude |------| Carry |-----| Stone |
      +------+      +-------+     +-------+
                        |
    +--------+      +-------+
    | Friday |------| Start |
    | 12th   |      +-------+
    +--------+      

So what started on Friday? Did the stone get created on Friday? What object was related by Start? Not the dude, nor the stone, but the relation object Carry.

How about "dropped it three days later"?

         +------+      +------------+     +-------+
         | Dude |------|   Carry    |-----| Stone |
         +------+      +------------+     +-------+
                        |         |
    +--------+      +-------+   +-----+   +--------+
    | Friday |------| Start |   | End |---| Monday |
    | 12th   |      +-------+   +-----+   | 15th   |
    +--------+                            +--------+

Picked up by bill and put into a bag?

         +------+      +------------+     +-------+   +-------+   +-----+   +-------+
         | Dude |------|   Carry    |-----| Stone |---| Carry |---| Bag |---| Carry |
         +------+      +------------+     +-------+   +-------+   +-----+   +-------+
                        |         |                     |                       |
    +--------+      +-------+   +-----+   +--------+ +-------+  +--------+  +------+
    | Friday |------| Start |   | End |---| Monday | | Start |--| Monday |  | Bill |
    | 12th   |      +-------+   +-----+   | 15th   | +-------|  | 15th   |  +------+
    +--------+                            +--------+            +--------+

The above picture shows that the information is being stored as objects with inter-connections.

3.2.3 A character named Otter wears a hat.

In NMC1, we could update the description of the character to include text to the effect of him wearing the hat. We could create a hat object and add the hat to his inventory, and write MPI in the description that would check the inventory for a hat, and if found, include the details in the description during a look. We could even have re-written look to examine the contents of the inventory and determine if any of them had "clothing properties" set, and if so, have them included in the description automatically.

There are many problems with each of these NMC1 solutions. For instance, if the character simply added the hat to the description, to take off the hat would require writing a new hatless description. If the description included MPI to check for the hat, then adding any other piece of clothing other than that hat would be ignored. In the case of the full upgrade to look to take hats into account, the act of picking up a hat (and thus having it "in the inventory") would necessitate wearing it, even though you only wanted it in your pocket.

In short, the problems are all tied to the fact that the relationship between Otter and the hat wasn't stored in NMC1, only the potential of the text and maybe the containment of the object in an inventory.

In NMC2, we would have two objects, Otter and a hat.

If Otter simply carries the hat, then there's an object created that indicates (via its properties) that a subject (Otter) has a relationship (carries) with a direct object (the hat).

If Otter then chooses to stop carrying the hat and start wearing it then we would destroy the carries object (it would have "expired") and then create a new object, one that has the subject of Otter, relationship of wearing, and the direct object of hat.

Now for this to be "displayed correctly" by a look program, the programmer of look would have to know that "wearing" objects indicate clothing.

Like the problem in NMC1, the programmers that write programs still have to understand that there is an intent in something. So in this case, the people who write the look program would have to know a set of relationship keys (such as "wearing" or "carries" or "holding in hands") should be taken into account when rendering the textual output.

3.3 Some Observations

Any number of objects can be created and have properties set on them. Using objects to represent relationships instead of using "pairs" of properties (Dude with a property for what is carried, the stone, and the stone with a property for who carries it, the dude) means that a single object with two properties (carrying, that holds the dude in one property and the stone in the other) can allow an arbitrarily large number of relationships, even over time.

Further, there's no finite list of relationships. In NMC1, there were a few relationships:

  1. "contains" (one object is in the "contents list" of another) with the opposite "location"
  2. "owned by" which indicated what player object owned any object
  3. "linked to" which indicated the target of exits
  4. "dropped to" which indicated the target of objects dropped into a room

If there was a desire to allow true clothing by having a new relationship, "wears", there's no way to do it in NMC1 short of convention. Programmers would "get together" and agree that a character would have a list names "/wears/" that would hold the dbref of things they wear. They would need to agree that if an item was wearable as clothing it would hold a '1' in the "/wearable" property. They would further agree that if an item of clothing was put on, it would hold the dbref of the character wearing it in the "/wearer" property.

If the object worn was deleted, they would need to re-write the @recycle program to remove the dbref from the list of the character in the "/wearer" property. If the character was deleted, @toad (or @recycle for zombies) would need to visit each item in the "/wears/" list and clear its "/wearer" property.

In short, adding a new relationship in NMC1 requires modifications to many different programs and changes to the properties of the objects to be related.

With the proposal for using objects instead of modifying properties, to invent a "wears" relationship (such as "otter wears hat") requires that we create an object and give it two properties: wearer and worn. The wearer would hold the character's reference, and the worn would hold the object's reference. No changes or updates are needed to either object.

We could further assign rules: no object can be worn unless it is marked as "wearable". Thus, we could either assign a property for that (just like NMC1) or we could create a new object "WearableMarker" and have it hold in a property the reference to the item. Now, the rule could say "to create a wearable object, a character must be given for wearer and an object that has a WearableMarker referring to it must be given for worn."

Relations and rules can be freely added to the system without changing existing objects or programs. This lets the system extend.

To use this model for real, the issues of security, logging, and efficiency would need to be addressed.

3.4 Conclusion

Building a new approach to relationships based on classes and objects to avoid the referential integrity and extensibility problems of lists and back-pointers seems like a promising approach.

There's a lot of pragmatics that need to be addressed, and a prototype needs to be built to test these ideas.

Because these relationships are referencing objects instead of modifying them there is a great potential for abuse (the permission scheme that protects the objects from being changed or read doesn't protect them from being referenced). Thus, these objects may be part of an underlying core technology instead of the same free-form object style of the VR objects.

In short, these objects are part of the machinery of the system, not part of the VR model.

A prototype for these concepts will be added to the project plan.

End of Document