Extending Bravo

Bravo is designed to be highly extensible. This document is a short guide to the basics of writing extension code for Bravo.

Asynchronous Ideas

Bravo, being built on Twisted, has inherited most of the concepts of asynchronous control flow from Twisted, and uses them liberally. Nearly every plugin method is permitted to return a Deferred in place of their actual return value.

The Good, the Bad, and the Ugly

There are a lot of modules in the standard library. Some of them should not be used in Bravo.

The following modules are blacklisted because they conflict with, or are slow compared to, Twisted’s own systems:

These modules are bad. All of them duplicate functionality available in Twisted, and do it in ways that can interfere with Twisted’s ability to do things in a speedy manner. Do not use them under any circumstances.

  • asyncore
  • multiprocessing
  • socket
  • subprocess
  • thread
  • threading

These modules are ugly. They can quite easily corrupt memory or cause server crashes, and should be used with extreme caution and very good reasons. If you don’t know exactly what you are doing, don’t use these.

  • ctypes
  • gc
  • imp
  • inspect

Parameters

Hooks should accept a single named parameter, factory, which will be provided when the hook is loaded.

The Flexibility of Commands

Bravo’s command interface is designed to feel like a regular class instead of a specialized plugin, while still providing lots of flexibility to authors. Let’s look at a simple plugin:

class Hello(object):
    """
    Say hello to the world.
    """

    implements(IChatCommand)

    def chat_command(self, username, parameters):
        greeting = "Hello, %s!" % username
        yield greeting

    name = "hello"
    aliases = tuple()
    usage = ""

This command is a simple greeter which merely echoes a salutation to its caller. It is an IChatCommand, so it only works in the in-game chat, but that should not be a problem, since there is an internal, invisible adaptation from IChatCommand to IConsoleCommand. This means that chat commands are also valid console commands, without any action on your part! Pretty cool, huh?

So, how does this plugin actually work? Well, nearly every line of this plugin is required. The first thing you’ll notice is that this plugin has a class docstring. Docstrings on commands are required; the docstring is used to provide help text. As with all chat commands, this plugin implements(IChatCommand), which lets it be discovered as a command.

The plugin implements the required chat_command(username, parameters), which will be called when a player uses the command. An interesting thing to note is that this plugin yields its return value; commands may return any iterable of lines, including a generator!

Finally, the plugin finishes with more required interface attributes: a name which will be used to call the command, a (possibly empty) list of aliases which can also be used to call the command, and a (possibly empty) usage string.