From 5a465746c5e74c20ff51509960946e41ea2933f6 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Mon, 6 Apr 2009 16:19:07 +0000 Subject: [PATCH] Adjust some function names in the script parents to be at_ instead of a_. Also re-designed the scheduling system to be a lot more pythonic and easy to use. Utilizes classes to represent events. Much easier to plug in events from within the game directory now as well. --- src/commands/general.py | 8 +-- src/commands/info.py | 6 +- src/commands/objmanip.py | 4 ++ src/events.py | 88 ++++++++++++++++++++++-- src/scheduler.py | 110 ++++-------------------------- src/script_parents/basicobject.py | 79 ++++++++++++--------- src/script_parents/basicplayer.py | 4 +- src/server.py | 5 +- 8 files changed, 159 insertions(+), 145 deletions(-) diff --git a/src/commands/general.py b/src/commands/general.py index 51df9a9530..4f71dbffcc 100644 --- a/src/commands/general.py +++ b/src/commands/general.py @@ -136,10 +136,10 @@ def cmd_look(command): target_obj = source_object.get_location() # SCRIPT: Get the item's appearance from the scriptlink. - source_object.emit_to(target_obj.scriptlink.return_appearance(source_object)) + source_object.emit_to(target_obj.scriptlink.return_appearance(pobject=source_object)) # SCRIPT: Call the object's script's a_desc() method. - target_obj.scriptlink.a_desc(source_object) + target_obj.scriptlink.at_desc(pobject=source_object) GLOBAL_CMD_TABLE.add_command("look", cmd_look) def cmd_get(command): @@ -179,7 +179,7 @@ def cmd_get(command): exclude=source_object) # SCRIPT: Call the object's script's a_get() method. - target_obj.scriptlink.a_get(source_object) + target_obj.scriptlink.at_get(source_object) GLOBAL_CMD_TABLE.add_command("get", cmd_get) def cmd_drop(command): @@ -211,7 +211,7 @@ def cmd_drop(command): exclude=source_object) # SCRIPT: Call the object's script's a_drop() method. - target_obj.scriptlink.a_drop(source_object) + target_obj.scriptlink.at_drop(source_object) GLOBAL_CMD_TABLE.add_command("drop", cmd_drop), def cmd_examine(command): diff --git a/src/commands/info.py b/src/commands/info.py index 2fe8fdfe38..83898cdc88 100644 --- a/src/commands/info.py +++ b/src/commands/info.py @@ -110,9 +110,9 @@ def cmd_ps(command): source_object.emit_to("-- Interval Events --") for event in scheduler.schedule: source_object.emit_to(" [%d/%d] %s" % ( - scheduler.get_event_nextfire(event), - scheduler.get_event_interval(event), - scheduler.get_event_description(event))) + event.get_nextfire(), + event.interval, + event.description)) source_object.emit_to("Totals: %d interval events" % (len(scheduler.schedule),)) GLOBAL_CMD_TABLE.add_command("@ps", cmd_ps, priv_tuple=("genperms.process_control")), diff --git a/src/commands/objmanip.py b/src/commands/objmanip.py index 4b732283ae..754fa8baf2 100644 --- a/src/commands/objmanip.py +++ b/src/commands/objmanip.py @@ -741,6 +741,10 @@ def cmd_destroy(command): source_object.emit_to("That object is already destroyed.") return + # Run any scripted things that happen before destruction. + target_obj.scriptlink.at_pre_destroy(pobject=source_object) + + # Notify destroyer and do the deed. source_object.emit_to("You destroy %s." % target_obj.get_name()) target_obj.destroy() GLOBAL_CMD_TABLE.add_command("@destroy", cmd_destroy, diff --git a/src/events.py b/src/events.py index 5d69660de9..3f3e68ac7b 100644 --- a/src/events.py +++ b/src/events.py @@ -1,11 +1,89 @@ +""" +Holds the global events scheduled in scheduler.py. + +Events are sub-classed from IntervalEvent (which is not to be used directly). +Create your sub-class, call src.scheduler.add_event(YourEventClass()) to add +it to the global scheduler. +""" +import time +from twisted.internet import task import session_mgr +from src import scheduler -""" -Holds the events scheduled in scheduler.py. -""" +class IntervalEvent(object): + """ + Represents an event that is triggered periodically. Sub-class this and + fill in the stub function. + """ + # This is what shows up on @ps in-game. + name = None + # An interval (in seconds) for execution. + interval = None + # A timestamp (int) for the last time the event was fired. + time_last_executed = None + # A reference to the task.LoopingCall object. + looped_task = None + + def __unicode__(self): + """ + String representation of the event. + """ + return self.name + + def start_event_loop(self): + """ + Called to start up the event loop when the event is added to the + scheduler. + """ + # Set the call-back function for the task to trigger_event, but pass + # a reference to the event function. + self.looped_task = task.LoopingCall(self.fire_event) + # Start the task up with the specified interval. + self.looped_task.start(self.interval, now=False) + + def event_function(self): + """ + ### Over-ride this in your sub-class. ### + """ + pass + + def get_nextfire(self): + """ + Returns a value in seconds when the event is going to fire off next. + """ + return (self.time_last_executed + self.interval) - time.time() + + def set_lastfired(self): + """ + Sets the timestamp (int) that the event was last fired. + """ + self.time_last_executed = time.time() + + def fire_event(self): + """ + Set the last ran stamp and fire off the event. + """ + self.set_lastfired() + self.event_function() -def evt_check_sessions(): +class IEvt_Check_Sessions(IntervalEvent): """ Event: Check all of the connected sessions. """ - session_mgr.check_all_sessions() + name = 'IEvt_Check_Sessions' + interval = 60 + description = "Session consistency checks." + + def event_function(self): + """ + This is the function that is fired every self.interval seconds. + """ + session_mgr.check_all_sessions() + +def add_global_events(): + """ + When the server is started up, this is triggered to add all of the + events in this file to the scheduler. + """ + # Create an instance and add it to the scheduler. + scheduler.add_event(IEvt_Check_Sessions()) \ No newline at end of file diff --git a/src/scheduler.py b/src/scheduler.py index a65da383f7..fcbbca8988 100644 --- a/src/scheduler.py +++ b/src/scheduler.py @@ -1,112 +1,28 @@ -import time -from twisted.internet import task -from src import events """ This file contains the event scheduler system. ADDING AN EVENT: -* Create an event function to call. -* Add an entry to the 'schedule' dictionary here. +* Create an event sub-class in events.py. +* Add it to the add_global_events() function at the end of the module. * Profit. """ -# Dictionary of events with a list in the form of: -# [, , , , ] -schedule = { - 'evt_check_sessions': [events.evt_check_sessions, # Function - 60, # Interval (seconds) - time.time(), # Last time executed (now) - None, # Task object - "Session consistency checks."] # Description -} +# List of IntervalEvent sub-classed objects. +schedule = [] def start_events(): """ Start the event system, which is built on Twisted's framework. """ for event in schedule: - event_func = get_event_function(event) - - if callable(event_func): - # Set the call-back function for the task to trigger_event, but pass - # a reference to the event function. - event_task = task.LoopingCall(trigger_event, event_func, event) - # Start the task up with the specified interval. - event_task.start(get_event_interval(event), now=False) - # Set a reference to the event's task object in the dictionary so we - # can re-schedule, start, and stop events from elsewhere. - set_event_taskobj(event, event_task) - -def get_event(event_name): + event.start_event_loop() + +def add_event(event): """ - Return the relevant entry in the schedule dictionary for the named event. - - event_name: (string) The key of the event in the schedule dictionary. + Adds an event instance to the scheduled event list. + + Args: + * event: (IntervalEvent) The event to add to the scheduler. """ - return schedule.get(event_name, None) - -def get_event_function(event_name): - """ - Return a reference to the event's function. - - event_name: (string) The key of the event in the schedule dictionary. - """ - return get_event(event_name)[0] - -def get_event_interval(event_name): - """ - Return the event's execution interval. - - event_name: (string) The key of the event in the schedule dictionary. - """ - return get_event(event_name)[1] - -def get_event_nextfire(event_name): - """ - Returns a value in seconds when the event is going to fire off next. - - event_name: (string) The key of the event in the schedule dictionary. - """ - return (get_event(event_name)[2]+get_event_interval(event_name))-time.time() - -def get_event_taskobj(event_name): - """ - Returns an event's task object. - - event_name: (string) The key of the event in the schedule dictionary. - """ - return get_event(event_name)[3] - -def get_event_description(event_name): - """ - Returns an event's description. - - event_name: (string) The key of the event in the schedule dictionary. - """ - return get_event(event_name)[4] - -def set_event_taskobj(event_name, taskobj): - """ - Sets an event's task object. - - event_name: (string) The key of the event in the schedule dictionary. - """ - get_event(event_name)[3] = taskobj - -def set_event_lastfired(event_name): - """ - Sets an event's last fired time. - - event_name: (string) The key of the event in the schedule dictionary. - """ - get_event(event_name)[2] = time.time() - -def trigger_event(event_func, event_name): - """ - Update the last ran time and fire off the event. - - event_func: (func_reference) Reference to the event function to fire. - eventname: (string) The name of the event (as per schedule dict). - """ - event_func() - set_event_lastfired(event_name) + schedule.append(event) + event.start_event_loop() \ No newline at end of file diff --git a/src/script_parents/basicobject.py b/src/script_parents/basicobject.py index 3b4bfe0232..df2ebd9029 100644 --- a/src/script_parents/basicobject.py +++ b/src/script_parents/basicobject.py @@ -10,15 +10,15 @@ SCRIPT_DEFAULT_OBJECT variable in settings.py to point to the new class. from src import ansi class EvenniaBasicObject(object): - def __init__(self, source_obj, *args, **kwargs): + def __init__(self, scripted_obj, *args, **kwargs): """ Get our ducks in a row. - source_obj: (Object) A reference to the object being scripted (the child). + scripted_obj: (Object) A reference to the object being scripted (the child). """ - self.source_obj = source_obj + self.scripted_obj = scripted_obj - def a_desc(self, pobject): + def at_desc(self, pobject=None): """ Perform this action when someone uses the LOOK command on the object. @@ -26,10 +26,43 @@ class EvenniaBasicObject(object): * pobject: (Object) The object requesting the action. """ # Un-comment the line below for an example - #print "SCRIPT TEST: %s looked at %s." % (pobject, self.source_obj) + #print "SCRIPT TEST: %s looked at %s." % (pobject, self.scripted_obj) + pass + + def at_pre_destroy(self, pobject=None): + """ + Performed right before an object is destroyed. + + values: + * pobject: (Object) The object requesting the action. + """ + # Un-comment the line below for an example + #print "SCRIPT TEST: %s looked at %s." % (pobject, self.scripted_obj) pass - def return_appearance(self, pobject): + def at_get(self, pobject): + """ + Perform this action when someone uses the GET command on the object. + + values: + * pobject: (Object) The object requesting the action. + """ + # Un-comment the line below for an example + #print "SCRIPT TEST: %s got %s." % (pobject, self.scripted_obj) + pass + + def at_drop(self, pobject): + """ + Perform this action when someone uses the DROP command on the object. + + values: + * pobject: (Object) The object requesting the action. + """ + # Un-comment the line below for an example + #print "SCRIPT TEST: %s dropped %s." % (pobject, self.scripted_obj) + pass + + def return_appearance(self, pobject=None): """ Returns a string representation of an object's appearance when LOOKed at. @@ -37,9 +70,12 @@ class EvenniaBasicObject(object): * pobject: (Object) The object requesting the action. """ # This is the object being looked at. - target_obj = self.source_obj - # See if the envoker sees dbref numbers. - show_dbrefs = pobject.sees_dbrefs() + target_obj = self.scripted_obj + # See if the envoker sees dbref numbers. + if pobject: + show_dbrefs = pobject.sees_dbrefs() + else: + show_dbrefs = False description = target_obj.get_description() if description is not None: @@ -52,13 +88,14 @@ class EvenniaBasicObject(object): target_obj.get_name(show_dbref=show_dbrefs), ) + # Storage for the different object types. con_players = [] con_things = [] con_exits = [] for obj in target_obj.get_contents(): if obj.is_player(): - if obj != pobject and obj.is_connected_plr(): + if (obj != pobject and obj.is_connected_plr()) or pobject == None: con_players.append(obj) elif obj.is_exit(): con_exits.append(obj) @@ -80,28 +117,6 @@ class EvenniaBasicObject(object): return retval - def a_get(self, pobject): - """ - Perform this action when someone uses the GET command on the object. - - values: - * pobject: (Object) The object requesting the action. - """ - # Un-comment the line below for an example - #print "SCRIPT TEST: %s got %s." % (pobject, self.source_obj) - pass - - def a_drop(self, pobject): - """ - Perform this action when someone uses the GET command on the object. - - values: - * pobject: (Object) The object requesting the action. - """ - # Un-comment the line below for an example - #print "SCRIPT TEST: %s dropped %s." % (pobject, self.source_obj) - pass - def default_lock(self, pobject): """ This method returns a True or False boolean value to determine whether diff --git a/src/script_parents/basicplayer.py b/src/script_parents/basicplayer.py index e0d459608c..2e866c258b 100644 --- a/src/script_parents/basicplayer.py +++ b/src/script_parents/basicplayer.py @@ -23,7 +23,7 @@ class EvenniaBasicPlayer(object): 'logged in', in a sense that they're not ready to send logged in commands or receive communication. """ - pobject = self.source_obj + pobject = self.scripted_obj # Load the player's channels from their JSON __CHANLIST attribute. comsys.load_object_channels(pobject) @@ -36,7 +36,7 @@ class EvenniaBasicPlayer(object): The user is now logged in. This is what happens right after the moment they are 'connected'. """ - pobject = self.source_obj + pobject = self.scripted_obj pobject.emit_to("You are now logged in as %s." % (pobject.name,)) pobject.get_location().emit_to_contents("%s has connected." % diff --git a/src/server.py b/src/server.py index 6dc4d37a1b..2c130aae0c 100755 --- a/src/server.py +++ b/src/server.py @@ -7,7 +7,7 @@ from django.db import connection from django.conf import settings from src.config.models import ConfigValue from src.session import SessionProtocol -from src import scheduler +from src import events from src import logger from src import session_mgr from src import alias_mgr @@ -50,7 +50,8 @@ class EvenniaService(service.Service): alias_mgr.load_cmd_aliases() print '-'*50 - scheduler.start_events() + # Fire up the event scheduler. + events.add_global_events() """ BEGIN SERVER STARTUP METHODS