Add an event handler on all objects

This commit is contained in:
Vincent Le Goff 2017-03-22 16:57:36 -07:00 committed by Griatch
parent 81f4c590bd
commit 38563a6593
4 changed files with 226 additions and 11 deletions

View file

@ -67,7 +67,7 @@ def patch_hook(typeclass, method_name):
inheritance tree for a couple of methods.
"""
hook = getattr(typeclass, method_name)
hook = getattr(typeclass, method_name, None)
def wrapper(method):
"""Wrapper around the hook."""
def overridden_hook(*args, **kwargs):

View file

@ -0,0 +1,182 @@
"""
Module containing the EventHandler for individual objects.
"""
from collections import namedtuple
class EventsHandler(object):
"""
The event handler for a specific object.
The script that contains all events will be reached through this
handler. This handler is therefore a shortcut to be used by
developers. This handler (accessible through `obj.events`) is a
shortcut to manipulating events within this object, getting,
adding, editing, deleting and calling them.
"""
script = None
def __init__(self, obj):
self.obj = obj
def all(self):
"""
Return all events linked to this object.
Returns:
All events in a dictionary event_name: event}. The event
is returned as a namedtuple to simply manipulation.
"""
events = {}
handler = type(self).script
if handler:
dicts = handler.get_events(self.obj)
for event_name, in_list in dicts.items():
new_list = []
for event in in_list:
event = self.format_event(event)
new_list.append(event)
if new_list:
events[event_name] = new_list
return events
def get(self, event_name):
"""
Return the events associated with this name.
Args:
event_name (str): the name of the event.
This method returns a list of Event objects (namedtuple
representations). If the event name cannot be found in the
object's events, return an empty list.
"""
return self.all().get(event_name, [])
def add(self, event_name, code, author=None, valid=False, parameters=""):
"""
Add a new event for this object.
Args:
event_name (str): the name of the event to add.
code (str): the Python code associated with this event.
author (Character or Player, optional): the author of the event.
valid (bool, optional): should the event be connected?
parameters (str, optional): optional parameters.
Returns:
The event definition that was added or None.
"""
handler = type(self).script
if handler:
return self.format_event(handler.add_event(self.obj, event_name, code,
author=author, valid=valid, parameters=parameters))
def edit(self, event_name, number, code, author=None, valid=False):
"""
Edit an existing event bound to this object.
Args:
event_name (str): the name of the event to edit.
number (int): the event number to be changed.
code (str): the Python code associated with this event.
author (Character or Player, optional): the author of the event.
valid (bool, optional): should the event be connected?
Returns:
The event definition that was edited or None.
Raises:
RuntimeError if the event is locked.
"""
handler = type(self).script
if handler:
return self.format_event(handler.edit_event(self.obj, event_name,
number, code, author=author, valid=valid))
def remove(self, event_name, number):
"""
Delete the specified event bound to this object.
Args:
event_name (str): the name of the event to delete.
number (int): the number of the event to delete.
Raises:
RuntimeError if the event is locked.
"""
handler = type(self).script
if handler:
handler.del_event(self.obj, event_name, number)
def call(self, event_name, *args, **kwargs):
"""
Call the specified event(s) bound to this object.
Args:
event_name (str): the event name to call.
*args: additional variables for this event.
Kwargs:
number (int, optional): call just a specific event.
parameters (str, optional): call an event with parameters.
locals (dict, optional): a locals replacement.
Returns:
True to report the event was called without interruption,
False otherwise.
"""
handler = type(self).script
if handler:
return handler.call_event(self.obj, event_name, *args, **kwargs)
return False
@staticmethod
def format_event(event):
"""
Return the Event namedtuple to represent the specified event.
Args:
event (dict): the event definition.
The event given in argument should be a dictionary containing
the expected fields for an event (code, author, valid...).
"""
if "obj" not in event:
event["obj"] = None
if "name" not in event:
event["name"] = "unknown"
if "number" not in event:
event["number"] = -1
if "code" not in event:
event["code"] = ""
if "author" not in event:
event["author"] = None
if "valid" not in event:
event["valid"] = False
if "parameters" not in event:
event["parameters"] = ""
if "created_on" not in event:
event["created_on"] = None
if "updated_by" not in event:
event["updated_by"] = None
if "updated_on" not in event:
event["updated_on"] = None
return Event(**event)
Event = namedtuple("Event", ("obj", "name", "number", "code", "author",
"valid", "parameters", "created_on", "updated_by", "updated_on"))

View file

@ -6,13 +6,14 @@ from datetime import datetime, timedelta
from Queue import Queue
from django.conf import settings
from evennia import DefaultScript, ScriptDB
from evennia import DefaultObject, DefaultScript, ScriptDB
from evennia import logger
from evennia.utils.dbserialize import dbserialize
from evennia.utils.utils import all_from_module, delay
from evennia.contrib.events.custom import connect_event_types, \
get_next_wait, patch_hooks
from evennia.contrib.events.custom import (
connect_event_types, get_next_wait, patch_hooks)
from evennia.contrib.events.exceptions import InterruptEvent
from evennia.contrib.events.handler import EventsHandler as Handler
from evennia.contrib.events import typeclasses
class EventHandler(DefaultScript):
@ -65,6 +66,10 @@ class EventHandler(DefaultScript):
delay(seconds, complete_task, task_id)
# Place the script in the EventsHandler
Handler.script = self
DefaultObject.events = typeclasses.PatchedObject.events
def get_events(self, obj):
"""
Return a dictionary of the object's events.
@ -80,7 +85,21 @@ class EventHandler(DefaultScript):
when several objects would share events.
"""
return self.db.events.get(obj, {})
obj_events = self.db.events.get(obj, {})
events = {}
for event_name, event_list in obj_events.items():
new_list = []
for i, event in enumerate(event_list):
event = dict(event)
event["obj"] = obj
event["name"] = event_name
event["number"] = i
new_list.append(event)
if new_list:
events[event_name] = new_list
return events
def get_event_types(self, obj):
"""
@ -212,6 +231,13 @@ class EventHandler(DefaultScript):
elif valid and (obj, event_name, number) in self.db.to_valid:
self.db.to_valid.remove((obj, event_name, number))
# Build the definition to return (a dictionary)
definition = dict(events[number])
definition["obj"] = obj
definition["name"] = event_name
definition["number"] = number
return definition
def del_event(self, obj, event_name, number):
"""
Delete the specified event.
@ -259,11 +285,11 @@ class EventHandler(DefaultScript):
i += 1
# Update locked event
for line in self.db.locked:
for i, line in enumerate(self.db.locked):
t_obj, t_event_name, t_number = line
if obj is t_obj and event_name == t_event_name:
if number > t_number:
line[2] -= 1
if number < t_number:
self.db.locked[i] = (t_obj, t_event_name, t_number - 1)
# Delete time-related events associated with this object
for script in list(obj.scripts.all()):

View file

@ -4,9 +4,16 @@ Patched typeclasses for Evennia.
from evennia import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
from evennia import ScriptDB
from evennia.utils.utils import inherits_from
from evennia.contrib.events.custom import create_event_type, patch_hook, \
create_time_event
from evennia.utils.utils import inherits_from, lazy_property
from evennia.contrib.events.custom import (
create_event_type, patch_hook, create_time_event)
from evennia.contrib.events.handler import EventsHandler
class PatchedObject(object):
@lazy_property
def events(self):
"""Return the EventsHandler."""
return EventsHandler(self)
class PatchedExit(object):