Updated script docs
This commit is contained in:
parent
b4fb81f3b5
commit
b998c34c2a
1 changed files with 143 additions and 169 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
"""
|
"""
|
||||||
This module contains the base DefaultScript class that all
|
This module defines Scripts, out-of-character entities that can store
|
||||||
scripts are inheriting from.
|
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
|
from twisted.internet.defer import Deferred, maybeDeferred
|
||||||
|
|
@ -23,6 +23,7 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
"""
|
"""
|
||||||
LoopingCall that can start at a delay different
|
LoopingCall that can start at a delay different
|
||||||
than `self.interval`.
|
than `self.interval`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
start_delay = None
|
start_delay = None
|
||||||
callcount = 0
|
callcount = 0
|
||||||
|
|
@ -31,18 +32,32 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
"""
|
"""
|
||||||
Start running function every interval seconds.
|
Start running function every interval seconds.
|
||||||
|
|
||||||
This overloads the LoopingCall default by offering
|
This overloads the LoopingCall default by offering the
|
||||||
the start_delay keyword and ability to repeat.
|
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 "
|
assert not self.running, ("Tried to start an already running "
|
||||||
"ExtendedLoopingCall.")
|
"ExtendedLoopingCall.")
|
||||||
|
|
@ -72,21 +87,29 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
"tick one step"
|
"""
|
||||||
|
Tick one step
|
||||||
|
"""
|
||||||
self.callcount += 1
|
self.callcount += 1
|
||||||
super(ExtendedLoopingCall, self).__call__()
|
super(ExtendedLoopingCall, self).__call__()
|
||||||
|
|
||||||
def _reschedule(self):
|
def _reschedule(self):
|
||||||
"""
|
"""
|
||||||
Handle call rescheduling including
|
Handle call rescheduling including nulling `start_delay` and
|
||||||
nulling `start_delay` and stopping if
|
stopping if number of repeats is reached.
|
||||||
number of repeats is reached.
|
|
||||||
"""
|
"""
|
||||||
self.start_delay = None
|
self.start_delay = None
|
||||||
super(ExtendedLoopingCall, self)._reschedule()
|
super(ExtendedLoopingCall, self)._reschedule()
|
||||||
|
|
||||||
def force_repeat(self):
|
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 "
|
assert self.running, ("Tried to fire an ExtendedLoopingCall "
|
||||||
"that was not running.")
|
"that was not running.")
|
||||||
if self.call is not None:
|
if self.call is not None:
|
||||||
|
|
@ -96,8 +119,13 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
|
|
||||||
def next_call_time(self):
|
def next_call_time(self):
|
||||||
"""
|
"""
|
||||||
Return the time in seconds until the next call. This takes
|
Get the next call time.
|
||||||
`start_delay` into account.
|
|
||||||
|
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:
|
if self.running:
|
||||||
currentTime = self.clock.seconds()
|
currentTime = self.clock.seconds()
|
||||||
|
|
@ -107,10 +135,10 @@ class ExtendedLoopingCall(LoopingCall):
|
||||||
#
|
#
|
||||||
# Base script, inherit from DefaultScript below instead.
|
# Base script, inherit from DefaultScript below instead.
|
||||||
#
|
#
|
||||||
class ScriptBase(ScriptDB):
|
class _ScriptBase(ScriptDB):
|
||||||
"""
|
"""
|
||||||
Base class for scripts. Don't inherit from this, inherit
|
Base class for scripts. Don't inherit from this, inherit from the
|
||||||
from the class `DefaultScript` below instead.
|
class `DefaultScript` below instead.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__metaclass__ = TypeclassBase
|
__metaclass__ = TypeclassBase
|
||||||
|
|
@ -118,8 +146,11 @@ class ScriptBase(ScriptDB):
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
This has to be located at this level, having it in the
|
Compares two Scripts. Compares dbids.
|
||||||
parent doesn't work.
|
|
||||||
|
Args:
|
||||||
|
other (Script): A script to compare with.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return other.dbid == self.dbid
|
return other.dbid == self.dbid
|
||||||
|
|
@ -127,7 +158,10 @@ class ScriptBase(ScriptDB):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _start_task(self):
|
def _start_task(self):
|
||||||
"start task runner"
|
"""
|
||||||
|
Start task runner.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
self.ndb._task = ExtendedLoopingCall(self._step_task)
|
self.ndb._task = ExtendedLoopingCall(self._step_task)
|
||||||
|
|
||||||
|
|
@ -146,13 +180,19 @@ class ScriptBase(ScriptDB):
|
||||||
now=not self.db_start_delay)
|
now=not self.db_start_delay)
|
||||||
|
|
||||||
def _stop_task(self):
|
def _stop_task(self):
|
||||||
"stop task runner"
|
"""
|
||||||
|
Stop task runner
|
||||||
|
|
||||||
|
"""
|
||||||
task = self.ndb._task
|
task = self.ndb._task
|
||||||
if task and task.running:
|
if task and task.running:
|
||||||
task.stop()
|
task.stop()
|
||||||
|
|
||||||
def _step_errback(self, e):
|
def _step_errback(self, e):
|
||||||
"callback for runner errors"
|
"""
|
||||||
|
Callback for runner errors
|
||||||
|
|
||||||
|
"""
|
||||||
cname = self.__class__.__name__
|
cname = self.__class__.__name__
|
||||||
estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
|
estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \
|
||||||
{"key": self.key, "dbid": self.dbid, "cname": cname,
|
{"key": self.key, "dbid": self.dbid, "cname": cname,
|
||||||
|
|
@ -164,7 +204,10 @@ class ScriptBase(ScriptDB):
|
||||||
logger.log_errmsg(estring)
|
logger.log_errmsg(estring)
|
||||||
|
|
||||||
def _step_callback(self):
|
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():
|
if not self.is_valid():
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
@ -181,7 +224,9 @@ class ScriptBase(ScriptDB):
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def _step_task(self):
|
def _step_task(self):
|
||||||
"Step task. This groups error handling."
|
"""
|
||||||
|
Step task. This groups error handling.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return maybeDeferred(self._step_callback).addErrback(self._step_errback)
|
return maybeDeferred(self._step_callback).addErrback(self._step_errback)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -191,11 +236,17 @@ class ScriptBase(ScriptDB):
|
||||||
|
|
||||||
def time_until_next_repeat(self):
|
def time_until_next_repeat(self):
|
||||||
"""
|
"""
|
||||||
Returns the time in seconds until the script will be
|
Get time until the script fires it `at_repeat` hook again.
|
||||||
run again. If this is not a stepping script, returns `None`.
|
|
||||||
This is not used in any way by the script's stepping
|
Returns:
|
||||||
system; it's only here for the user to be able to
|
next (int): Time in seconds until the script runs again.
|
||||||
check in on their scripts and when they will next be run.
|
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
|
task = self.ndb._task
|
||||||
if task:
|
if task:
|
||||||
|
|
@ -206,21 +257,34 @@ class ScriptBase(ScriptDB):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def remaining_repeats(self):
|
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
|
task = self.ndb._task
|
||||||
if task:
|
if task:
|
||||||
return max(0, self.db_repeats - task.callcount)
|
return max(0, self.db_repeats - task.callcount)
|
||||||
|
|
||||||
def start(self, force_restart=False):
|
def start(self, force_restart=False):
|
||||||
"""
|
"""
|
||||||
Called every time the script is started (for
|
Called every time the script is started (for persistent
|
||||||
persistent scripts, this is usually once every server start)
|
scripts, this is usually once every server start)
|
||||||
|
|
||||||
force_restart - if True, will always restart the script, regardless
|
Args:
|
||||||
of if it has started before.
|
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:
|
if self.is_active and not force_restart:
|
||||||
|
|
@ -255,14 +319,18 @@ class ScriptBase(ScriptDB):
|
||||||
|
|
||||||
def stop(self, kill=False):
|
def stop(self, kill=False):
|
||||||
"""
|
"""
|
||||||
Called to stop the script from running.
|
Called to stop the script from running. This also deletes the
|
||||||
This also deletes the script.
|
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:
|
if not kill:
|
||||||
try:
|
try:
|
||||||
self.at_stop()
|
self.at_stop()
|
||||||
|
|
@ -317,116 +385,13 @@ class ScriptBase(ScriptDB):
|
||||||
if task:
|
if task:
|
||||||
task.force_repeat()
|
task.force_repeat()
|
||||||
|
|
||||||
# hooks
|
|
||||||
def at_script_creation(self):
|
|
||||||
"placeholder"
|
|
||||||
pass
|
|
||||||
|
|
||||||
def is_valid(self):
|
class DefaultScript(_ScriptBase):
|
||||||
"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):
|
|
||||||
"""
|
"""
|
||||||
This is the base TypeClass for all Scripts. Scripts describe
|
This is the base TypeClass for all Scripts. Scripts describe
|
||||||
events, timers and states in game, they can have a time component
|
events, timers and states in game, they can have a time component
|
||||||
or describe a state that changes under certain conditions.
|
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):
|
def at_first_save(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -481,38 +446,39 @@ class DefaultScript(ScriptBase):
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""
|
"""
|
||||||
Is called to check if the script is valid to run at this time.
|
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
|
Should return a boolean. The method is assumed to collect all
|
||||||
information from its related self.obj.
|
needed information from its related self.obj.
|
||||||
"""
|
"""
|
||||||
return not self._is_deleted
|
return not self._is_deleted
|
||||||
|
|
||||||
def at_start(self):
|
def at_start(self):
|
||||||
"""
|
"""
|
||||||
Called whenever the script is started, which for persistent
|
Called whenever the script is started, which for persistent
|
||||||
scripts is at least once every server start. It will also be called
|
scripts is at least once every server start. It will also be
|
||||||
when starting again after a pause (such as after a server reload)
|
called when starting again after a pause (such as after a
|
||||||
|
server reload)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
"""
|
"""
|
||||||
Called repeatedly if this Script is set to repeat
|
Called repeatedly if this Script is set to repeat regularly.
|
||||||
regularly.
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_stop(self):
|
def at_stop(self):
|
||||||
"""
|
"""
|
||||||
Called whenever when it's time for this script to stop
|
Called whenever when it's time for this script to stop (either
|
||||||
(either because is_valid returned False or it runs out of iterations)
|
because is_valid returned False or it runs out of iterations)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def at_server_reload(self):
|
def at_server_reload(self):
|
||||||
"""
|
"""
|
||||||
This hook is called whenever the server is shutting down for
|
This hook is called whenever the server is shutting down for
|
||||||
restart/reboot. If you want to, for example, save non-persistent
|
restart/reboot. If you want to, for example, save
|
||||||
properties across a restart, this is the place to do it.
|
non-persistent properties across a restart, this is the place
|
||||||
|
to do it.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -527,16 +493,24 @@ class DefaultScript(ScriptBase):
|
||||||
# Some useful default Script types used by Evennia.
|
# Some useful default Script types used by Evennia.
|
||||||
|
|
||||||
class DoNothing(DefaultScript):
|
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):
|
def at_script_creation(self):
|
||||||
"Setup the script"
|
"""
|
||||||
|
Setup the script
|
||||||
|
"""
|
||||||
self.key = "sys_do_nothing"
|
self.key = "sys_do_nothing"
|
||||||
self.desc = _("This is an empty placeholder script.")
|
self.desc = "This is an empty placeholder script."
|
||||||
|
|
||||||
|
|
||||||
class Store(DefaultScript):
|
class Store(DefaultScript):
|
||||||
"Simple storage script"
|
"""
|
||||||
|
Simple storage script
|
||||||
|
"""
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
"Setup the script"
|
"""
|
||||||
|
Setup the script
|
||||||
|
"""
|
||||||
self.key = "sys_storage"
|
self.key = "sys_storage"
|
||||||
self.desc = _("This is a generic storage container.")
|
self.desc = "This is a generic storage container."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue