From 74ab1ed03023cfe93509152810b43e46d73a323b Mon Sep 17 00:00:00 2001 From: Vincent Le Goff Date: Wed, 8 Mar 2017 21:13:27 -0800 Subject: [PATCH] Add basic structure of event types and helpers --- evennia/contrib/events/README.md | 6 +-- evennia/contrib/events/exceptions.py | 15 ++++++ evennia/contrib/events/extend.py | 72 ++++++++++++++++++++++++++++ evennia/contrib/events/helpers.py | 53 ++++++++++++++++++++ evennia/contrib/events/scripts.py | 4 ++ 5 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 evennia/contrib/events/exceptions.py create mode 100644 evennia/contrib/events/extend.py create mode 100644 evennia/contrib/events/helpers.py diff --git a/evennia/contrib/events/README.md b/evennia/contrib/events/README.md index a4256fd93..1cc8ceed3 100644 --- a/evennia/contrib/events/README.md +++ b/evennia/contrib/events/README.md @@ -122,7 +122,7 @@ This section will explain how to add new helper functions and events. Default events are great but you may need more events to fit with your purposes. For instance, if you have a `yell` command and would like a `can_yell` event in all your rooms. -The way to do this is to add, below your class definition, lines to add these events. The `create_event` function should be called. It takes the following arguments: +The way to do this is to add, below your class definition, lines to add these events. The `create_event_type` function should be called. It takes the following arguments: - The class to have these events (defined above). - The name of the event to add (str). @@ -134,7 +134,7 @@ Here's an example of adding the `can_yell` event to all your rooms: ```python # In typeclasses/rooms.py from evennia import DefaultRoom -from evennia.contrib.events.extend import create_event +from evennia.contrib.events.extend import create_event_type class Room(DefaultRoom): """ @@ -149,7 +149,7 @@ class Room(DefaultRoom): pass # Room events -create_event(Room, "can_yell", ["character", "room", "message"], """ +create_event_type(Room, "can_yell", ["character", "room", "message"], """ Can the character yell in this room? This event is called when a character uses the 'yell' command to yell in this room. This event is called BEFORE the character diff --git a/evennia/contrib/events/exceptions.py b/evennia/contrib/events/exceptions.py new file mode 100644 index 000000000..b33e4a3e2 --- /dev/null +++ b/evennia/contrib/events/exceptions.py @@ -0,0 +1,15 @@ +""" +Module containing the exceptions of the event system. +""" + +class InterruptEvent(RuntimeError): + + """ + Interrupt the current event. + + You shouldn't have to use this exception directly, probably use the + `deny()` function that handles it instead. + + """ + + pass diff --git a/evennia/contrib/events/extend.py b/evennia/contrib/events/extend.py new file mode 100644 index 000000000..39e5d8aaf --- /dev/null +++ b/evennia/contrib/events/extend.py @@ -0,0 +1,72 @@ +""" +Functions to extend the event system. + +These funcitons are not helpers (helpers are in a separate module) +and are designed to be used more by developers to add event types. + +""" + +from evennia import logger +from evennia import ScriptDB + +def create_event_type(typeclass, event_name, variables, help_text): + """ + Create a new event type for a specific typeclass. + + Args: + typeclass (type): the class defining tye typeclass to be used. + event_name (str): the name of the event to be added. + variables (list of str): a list of variable names. + help_text (str): a help text of the event. + + Events obey the inheritance hierarchy: if you set an event on + DefaultRoom, for instance, and if your Room typeclass inherits + from DefaultRoom (the default), the event will be available to + all rooms. Objects of the typeclass set in argument will be + able to set one or more events of that name. + + If the event already exists in the typeclass, replace it. + + """ + typeclass_name = typeclass.__module__ + "." + typeclass.__name__ + try: + script = ScriptDB.objects.get(db_key="event_handler") + except ScriptDB.DoesNotExist: + logger.log_err("Can't create event {} in typeclass {}, the " \ + "script handler isn't defined".format(name, typeclass_name)) + return + + # Get the event types for this typeclass + event_types = script.db.event_types.get(typeclass_name, {}) + if not event_types: + script.db.event_types[typeclass_name] = event_types + + # Add or replace the event + event_types[event_name] = (variables, help_text) + +def del_event_type(typeclass, event_name): + """ + Delete the event type for this typeclass. + + Args: + typeclass (type): the class defining the typeclass. + event_name (str): the name of the event to be deleted. + + If you want to delete an event type, you need to remove it from + the typeclass that defined it: other typeclasses in the inheritance + hierarchy are not affected. This method doesn't remove the + already-created events associated with individual objects. + + """ + typeclass_name = typeclass.__module__ + "." + typeclass.__name__ + try: + script = ScriptDB.objects.get(db_key="event_handler") + except ScriptDB.DoesNotExist: + logger.log_err("Can't create event {} in typeclass {}, the " \ + "script handler isn't defined".format(name, typeclass_name)) + return + + # Get the event types for this typeclass + event_types = script.db.event_types.get(typeclass_name, {}) + if event_name in event_types: + del event_types[event_name] diff --git a/evennia/contrib/events/helpers.py b/evennia/contrib/events/helpers.py new file mode 100644 index 000000000..b763d5781 --- /dev/null +++ b/evennia/contrib/events/helpers.py @@ -0,0 +1,53 @@ +""" +Module defining basic helpers for the event system. + + +Hlpers are just Python function that can be used inside of events. They + +""" + +from evennia import ObjectDB +from evennia.contrib.events.exceptions import InterruptEvent + +def deny(): + """ + Deny, that is stop, the event here. + + This function will raise an exception to terminate the event + in a controlled way. If you use this function in an event called + prior to a command, the command will be cancelled as well. Good + situations to use the `deny()` function are in events that begins + by `can_`, because they usually can be cancelled as easily as that. + + """ + raise InterruptEvent + +def get(**kwargs): + """ + Return an object with the given search option or None if None is found. + + This function is very useful to retrieve objects with a specific + ID. You know that room #32 exists, but you don't have it in + the event variables. Quite simple: + room = get(id=32) + + This function doesn't perform a search on objects, but a direct + search in the database. It's recommended to use it for objects + you know exist, using their IDs or other unique attributes. + Looking for objects by key is possible (use `db_key` as an + argument) but remember several objects can share the same key. + + Kwargs: + Any searchable data or property (id, db_key, db_location...). + + Returns: + The object found that meet these criteria for research, or + None if none is found. + + """ + try: + object = ObjectDB.objects.get(**kwargs) + except ObjectDB.DoesNotExist: + object = None + + return object diff --git a/evennia/contrib/events/scripts.py b/evennia/contrib/events/scripts.py index 593a596e2..5f4af422f 100644 --- a/evennia/contrib/events/scripts.py +++ b/evennia/contrib/events/scripts.py @@ -12,3 +12,7 @@ class EventHandler(DefaultScript): self.key = "event_handler" self.desc = "Global event handler" self.persistent = True + + # Permanent data to be stored + self.db.event_types = {} + self.db.events = {}