# Twisted Reality: A Flexible Framework for Virtual Worlds

- Allen Short <washort@twistedmatrix.com>
- Glyph Lefkowitz <glyph@twistedmatrix.com>

## Abstract

Flexibly modelling virtual worlds in object-oriented languages has historically been difficult; the issues arising from multiple inheritance and order-of-execution resolution have limited the sophistication of existing object-oriented simulations. Twisted Reality avoids these problems by reifying both actions and relationships, and avoiding inheritance in favor of automated composition through adapters and interfaces.

## Motivation

Text-based simulations have a long and venerable history, from games such as Infocom's Zork and Bartle's MUD to modern systems such as Inform, LambdaMOO and Cold. The general trend in the development of these systems has been toward domain-specific languages, which has largely been an improvement. However, a discrepancy remains between systems for single-user and multiple-user simulations: in single-user systems such as Inform, incremental extensibility has been sacrificed to allow for complex interaction with the world; whereas in multiple-user systems, incremental extensibility is paramount, but it is achieved at the cost of a much simpler model of interaction. Twisted Reality aims to bring the sophistication of Inform's action model to multiuser simulation.

## The Twisted Component Model

Twisted's component system is almost identical to Zope 3's. The primary element is the interface, a class used as a point of integration and documentation. Classes may declare the interfaces they implement by setting their `__implements__` attribute to a tuple of interfaces. Additional interfaces may be added to classes with `registerAdapter(adapterClass,originalClass,interface)`; when `getAdapter(obj, interfaceClass)` is called on an object, the adapter associated with that interface and class is looked up and instantiated as a wrapper around `obj`. (Alternately, if `obj` implements the requested interface, the original object is simply returned.)

### Componentized

In addition to the basic system of adapters and interfaces, Twisted has the `Componentized` class. Instances of `Componentized` hold instances of their adapters. This storage of adapter instances encourages separation of concerns; multiple related instances representing aspects of a simulation object can be automatically composed in a single Componentized instance.

`Componentized` is the heart of Twisted Reality; it is subclassed by `Thing`, the base class for all simulation objects. Functionality is added to `Thing`s with adapters; for example, the `Portable` adapter adds the abilities to be picked up and dropped.

By separating aspects of the simulation object into multiple instances, several improvements in ease of code maintenance can be realized. Persistence of simulation objects, for example, is greatly eased by `Componentized`: each adapter's state can be stored in a separate database table or similar data store.

## Parsing System

The key element missing from multiuser simulations' parsing systems is an abstract representation of actions. Current systems proceed directly from parsing the user's input to executing object-specific code. For example, LambdaMOO, one of the most popular object-oriented simulation frameworks, handles input using a non-customizable lexer which dispatches to parsing methods on simulation objects. The ColdCore framework, a similar effort, improves on this model by providing pattern-matching facilities for the lexer, but performs dispatch in essentially the same fashion. In contrast to these systems, Twisted Reality separates parsing from simulation objects entirely, keeping a global registry of parser methods which produce objects representing actions, rather than directly performing the actions. Adding this layer allows for more sophisticated parsing and sensitivity to ambiguity.

The parser in `reality.text.english` uses a relatively simple strategy: it keeps a parser registry which maps verbs (i.e., substrings at the beginning of the user input) to parser methods, and runs all methods whose prefixes match the input, collecting the actions they return. Parsing methods are added to the system by registering `Subparser`s.

    class MusicParser(english.Subparser):
        def parse_blow(self, player, instrumentName):
            actor = player.getComponent(IPlayWindInstrumentActor)
            if actor is None:
                return []
            return [PlayWindInstrument(actor, instrumentName)]

    english.registerSubparser(MusicParser())

`english.registerSubparser` collects methods prefixed with `parse_` from the subparser and places them in the parsing registry.

    a Room
    You see a rocket, a whistle, and a candle.
    Exits: a door, north
    bob: blow whistle
    You play a shrill blast upon a whistle.

Here is one of the simplest cases for the parser: `blow whistle` should obviously resolve to a single action, in this case `PlayWindInstrument`.

The parser calls `MusicParser.parse_blow` with the actor and the remainder of the input, and adds the list of actions it returns to the collection of possible actions. If only one action is possible, it immediately dispatches it. This strategy allows the parser to examine the state of the simulation before committing to a decision about what the player means. For example, the check for the actor interface is a simple form of permissions; if you don't implement the required interface, you aren't allowed to perform the action.

Since this sort of parser is quite common, it has been generalized to a simple mapping of command names to actions:

    class FireParser(english.Subparser):
        simpleTargetParsers = {"blow": Extinguish}

    english.registerSubparser(FireParser())

    bob: blow candle
    You blow out a candle.

The real test of any parsing system of this nature, of course, is its ability to handle ambiguity. Since two possibilities for parsing a command starting with blow now exist, the parser has two potential actions to examine: `PlayWindInstrument` and `Extinguish`. Obviously, only `Extinguish` makes sense, and the parser determines this by examining the interfaces on the targets and rejecting actions for which the target is invalid.

    class ExplosivesParser(english.Subparser):
        simpleToolParsers = {"blow": BlowUp}

    english.registerSubparser(ExplosivesParser())

    bob: blow door
    You fire a rocket at a door.
    *BOOM*!!
    The door shatters to pieces!

The other common case is actions with three participants -- actor, target, and tool. The parser generated here is intelligent enough to look around for an appropriate tool (again, by examining interfaces) and include it in the action.

Despite these techniques for disambiguating the user's meaning, situations will inevitably arise where multiple actions are equally valid parses. In these cases, the parser formats the list of potential actions and presents the choices to the user.

    You see a short sword, and a long sword.
    bob: get sword
    Which Target?
    1: long sword
    2: short sword
    bob: 1
    You take a long sword.

## Actions System

Actions in Twisted Reality, as in Inform, are objects representing a successful parse of a player's intentions. Actions are classified according to the number of objects they operate upon: `NoTargetAction` (actions such as `Say` or `Look`), `TargetAction` (e.g. `Eat`, `Wear`), `ToolAction` (e.g. `Open`, `Take`). When actions are defined, interfaces corresponding to the possible roles in the action are also created. When an action is instantiated, it asks the participants in the action to adapt themselves to the actor, target, or tool interfaces, as appropriate. When dispatched, the action may call handler methods on the adapted objects or dispatch subsidiary actions.

    IDamageActor = things.IThing
    class Damage(actions.ToolAction):
        def formatToActor(self):
            with = ""
            if self.tool:
                with = " with ", self.tool
            return ("You hit ",self.target) + with + (".",)
        def formatToTarget(self):
            with = ()
            if self.tool:
                with = " with ", self.tool
            return (self.actor," hits you") + with + (".",)
        def formatToOther(self):
            with = ""
            if self.tool:
                with = " with ", self.tool
            return self.actor," hits ",self.target) + with + (".",)
        def doAction(self):
            amount = self.tool.getDamageAmount()
            self.target.damage(amount)

    class Weapon(components.Adapter):
        __implements__ = IDamageTool
        def getDamageAmount(self):
            return 10

    class Damageable(components.Adapter):
        __implements__ = IDamageTarget
        def damage(self, amount):
            self.original.emitEvent("Ow! that hurt. You take %d points of damage."
                                    % amount, intensity=1)
    class HarmParser(english.Subparser):
        simpleToolParsers = {"hit":Damage}

    english.registerSubparser(HarmParser())
    components.registerAdapter(Damageable, things.Actor, IDamageTarget)

`actions.ToolAction`, via metaclass magic, creates three interfaces when subclasssed, named after the subclass: in this case, `IDamageActor`, `IDamageTarget`, and `IDamageTool`. However, since `IDamageActor` already exists, the metaclass does not clobber it. Setting `IDamageActor` to `IThing` indicates that any `Thing` may perform the `Damage` action. The other elements of the action are represented here by `Weapon` and `Damageable` as the tool and the target, respectively. The `HarmParser` adds a hit command, and the call to `registerAdapter` ensures that any `Actor`s who do not already have a component implementing `IDamageTarget` will receive a `Damageable` when needed.

    room = ambulation.Room("room")
    bob = things.Actor( "Bob")
    rodney = things.Actor("rodneY")
    sword = things.Movable("sword")

    sword.addAdapter(conveyance.Portable, True)
    sword.addAdapter(harm.Weapon, True)

    for o in rodney, bob, sword:
        o.moveTo(room)

In this example, we create instances of `Movable` `Actor` (subclasses of `Thing`), a `Room`, then adds a `Portable` adapter to the sword, allowing it to be picked up and dropped, as well as a `Weapon` adapter, and finally moves all three into the room.

    a room
    You see rodneY, and a sword.
    Bob: get sword
    You take a sword.
    Bob: hit rodney with sword
    You hit rodneY with a sword.

The parser instantiates the `Damage` action with Bob, Rodney, and the sword as actor, target, and tool. The action is dispatched, calling `Damage.doAction`, which inflicts damage upon Rodney. From Rodney's perspective:

    a room
    You see Bob, and a sword.
    Bob takes a sword.
    Bob hits you with a sword.
    Ow! that hurt. You take 10 points of damage.
    rodneY:

The primary advantage of this actions system is that it provides a central point for dispatching object-specific behaviour in a customizable manner. This mechanism prevents order-of-execution problems: in other simulations of this type, combining multiple game effects is difficult since the connections between them are not made explicit. When confronted with ambiguity, TR's action system refuses to guess: all combinations of effects that make sense must be implemented separately. The Adapters system makes this manageable even in the face of arbitrarily extended complexity.

Also, it allows for centralized handling of string formatting, instead of having each actor or target handle output of event descriptions. For example, suppose there is a zone prohibiting PvP combat. The `Damage` action can suppress the usual messages describing combat (as well as the actual damage routines) since it is responsible for generating them.

## Composing Simulations with Adapters

The combination of these features -- an incrementally extendable parser, actions as first-class objects, componentized simulation objects -- provide a powerful basis for the composition of simulations within a virtual world, often enabling extensions to the world and object behaviour without touching unrelated code. For example, to add armor that reduces damage to the simple combat simulation described above, we add an `Armor` class which forwards the `IDamageTarget` interface:

    class Armor(raiment.Wearable):
        __implements__ = IDamageTarget,  raiment.IWearTarget, raiment.IUnwearTarget
        originalTarget = None
        armorCoefficient = 0.5
        def dress(self, wearer):
            originalTarget = wearer.getComponent(IDamageTarget)
            if originalTarget:
                self.originalTarget = originalTarget
                wearer.original.setComponent(IDamageTarget, self)

        def undress(self, wearer):
            if self.originalTarget:
                wearer.setComponent(IDamageTarget, self.originalTarget)

        def damage(self, amount):
            self.original.emitEvent("Your armor cushions the blow.", intensity=2)
            if self.originalTarget:
                self.originalTarget.damage(amount * self.armorCoefficient)

`Armor` inherits from the `Wearable` adapter, and thus receives notification of the player wearing or removing it. When this happens, it forwards or unforwards the `damage` method, respectively.

    a room
    You see an armor, Bob, and a sword.
    rodneY: take armor
    You take an armor.
    rodneY: wear armor
    You put on an armor.
    Bob hits you with a sword.
    Your armor cushions the blow.
    Ow! that hurt. You take 5 points of damage.

In this fashion, the combat simulation can be extended to deal with various types of weapons, armor, damageable objects, and types of damage, with little or no changes to existing code.

Now, let us consider a second type of simulation common to virtual worlds: shops. We wish to prevent unpaid items from leaving the shop, and to have a price associated with each item.

    class IVendor(components.Interface): pass
    class IMerchandise(components.Interface): pass

    class Buy(actions.TargetAction):
        def formatToOther(self):
            return ""
        def formatToActor(self):
            return ("You buy ",self.target," from ",self.vendor," for ",
                    self.target.price," zorkmids.")

        def doAction(self):
            vendors = self.actor.original.lookFor(None, IVendor)
            if vendors:
                #assume only one vendor per room, for now
                self.vendor = vendors[0]
            else:
                raise errors.Failure("There appears to be no shopkeeper here "
                                     "to receive your payment.")
            amt = self.target.price
            self.actor.withdraw(amt)
            self.vendor.buy(self.target, amt)

    class ShopParser(english.Subparser):
        simpleTargetParsers = {"buy": Buy}
    english.registerSubparser(ShopParser())

The basic behaviour for buying an object in a shop is simple: first, a vendor is located, the price is looked up, then money is transferred from the buyer's account to the vendor's.

    class Customer(components.Adapter):
        __implements__ = IBuyActor

        def withdraw(self, amt):
            "interface to accounting system goes here"

    class Vendor(components.Adapter):
        __implements__ = IVendor

        def shoutPrice(self, merch, cust):
            n = self.getComponent(english.INoun)
            title = ('creature', 'sir','lady'
                     )[cust.getComponent(things.IThing).gender]
            merchName = merch.original.getComponent(english.INoun).name))
            self.original.emitEvent('%s says "For you, good %s, only %d '
                                    'zorkmids for this %s."' % (n.nounPhrase(cust),
                                                                title, merch.price,
                                                                merchName))

        def buy(self, merchandise, amount):
            self.deposit(amount)
            merchandise.original.removeComponent(merchandise)

        def stock(self, obj, price):
            m = Merchandise(obj)
            m.price = price
            m.owner = self
            m.home = self.original.location
            obj.addComponent(m, ignoreClass=1)

        def deposit(self, amt):
            "more accounting code"

The essential operations for management of shop inventory are `Vendor.stock` and `Vendor.buy`, which add and remove a `Merchandise` adapter, which stores the state related to the shop simulation for the object (in this case, its price, its owner, and the location it lives).

    A weapons shop.  You see a long sword, and Asidonhopo.
    Exits: a Secret Trapdoor, down; a Security Door, north
    bob: get sword
    You take a long sword.
    Asidonhopo says "For you, good sir, only 100 zorkmids for this long sword."

To enforce our anti-theft policy, we put constraints on the exits to the shop.

    class ShopDoor(ambulation.Door):
        def collectImplementors(self, asker, iface, collection, seen,
                                event=None, name=None, intensity=2):
            if iface == ambulation.IWalkTarget:
                unpaidItems = asker.searchContents(None, IMerchandise)
                if unpaidItems:
                    collection[self] = things.Refusal(self, "You cant leave, "
                                                            "you haven't paid!")
                    return

            ambulation.Door.collectImplementors(self, asker, iface,
                                                collection, seen, event,
                                                name, intensity)
            return collection

`collectImplementors` is the means by which queries for action participants are accomplished. It is a rather general graph-traversal mechanism and thus takes a few arguments: `asker` is the object that initiated the query. `iface` is the interface the results must conform to, `collection` is the results so far, and `seen` is a collection of objects already visited. The check done here is fairly simple: it refuses queries for `IWalkTarget`s (the interface needed for walking between rooms) if the asker contains things that implement `IMerchandise`, in particular unpaid items. Otherwise, it passes on the query to its superclass.

    bob: go north
    You cant leave, you haven't paid!

Here, the Security Door examines the actor's contents for objects implementing IMerchandise. Since the sword still has a Merchandise adapter attached, the passage is barred.

    bob: go down

However, relying on the exits to contain merchandise is potentially error-prone; it demands knowing about all forms of locomotion in advance. If an unsecured exit from the shop exists, or the player has the ability to teleport, this form of security can be bypassed. Therefore, it is advantageous to have the Merchandise adapter itself keep the item within the shop.

    class Merchandise(components.Adapter):
        __implements__ = IMerchandise, things.IMoveListener, IBuyTarget

        def thingArrived(*args):
            pass
        def thingLeft(*args):
            pass
        def thingMoved(self, emitter, event):
            if self.original == emitter and isinstance(event, conveyance.Take):
                self.owner.shoutPrice(self, self.original.location)
            if self.original.getOutermostRoom() != self.home:
                self.original.emitEvent("The %s vanishes with a *foop*."
                                        % self.getComponent(english.INoun).name)
                self.original.moveTo(self.home)

When objects move, they broadcast events to nearby things (where nearby is determined, again, by `collectImplementors`) that implement `IMoveListener`. In this case, the `Merchandise` adapter listens for being picked up, and prompts the shopkeeper to quote the price, and also checks to make sure it is contained by its home room. If the player manages to leave the shop with unpaid merchandise --

    The long sword vanishes with a *foop*.

then it sets its location to its home room and informs the prospective shoplifter he no longer has his prize.

## Future Directions

Current development efforts focus on enlarging the standard library of simulation objects and behaviour, developing web-based interfaces to the simulation, and improving the persistence layer. Possible extensions include client-side generation of action objects, enabling the development of graphical interfaces, or adapting the text system to other languages than English.

## Conclusions

As seen in these examples, Twisted Reality provides features not found in other object-oriented simulation frameworks. The component model allows automatic aggregation of related objects; the actions system provides a mechanism for precise control of game effects; and the parser enables incremental extension of user input handling. Combined, they provide a powerful basis for modelling virtual worlds by composing simulations.

## Acknowledgements

Thanks to Chris Armstrong and Donovan Preston for contributions to Twisted Reality, and to Ying Li for editorial assistance.

## References

- Jason Asbahr, Beyond: A Portable Virtual World Simulation Framework, Proceedings of the Seventh International Python Conference (1998).
- Pavel Curtis, LambdaMOO programmer's manual, 1997.
- Jim Fulton, Zope Component Architecture
- Brandon Gillespie, ColdC Reference Manual, 2001.
- Glyph Lefkowitz, and Moshe Zadka, The Twisted Network Framework, Proceedings of the Tenth International Python Conference (2002): 83.
- Graham Nelson, The Inform Designer's Manual. 4th ed. (St Charles, IL: Interactive Fiction Library, 2001).