Updated script docs

This commit is contained in:
Griatch 2015-03-09 17:22:47 +01:00
parent b4fb81f3b5
commit b998c34c2a

View file

@ -1,8 +1,8 @@
"""
This module contains the base DefaultScript class that all
scripts are inheriting from.
This module defines Scripts, out-of-character entities that can store
data both on themselves and on other objects while also having the
ability to run timers.
It also defines a few common scripts.
"""
from twisted.internet.defer import Deferred, maybeDeferred
@ -23,6 +23,7 @@ class ExtendedLoopingCall(LoopingCall):
"""
LoopingCall that can start at a delay different
than `self.interval`.
"""
start_delay = None
callcount = 0
@ -31,18 +32,32 @@ class ExtendedLoopingCall(LoopingCall):
"""
Start running function every interval seconds.
This overloads the LoopingCall default by offering
the start_delay keyword and ability to repeat.
This overloads the LoopingCall default by offering the
start_delay keyword and ability to repeat.
Args:
interval (int): Repeat interval in seconds.
now (bool, optional): Whether to start immediately or after
`start_delay` seconds.
start_delay (bool: The number of seconds before starting.
If None, wait interval seconds. Only valid if `now` is `False`.
It is used as a way to start with a variable start time
after a pause.
count_start (int): Number of repeats to start at. The count
goes up every time the system repeats. This is used to
implement something repeating `N` number of times etc.
Raises:
AssertError: if trying to start a task which is already running.
ValueError: If interval is set to an invalid value < 0.
Notes:
As opposed to Twisted's inbuilt count mechanism, this
system will count also if force_repeat() was called rather
than just the number of `interval` seconds since the start.
This allows us to force-step through a limited number of
steps if we want.
start_delay: The number of seconds before starting.
If None, wait interval seconds. Only
valid is now is False.
repeat_start: the task will track how many times it has run.
this will change where it starts counting from.
Note that as opposed to Twisted's inbuild
counter, this will count also if force_repeat()
was called (so it will not just count the number
of interval seconds since start).
"""
assert not self.running, ("Tried to start an already running "
"ExtendedLoopingCall.")
@ -72,21 +87,29 @@ class ExtendedLoopingCall(LoopingCall):
return d
def __call__(self):
"tick one step"
"""
Tick one step
"""
self.callcount += 1
super(ExtendedLoopingCall, self).__call__()
def _reschedule(self):
"""
Handle call rescheduling including
nulling `start_delay` and stopping if
number of repeats is reached.
Handle call rescheduling including nulling `start_delay` and
stopping if number of repeats is reached.
"""
self.start_delay = None
super(ExtendedLoopingCall, self)._reschedule()
def force_repeat(self):
"Force-fire the callback"
"""
Force-fire the callback
Raises:
AssertionError: When trying to force a task that is not
running.
"""
assert self.running, ("Tried to fire an ExtendedLoopingCall "
"that was not running.")
if self.call is not None:
@ -96,8 +119,13 @@ class ExtendedLoopingCall(LoopingCall):
def next_call_time(self):
"""
Return the time in seconds until the next call. This takes
`start_delay` into account.
Get the next call time.
Returns:
next (int): The time in seconds until the next call. This
takes `start_delay` into account. Returns `None` if
the task is not running.
"""
if self.running:
currentTime = self.clock.seconds()
@ -107,10 +135,10 @@ class ExtendedLoopingCall(LoopingCall):
#
# Base script, inherit from DefaultScript below instead.
#
class ScriptBase(ScriptDB):
class _ScriptBase(ScriptDB):
"""
Base class for scripts. Don't inherit from this, inherit
from the class `DefaultScript` below instead.
Base class for scripts. Don't inherit from this, inherit from the
class `DefaultScript` below instead.
"""
__metaclass__ = TypeclassBase
@ -118,8 +146,11 @@ class ScriptBase(ScriptDB):
def __eq__(self, other):
"""
This has to be located at this level, having it in the
parent doesn't work.
Compares two Scripts. Compares dbids.
Args:
other (Script): A script to compare with.
"""
try:
return other.dbid == self.dbid
@ -127,7 +158,10 @@ class ScriptBase(ScriptDB):
return False
def _start_task(self):
"start task runner"
"""
Start task runner.
"""
self.ndb._task = ExtendedLoopingCall(self._step_task)
@ -146,13 +180,19 @@ class ScriptBase(ScriptDB):
now=not self.db_start_delay)
def _stop_task(self):
"stop task runner"
"""
Stop task runner
"""
task = self.ndb._task
if task and task.running:
task.stop()
def _step_errback(self, e):
"callback for runner errors"
"""
Callback for runner errors
"""
cname = self.__class__.__name__
estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
{"key": self.key, "dbid": self.dbid, "cname": cname,
@ -164,7 +204,10 @@ class ScriptBase(ScriptDB):
logger.log_errmsg(estring)
def _step_callback(self):
"step task runner. No try..except needed due to defer wrap."
"""
Step task runner. No try..except needed due to defer wrap.
"""
if not self.is_valid():
self.stop()
@ -181,7 +224,9 @@ class ScriptBase(ScriptDB):
self.stop()
def _step_task(self):
"Step task. This groups error handling."
"""
Step task. This groups error handling.
"""
try:
return maybeDeferred(self._step_callback).addErrback(self._step_errback)
except Exception:
@ -191,11 +236,17 @@ class ScriptBase(ScriptDB):
def time_until_next_repeat(self):
"""
Returns the time in seconds until the script will be
run again. If this is not a stepping script, returns `None`.
This is not used in any way by the script's stepping
system; it's only here for the user to be able to
check in on their scripts and when they will next be run.
Get time until the script fires it `at_repeat` hook again.
Returns:
next (int): Time in seconds until the script runs again.
If not a timed script, return `None`.
Notes:
This hook is not used in any way by the script's stepping
system; it's only here for the user to be able to check in
on their scripts and when they will next be run.
"""
task = self.ndb._task
if task:
@ -206,21 +257,34 @@ class ScriptBase(ScriptDB):
return None
def remaining_repeats(self):
"Get the number of returning repeats. Returns `None` if unlimited repeats."
"""
Get the number of returning repeats for limited Scripts.
Returns:
remaining (int or `None`): The number of repeats
remaining until the Script stops. Returns `None`
if it has unlimited repeats.
"""
task = self.ndb._task
if task:
return max(0, self.db_repeats - task.callcount)
def start(self, force_restart=False):
"""
Called every time the script is started (for
persistent scripts, this is usually once every server start)
Called every time the script is started (for persistent
scripts, this is usually once every server start)
force_restart - if True, will always restart the script, regardless
of if it has started before.
Args:
force_restart (bool, optional): Normally an already
started script will not be started again. if
`force_restart=True`, the script will always restart
the script, regardless of if it has started before.
Returns:
result (int): 0 or 1 depending on if the script successfully
started or not. Used in counting.
returns 0 or 1 to indicated the script has been started or not.
Used in counting.
"""
if self.is_active and not force_restart:
@ -255,14 +319,18 @@ class ScriptBase(ScriptDB):
def stop(self, kill=False):
"""
Called to stop the script from running.
This also deletes the script.
Called to stop the script from running. This also deletes the
script.
Args:
kill (bool, optional): - Stop the script without
calling any relevant script hooks.
Returns:
result (int): 0 if the script failed to stop, 1 otherwise.
Used in counting.
kill - don't call finishing hooks.
"""
#print "stopping script %s" % self.key
#import pdb
#pdb.set_trace()
if not kill:
try:
self.at_stop()
@ -317,116 +385,13 @@ class ScriptBase(ScriptDB):
if task:
task.force_repeat()
# hooks
def at_script_creation(self):
"placeholder"
pass
def is_valid(self):
"placeholder"
pass
def at_start(self):
"placeholder."
pass
def at_stop(self):
"placeholder"
pass
def at_repeat(self):
"placeholder"
pass
def at_init(self):
"called when typeclass re-caches. Usually not used for scripts."
pass
#
# Base Script - inherit from this
#
class DefaultScript(ScriptBase):
class DefaultScript(_ScriptBase):
"""
This is the base TypeClass for all Scripts. Scripts describe
events, timers and states in game, they can have a time component
or describe a state that changes under certain conditions.
Script API:
* Available properties (only available on initiated Typeclass objects)
key (string) - name of object
name (string)- same as key
aliases (list of strings) - aliases to the object. Will be saved to
database as AliasDB entries but returned as strings.
dbref (int, read-only) - unique #id-number. Also "id" can be used.
date_created (string) - time stamp of object creation
permissions (list of strings) - list of permission strings
desc (string) - optional description of script, shown in listings
obj (Object) - optional object that this script is connected to
and acts on (set automatically
by `obj.scripts.add()`)
interval (int) - how often script should run, in seconds.
<=0 turns off ticker
start_delay (bool) - if the script should start repeating right
away or wait self.interval seconds
repeats (int) - how many times the script should repeat before
stopping. <=0 means infinite repeats
persistent (bool) - if script should survive a server shutdown or not
is_active (bool) - if script is currently running
* Handlers
locks - lock-handler: use locks.add() to add new lock strings
db - attribute-handler: store/retrieve database attributes on this
self.db.myattr=val, val=self.db.myattr
ndb - non-persistent attribute handler: same as db but does not
create a database entry when storing data
* Helper methods
start() - start script (this usually happens automatically at creation
and obj.script.add() etc)
stop() - stop script, and delete it
pause() - put the script on hold, until unpause() is called. If script
is persistent, the pause state will survive a shutdown.
unpause() - restart a previously paused script. The script will
continue as if it was never paused.
force_repeat() - force-step the script, regardless of how much remains
until next step. This counts like a normal firing in all ways.
time_until_next_repeat() - if a timed script (interval>0), returns
time until next tick
remaining_repeats() - number of repeats remaining, if limited
* Hook methods
at_script_creation() - called only once, when an object of this
class is first created.
is_valid() - is called to check if the script is valid to be running
at the current time. If is_valid() returns False, the
running script is stopped and removed from the game. You
can use this to check state changes (i.e. an script
tracking some combat stats at regular intervals is only
valid to run while there is actual combat going on).
at_start() - Called every time the script is started, which for
persistent scripts is at least once every server start.
Note that this is unaffected by self.delay_start, which
only delays the first call to at_repeat(). It will also
be called after a pause, to allow for setting up the script.
at_repeat() - Called every self.interval seconds. It will be called
immediately upon launch unless self.delay_start is True,
which will delay the first call of this method by
self.interval seconds. If self.interval<=0, this method
will never be called.
at_stop() - Called as the script object is stopped and is about to
be removed from the game, e.g. because is_valid()
returned False or self.stop() was called manually.
at_server_reload() - Called when server reloads. Can be used to save
temporary variables you want should survive a reload.
at_server_shutdown() - called at a full server shutdown.
"""
def at_first_save(self):
"""
@ -481,38 +446,39 @@ class DefaultScript(ScriptBase):
def is_valid(self):
"""
Is called to check if the script is valid to run at this time.
Should return a boolean. The method is assumed to collect all needed
information from its related self.obj.
Should return a boolean. The method is assumed to collect all
needed information from its related self.obj.
"""
return not self._is_deleted
def at_start(self):
"""
Called whenever the script is started, which for persistent
scripts is at least once every server start. It will also be called
when starting again after a pause (such as after a server reload)
scripts is at least once every server start. It will also be
called when starting again after a pause (such as after a
server reload)
"""
pass
def at_repeat(self):
"""
Called repeatedly if this Script is set to repeat
regularly.
Called repeatedly if this Script is set to repeat regularly.
"""
pass
def at_stop(self):
"""
Called whenever when it's time for this script to stop
(either because is_valid returned False or it runs out of iterations)
Called whenever when it's time for this script to stop (either
because is_valid returned False or it runs out of iterations)
"""
pass
def at_server_reload(self):
"""
This hook is called whenever the server is shutting down for
restart/reboot. If you want to, for example, save non-persistent
properties across a restart, this is the place to do it.
restart/reboot. If you want to, for example, save
non-persistent properties across a restart, this is the place
to do it.
"""
pass
@ -527,16 +493,24 @@ class DefaultScript(ScriptBase):
# Some useful default Script types used by Evennia.
class DoNothing(DefaultScript):
"An script that does nothing. Used as default fallback."
"""
A script that does nothing. Used as default fallback.
"""
def at_script_creation(self):
"Setup the script"
"""
Setup the script
"""
self.key = "sys_do_nothing"
self.desc = _("This is an empty placeholder script.")
self.desc = "This is an empty placeholder script."
class Store(DefaultScript):
"Simple storage script"
"""
Simple storage script
"""
def at_script_creation(self):
"Setup the script"
"""
Setup the script
"""
self.key = "sys_storage"
self.desc = _("This is a generic storage container.")
self.desc = "This is a generic storage container."