festivecasual / sigma-mud

Experimental MUD server in Python
0 stars 0 forks source link

Whitepaper: Event Framework #18

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
A working whitepaper to discuss the soon-to-be-essential event framework, how 
it facilitates rich interactions (the "Captain X" mechanism, etc.), and how it 
supports the combat and magic systems.

Before beginning with conceptual specifics, some discussion about entity flags 
and their relevance to future design elements may be helpful.  The original 
intent of flags has been to aid fully or partially designer-exposed modules 
(tasks, handlers, etc.) in preventing or performing actions on a selective 
subset of objects.  A good example is the locomotion task, which can key off a 
flag to decide which denizens should wander randomly between rooms.

In view of this, it does not seem desirable to expect a certain set of flags 
within the core engine.  I propose a design guideline that any flag required by 
a core engine process should be converted to a specific XML tag.

The event framework, perhaps more than any other pluggable layer, will provide 
an excellent opportunity to leverage flags.  Entities are more than capable of 
introspecting on their flags to decide if an event is relevant to them 
specifically.  Of course, we also would aim to bind events only to objects of 
relevance for efficiency reasons.

It is interesting to note that modules are capable of modifying an entity's own 
flags dynamically to record state in a complex action.

Modules are tasked with maintaining and interpreting their relevant flags, but 
a libsigma support function should support event-created flags that record 
their originator using the delimited concept also employed in area files.  This 
support should also provide for a key/value arrangement, separated by an equals 
sign, to support basic key-value behavior.  This means tags could be in various 
forms:  'aggressive' (global), 'decay=5' (global, key/value), 
'shopkeeper/closed' (module-specific), or 'autospell/cast=boost' 
(module-specific, key/value).

Now to the proposal for an events implementation:

From an object standpoint, I propose a "callable chain" class for each event.  
These chains are instanced at the lowest level of universal application, with 
most events being bound at the Character level, and a narrow set of specialized 
events attached to Items and Rooms.

The callable chain is a derivative of the built-in list class that implements 
the __call__(...) member function:

class Event(list):
    def __init__(self, e=[]):
        super(Event, self).__init__()
        self.extend(e)

    def __call__(self, context=None):
        for f in self:
            if f(context):
                return True
        return False

Observations on this:

  - The Event is itself a list and therefore provides all the convenient capabilities of a list: slices, appends, prepends, deletes, searches, etc.  Virtually no custom support is necessary.

  - When the Event is called as a function, the intrinsic list iterates over itself, calling each attached function with a single argument, similar to the context provided to a handler function.

  - A function result that returns a True value immediately terminates execution of the rest of the chain.  While not to be used commonly, it could be at times useful to prepend a certain event function that short-circuits execution before a built-in or earlier-defined method is invoked.  Most functions should simply execute and terminate without returning a value, or returning False.

Similarly to the handler framework, events will only require a decorator in 
their declaration which adds them to a mapping object.  No further constraint 
on organization or implementation should be necessary, aside from the fact that 
all events should expect a single context argument.

The XML for this should be quite simple:

<event type="item_offered" function="thank" />

or perhaps more actively-phrased:

<event on="item_offered" call="thank" />

Complex event insertions can be handled by a task module (often only utilizing 
the 'init' phase of the module).  This should rarely be necessary.  To head off 
perhaps the greatest risk of a "complex" insertion, we can provide 'prepend' to 
manage insertion order:

<event on="item_offered" call="thank" prepend="true" />

One final facility should be a provision for flags within the event 
declaration, to wit:

<event on="item_offered" call="bless">
    <flag>vocal</flag>
    <flag>emote=bow</flag>
</event>

Utilizing the special libsigma support, these flags are automatically converted 
to bless/vocal and bless/emote=bow.

This facility provides a means of communicating configuration to the module 
without modifying the module code, and at run-time can additionally aid in 
maintaining state.

Technically speaking, this XML definition framework with flag capability 
eliminates the need for personas, and they will be removed from the codebase 
for now.  If present eventually, their application under this system would be 
to automate the application of a number of events, flags, and other 
modification to an entity.  This would allow construction of a complex 
Character profile ("Captain X") that would carry a payload of operations (item 
accepting, shopkeeping, periodic speaking, etc.).  The counter-argument would 
be a relatively low net decrease in complexity over simply setting up the 
entity by hand.

A final note on actual implementation: utilizing a simple set of "system" 
events bound mostly to Denizens will vastly simplify operations that otherwise 
would be carried in libsigma or handler implementations as special cases for 
Denizens.

For example, an 'accept' event added by default to all Denizens would allow the 
offer framework to apply universally while making Denizens' acceptance 
automatic.  'accept' could either respect an entity flag requesting no 
auto-accept of items, or alternatively a special 'stop' event (system-default 
or custom) could be prepended to the default chain as a no-op save returning 
True and terminating the chain.

Further default events could be useful in combat, and a rich event framework 
could significantly simplify a shopkeeper implementation, for example.

Original issue reported on code.google.com by bmcca...@gmail.com on 3 Sep 2010 at 2:15