Added more stable events.

- added PIDs to all events, so they can be deleted safely.
- scheduler.del_event(pid) cleanly deletes events from the scheduler
- added @delevent for deleting events based on PID (@ps shows this now)
- Events has a self.repeat property allowing them to only be repeated a certain time (default is infinitely many times). After the set number of repeats, the event deletes itself from the scheduler.

Events are currently not persistently stored; this is left for future commits.
.
Griatch
This commit is contained in:
Griatch 2009-11-18 20:10:35 +00:00
parent 642932a403
commit 5e866c6b73
5 changed files with 148 additions and 26 deletions

View file

@ -775,3 +775,21 @@ def cmd_help(command):
source_object.emit_to(string) source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("help", cmd_help) GLOBAL_CMD_TABLE.add_command("help", cmd_help)
## def cmd_testevent(command):
## from src import events
## from src import scheduler
## source_object = command.source_object
## if not command.command_argument:
## #event = events.IntervalEvent()
## event = events.IntervalEvent()
## event.repeats = 3
## event.interval = 5
## pid = scheduler.add_event(event)
## source_object.emit_to("event with pid %s added." % pid)
## else:
## pid = command.command_argument
## scheduler.del_event(pid)
## source_object.emit_to("event with pid %s removed (if it existed)." % pid)
## GLOBAL_CMD_TABLE.add_command("testevent", cmd_testevent)

View file

@ -135,13 +135,18 @@ def cmd_ps(command):
""" """
source_object = command.source_object source_object = command.source_object
source_object.emit_to("-- Interval Events --") source_object.emit_to("Processes Scheduled:\n-- PID [time/interval] [repeats] description --")
for event in scheduler.schedule: for event in scheduler.SCHEDULE:
source_object.emit_to(" [%d/%d] %s" % ( repeats = "[inf] "
event.get_nextfire(), if event.repeats != None:
event.interval, repeats = "[%i] " % event.repeats
event.description)) source_object.emit_to(" %i [%d/%d] %s%s" % (
source_object.emit_to("Totals: %d interval events" % (len(scheduler.schedule),)) event.pid,
event.get_nextfire(),
event.interval,
repeats,
event.description))
source_object.emit_to("Totals: %d interval events" % (len(scheduler.SCHEDULE),))
GLOBAL_CMD_TABLE.add_command("@ps", cmd_ps, GLOBAL_CMD_TABLE.add_command("@ps", cmd_ps,
priv_tuple=("genperms.process_control",), help_category="Admin") priv_tuple=("genperms.process_control",), help_category="Admin")

View file

@ -15,6 +15,9 @@ from src.helpsys import helpsystem
from src.config.models import CommandAlias from src.config.models import CommandAlias
from src.config import edit_aliases from src.config import edit_aliases
from src import cache from src import cache
from src import scheduler
def cmd_reload(command): def cmd_reload(command):
""" """
@reload - reload game subsystems @reload - reload game subsystems
@ -761,3 +764,37 @@ def cmd_setcmdalias(command):
GLOBAL_CMD_TABLE.add_command("@setcmdalias", cmd_setcmdalias, GLOBAL_CMD_TABLE.add_command("@setcmdalias", cmd_setcmdalias,
priv_tuple=("genperms.process_control",), priv_tuple=("genperms.process_control",),
help_category="Admin") help_category="Admin")
def cmd_delevent(command):
"""
@delevent - remove events manually
Usage:
@delevent <pid>
Removes an event with the given pid (process ID) from the event scheduler.
To see all active events and their pids, use the @ps command.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Usage: @delevent <pid>")
return
try:
pid = int(command.command_argument)
except ValueError:
source_object.emit_to("You must supply a valid pid number.")
return
event = scheduler.get_event(pid)
if event:
desc = event.description
scheduler.del_event(pid)
source_object.emit_to("Event %i - '%s' removed." % (pid, desc))
else:
source_object.emit_to("No event found with a pid of %i. Use @ps to list process IDs." % pid)
GLOBAL_CMD_TABLE.add_command("@delevent", cmd_delevent,
priv_tuple=("genperms.process_control",),
help_category="Admin")

View file

@ -18,18 +18,28 @@ class IntervalEvent(object):
""" """
Represents an event that is triggered periodically. Sub-class this and Represents an event that is triggered periodically. Sub-class this and
fill in the stub function. fill in the stub function.
self.repeats decides if this event will fire indefinitely or only a
certain number of times.
""" """
def __init__(self): def __init__(self, description="IntervalEvent"):
""" """
Executed when the class is instantiated. Executed when the class is instantiated.
""" """
# This is a globally unique ID of this event. If None, a new one will
# be allocated when the event is added to the scheduler.
self.pid = None
# This is set to prevent a Nonetype exception on @ps before the # This is set to prevent a Nonetype exception on @ps before the
# event is fired for the first time. # event is fired for the first time.
self.time_last_executed = time.time() self.time_last_executed = time.time()
# This is what shows up on @ps in-game. # This used to describe the event in @ps listings.
self.name = None self.description = description
# An interval (in seconds) for execution. # An interval (in seconds) for execution.
self.interval = None self.interval = None
# How many times to repeat this event.
# None : indefinitely,
# positive integer : number of times
self.repeats = None
# A reference to the task.LoopingCall object. # A reference to the task.LoopingCall object.
self.looped_task = None self.looped_task = None
@ -39,6 +49,18 @@ class IntervalEvent(object):
""" """
return self.name return self.name
def __eq__(self, event2):
"""
Handles comparison operations.
"""
return self.pid == event2.pid
def __hash__(self):
"""
Used for dictionary key comparisons.
"""
return self.pid
def start_event_loop(self): def start_event_loop(self):
""" """
Called to start up the event loop when the event is added to the Called to start up the event loop when the event is added to the
@ -78,9 +100,16 @@ class IntervalEvent(object):
def fire_event(self): def fire_event(self):
""" """
Set the last ran stamp and fire off the event. Set the last ran stamp and fire off the event.
Stop repeating if number of repeats have been achieved.
""" """
self.set_lastfired() self.set_lastfired()
self.event_function() self.event_function()
if self.repeats != None:
self.repeats -= 1
if self.repeats <= 0 and self.pid != None:
scheduler.del_event(self.pid)
class IEvt_Check_Sessions(IntervalEvent): class IEvt_Check_Sessions(IntervalEvent):
""" """

View file

@ -9,9 +9,23 @@ ADDING AN EVENT:
* Profit. * Profit.
""" """
# List of IntervalEvent sub-classed objects. # dict of IntervalEvent sub-classed objects, keyed by their
schedule = [] # process id:s.
SCHEDULE = []
def next_free_pid():
"""
Find the next free pid
"""
pids = [event.pid for event in SCHEDULE]
if not pids:
return 0
maxpid = max(pids)
freepids = [pid for pid in xrange(maxpid+1) if pid not in pids]
if freepids:
return min(freepids)
return maxpid + 1
def add_event(event): def add_event(event):
""" """
Adds an event instance to the scheduled event list. Call this any time you Adds an event instance to the scheduled event list. Call this any time you
@ -19,21 +33,40 @@ def add_event(event):
Args: Args:
* event: (IntervalEvent) The event to add to the scheduler. * event: (IntervalEvent) The event to add to the scheduler.
""" Returns:
* pid : (int) The process ID assigned to this event, for future reference.
#don't add multiple instances of the same event, instead replace """
if event in schedule: # Make sure not to add multiple instances of the same event.
schedule[schedule.index(event)] = event matches = [i for i, stored_event in enumerate(SCHEDULE) if event == stored_event]
return if matches:
# Before replacing an event, stop its old incarnation.
del_event(matches[0])
SCHEDULE[matches[0]] = event
else: else:
schedule.append(event) # Add a new event with a fresh pid.
event.pid = next_free_pid()
SCHEDULE.append(event)
event.start_event_loop() event.start_event_loop()
return event.pid
def del_event(event): def get_event(pid):
""" """
Remove an event from scheduler. Return an event with the given pid, if it exists,
otherwise return None.
""" """
if event in schedule: pid = int(pid)
i = schedule.index(event) imatches = [i for i, stored_event in enumerate(SCHEDULE) if stored_event.pid == pid]
schedule[i].stop_event_loop() if imatches:
del schedule[i] return SCHEDULE[imatches[0]]
def del_event(pid):
"""
Remove an event from scheduler. There should never be more than one
event with a certain pid, this cleans up in case there are any multiples.
"""
pid = int(pid)
imatches = [i for i, stored_event in enumerate(SCHEDULE) if stored_event.pid == pid]
for imatch in imatches:
SCHEDULE[imatch].stop_event_loop()
del SCHEDULE[imatch]