OBS: You need to resync your database! Moved cmdsets into the database rather than being dependent on scripts. Moved the creation of the cmdset- and cmdset-handlers into ObjectDB.__init__ rather than bootstrapping it from the typeclass. Added some more script functionality for testing, includong the @script command for assigning a script to an object.

This commit is contained in:
Griatch 2011-03-20 19:45:56 +00:00
parent e965830735
commit 126e2ea61f
17 changed files with 370 additions and 216 deletions

View file

@ -0,0 +1,62 @@
"""
Example script for testing. This adds a simple timer that
has your character make observations and noices at irregular
intervals.
To test, use
@script me = examples.bodyfunctions.BodyFunctions
The script will only send messages to the object it
is stored on, so make sure to put it on yourself
or you won't see any messages!
"""
import random
from game.gamesrc.scripts.basescript import Script
class BodyFunctions(Script):
"""
This class defines the script itself
"""
def at_script_creation(self):
self.key = "bodyfunction"
self.desc = "Adds various timed events to a character."
self.interval = 20 # seconds
self.start_delay # wait self.interval until first call
self.persistent = False
def at_repeat(self):
"""
This gets called every self.interval seconds. We make
a random check here so as to only return 30% of the time.
"""
if random.random() > 0.66:
# no message this time
return
rand = random.random()
# return a random message
if rand < 0.1:
string = "You tap your foot, looking around."
elif rand < 0.2:
string = "You have an itch. Hard to reach too."
elif rand < 0.3:
string = "You think you hear someone behind you. ... but when you look there's noone there."
elif rand < 0.4:
string = "You inspect your fingernails. Nothing to report."
elif rand < 0.5:
string = "You cough discreetly into your hand."
elif rand < 0.6:
string = "You scratch your head, looking around."
elif rand < 0.7:
string = "You blink, forgetting what it was you were going to do."
elif rand < 0.8:
string = "You feel lonely all of a sudden."
elif rand < 0.9:
string = "You get a great idea. Of course you won't tell anyone."
else:
string = "You suddenly realize how much you love Evennia!"
# echo the message to the object
self.obj.msg(string)

View file

@ -114,10 +114,7 @@ def get_and_merge_cmdsets(caller):
local_objlist = location.contents + caller.contents
local_objects_cmdsets = [obj.cmdset.current
for obj in local_objlist
if obj.cmdset.outside_access]
# print "used objs: %s" % ([obj.name
# for obj in local_objlist
# if obj.cmdset.outside_access])
if obj.locks.check(caller, 'call', no_superuser_bypass=True)]
# Merge all command sets into one
# (the order matters, the higher-prio cmdsets are merged last)

View file

@ -65,9 +65,7 @@ the 'Fishing' set. Fishing from a boat? No problem!
"""
import traceback
from src.utils import logger
from src.utils import create
from src.commands.cmdset import CmdSet
from src.scripts.scripts import AddCmdSet
CACHED_CMDSETS = {}
@ -128,7 +126,7 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
class CmdSetHandler(object):
"""
The CmdSetHandler is always stored on an object, supplied as the argument.
The CmdSetHandler is always stored on an object, this object is supplied as an argument.
The 'current' cmdset is the merged set currently active for this object.
This is the set the game engine will retrieve when determining which
@ -137,35 +135,29 @@ class CmdSetHandler(object):
the 'current' cmdset.
"""
def __init__(self, obj, outside_access=True):
def __init__(self, obj):
"""
This method is called whenever an object is recreated.
obj - this is a reference to the game object this handler
belongs to.
outside_access - if false, the cmdparser will only retrieve
this cmdset when it is its obj itself that is calling for it.
(this is is good to use for player objects, since they
should not have access to the cmdsets of other player
objects).
"""
self.obj = obj
# if false, only the object itself may use this handler
# (this should be set especially by character objects)
self.outside_access = outside_access
# the id of the "merged" current cmdset for easy access.
self.key = None
# this holds the "merged" current command set
self.current = None
# this holds a history of CommandSets
# this holds a history of CommandSets
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
# this tracks which mergetypes are actually in play in the stack
self.mergetype_stack = ["Union"]
self.update()
#print "cmdsethandler init. id:%s, obj:%s, cmdsetstack:%s " % (id(self), self.obj.key, [cmdset.key for cmdset in self.cmdset_stack])
# the subset of the cmdset_paths that are to be stored in the database
self.permanent_paths = [""]
# self.update(init_mode=True) is then called from the object __init__.
def __str__(self):
"Display current commands"
@ -180,8 +172,8 @@ class CmdSetHandler(object):
if mergetype != cmdset.mergetype:
mergetype = "%s^" % (mergetype)
string += "\n %i: <%s (%s, prio %i)>: %s" % \
(snum, cmdset.key, mergetype,
cmdset.priority, cmdset)
(snum, cmdset.key, mergetype,
cmdset.priority, cmdset)
string += "\n (combining %i cmdsets):" % (num+1)
else:
string += "\n "
@ -195,22 +187,40 @@ class CmdSetHandler(object):
mergetype, self.current)
return string.strip()
def update(self):
def update(self, init_mode=False):
"""
Re-adds all sets in the handler to have an updated
current set.
init_mode is used right after this handler was
created; it imports all permanent cmdsets from db.
"""
updated = None
if init_mode:
self.cmdset_stack = []
# reimport all permanent cmdsets
self.permanent_paths = self.obj.cmdset_storage
new_permanent_paths = []
for pos, path in enumerate(self.permanent_paths):
if pos == 0 and not path:
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
else:
cmdset = self.import_cmdset(path)
if cmdset:
self.cmdset_stack.append(cmdset)
# merge the stack into a new merged cmdset
new_current = None
self.mergetype_stack = []
for cmdset in self.cmdset_stack:
try:
# for cmdset's '+' operator, order matters.
updated = cmdset + updated
new_current = cmdset + new_current
except TypeError:
continue
self.mergetype_stack.append(updated.actual_mergetype)
self.current = updated
self.mergetype_stack.append(new_current.actual_mergetype)
self.current = new_current
def import_cmdset(self, cmdset_path, emit_to_obj=None):
"""
load a cmdset from a module.
@ -247,23 +257,17 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset, emit_to_obj)
cmdset = self.import_cmdset(cmdset)#, emit_to_obj)
if cmdset:
self.cmdset_stack.append(cmdset)
if permanent:
# store the path permanently
self.permanent_paths.append(cmdset.path)
self.obj.cmdset_storage = self.permanent_paths
else:
# store an empty entry and don't save (this makes it easy to delete).
self.permanent_paths.append("")
self.update()
if permanent:
# create a script to automatically add this cmdset at
# startup. We don't start it here since the cmdset was
# already added above.
try:
cmdset = "%s.%s" % (cmdset.__module__, cmdset.__name__)
except Exception:
logger.log_trace()
return
script = create.create_script(AddCmdSet)
script.db.cmdset = cmdset
script.db.add_default = False
self.obj.scripts.add(script, autostart=False)
def add_default(self, cmdset, emit_to_obj=None, permanent=False):
"""
@ -281,70 +285,74 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset, emit_to_obj)
cmdset = self.import_cmdset(cmdset)
if cmdset:
self.cmdset_stack[0] = cmdset
self.mergetype_stack[0] = cmdset.mergetype
self.update()
#print "add_default:", permanent
if permanent:
# create a script to automatically add this cmdset at
# startup. We don't start it here since the cmdset was
# already added above.
try:
cmdset = "%s.%s" % (cmdset.__module__, cmdset.__class__.__name__)
except Exception:
#print traceback.format_exc()
logger.log_trace()
return
#print "cmdset to add:", cmdset
script = create.create_script(AddCmdSet)
script.db.cmdset = cmdset
script.db.add_default = True
self.obj.scripts.add(script, key="add_default_cmdset", autostart=False)
if self.cmdset_stack:
self.cmdset_stack[0] = cmdset
self.mergetype_stack.insert[0] = cmdset.mergetype
else:
self.cmdset_stack = [cmdset]
self.mergetype_stack = cmdset.mergetype
if permanent:
if self.permanent_paths:
self.permanent_paths[0] = cmdset.path
else:
self.permanent_paths = [cmdset.path]
self.obj.cmdset_storage = self.permanent_paths
else:
if self.permanent_paths:
self.permanent_paths[0] = ""
else:
self.permanent_paths = [""]
self.update()
def delete(self, key_or_class=None):
def delete(self, cmdset=None):
"""
Remove a cmdset from the handler. If a key is supplied,
it attempts to remove this. If no key is given,
Remove a cmdset from the handler.
cmdset can be supplied either as a cmdset-key,
an instance of the CmdSet or a python path
to the cmdset. If no key is given,
the last cmdset in the stack is removed. Whenever
the cmdset_stack changes, the cmdset is updated.
The default cmdset (first entry in stack) is never
removed - remove it explicitly with delete_default.
key_or_class - a specific cmdset key or a cmdset class (in
the latter case, *all* cmdsets of this class
will be removed from handler!)
"""
if len(self.cmdset_stack) < 2:
# don't allow deleting default cmdsets here.
return
if not key_or_class:
if not cmdset:
# remove the last one in the stack (except the default position)
self.cmdset_stack.pop()
else:
# argument key is given, is it a key or a class?
default_cmdset = self.cmdset_stack[0]
if callable(key_or_class) and hasattr(key_or_class, '__name__'):
# this is a callable with __name__ - we assume it's a class
self.cmdset_stack = [cmdset for cmdset in self.cmdset_stack[1:]
if cmdset.__class__.__name__ != key_or_class.__name__]
self.permanent_paths.pop()
else:
# try it as a callable
if callable(cmdset) and hasattr(cmdset, 'path'):
indices = [i+1 for i, cset in enumerate(self.cmdset_stack[1:]) if cset.path == cmdset.path]
else:
# try it as a string
self.cmdset_stack = [cmdset for cmdset in self.cmdset_stack[1:]
if cmdset.key != key_or_class]
self.cmdset_stack.insert(0, default_cmdset)
# try it as a path or key
indices = [i+1 for i, cset in enumerate(self.cmdset_stack[1:]) if cset.path == cmdset or cset.key == cmdset]
for i in indices:
del self.cmdset_stack[i]
del self.permanent_paths[i]
self.obj.cmdset_storage = self.permanent_paths
# re-sync the cmdsethandler.
self.update()
def delete_default(self):
"This explicitly deletes the default cmdset. It's the only command that can."
self.cmdset_stack[0] = CmdSet(cmdsetobj=self.obj, key="Empty")
if self.cmdset_stack:
self.cmdset_stack[0] = CmdSet(cmdsetobj=self.obj, key="Empty")
self.permanent_paths[0] = ""
else:
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
self.permanent_paths = [""]
self.obj.cmdset_storage = self.permanent_paths
self.update()
def all(self):
@ -360,6 +368,8 @@ class CmdSetHandler(object):
"""
self.cmdset_stack = [self.cmdset_stack[0]]
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
self.permanent_paths[0] = [self.permanent_paths[0]]
self.obj.cmdset_storage = self.permanent_paths
self.update()
def reset(self):

View file

@ -750,6 +750,40 @@ class CmdLink(MuxCommand):
# give feedback
caller.msg(string)
class CmdUnLink(CmdLink):
"""
@unlink - unconnect objects
Usage:
@unlink <Object>
Unlinks an object, for example an exit, disconnecting
it from whatever it was connected to.
"""
# this is just a child of CmdLink
key = "@unlink"
locks = "cmd:perm(unlink) or perm(Builders)"
help_key = "Building"
def func(self):
"""
All we need to do here is to set the right command
and call func in CmdLink
"""
caller = self.caller
if not self.args:
caller.msg("Usage: @unlink <object>")
return
# This mimics '@link <obj> = ' which is the same as @unlink
self.rhs = ""
# call the @link functionality
super(CmdUnLink, self).func()
class CmdListCmdSets(MuxCommand):
"""
@ -1585,36 +1619,67 @@ class CmdTeleport(MuxCommand):
caller.msg("Teleported.")
class CmdUnLink(CmdLink):
class CmdScript(MuxCommand):
"""
@unlink - unconnect objects
attach scripts
Usage:
@unlink <Object>
@script[/switch] <obj> = <script.path or scriptkey>
Switches:
start - start a previously added script
stop - stop a previously added script
Unlinks an object, for example an exit, disconnecting
it from whatever it was connected to.
Attaches the given script to the object and starts it. Script path can be given
from the base location for scripts as given in settings.
If stopping/starting an already existing script, the script's key
can be given instead (if giving a path, *all* scripts with this path
on <obj> will be affected).
"""
# this is just a child of CmdLink
key = "@script"
aliases = "@addscript"
key = "@unlink"
locks = "cmd:perm(unlink) or perm(Builders)"
help_key = "Building"
locks = "cmd:perm(script) or perm(Wizards)"
def func(self):
"""
All we need to do here is to set the right command
and call func in CmdLink
"""
caller = self.caller
if not self.args:
caller.msg("Usage: @unlink <object>")
return
"Do stuff"
# This mimics '@link <obj> = ' which is the same as @unlink
self.rhs = ""
caller = self.caller
if not self.rhs:
string = "Usage: @script[/switch] <obj> = <script.path or script key>"
caller.msg(string)
return
# call the @link functionality
super(CmdUnLink, self).func()
inp = self.rhs
if not inp.startswith('src.') and not inp.startswith('game.'):
# append the default path.
inp = "%s.%s" % (settings.BASE_SCRIPT_PATH, inp)
obj = caller.search(self.lhs)
if not obj:
return
string = ""
if "stop" in self.switches:
# we are stopping an already existing script
ok = obj.scripts.stop(inp)
if not ok:
string = "Script %s could not be stopped. Does it exist?" % inp
else:
string = "Script stopped and removed from object."
if "start" in self.switches:
# we are starting an already existing script
ok = obj.scripts.start(inp)
if not ok:
string = "Script %s could not be (re)started." % inp
else:
string = "Script started successfully."
if not self.switches:
# adding a new script, and starting it
ok = obj.scripts.add(inp, autostart=True)
if not ok:
string = "Script %s could not be added." % inp
else:
string = "Script successfully added and started."
caller.msg(string)

View file

@ -77,6 +77,7 @@ class DefaultCmdSet(CmdSet):
self.add(building.CmdExamine())
self.add(building.CmdTypeclass())
self.add(building.CmdLock())
self.add(building.CmdScript())
# Comm commands
self.add(comms.CmdAddCom())

View file

@ -70,7 +70,9 @@ class CmdPy(MuxCommand):
Usage:
@py <cmd>
In this limited python environment.
In this limited python environment, there are a
few variables made available to give access to
the system.
available_vars: 'self','me' : caller
'here' : caller.location
@ -82,7 +84,7 @@ class CmdPy(MuxCommand):
'ConfigValue' ConfigValue class
only two
variables are defined: 'self'/'me' which refers to one's
own object, and 'here' which refers to the current
own object, and 'here' which refers to self's current
location.
"""
key = "@py"
@ -102,10 +104,11 @@ class CmdPy(MuxCommand):
return
# create temporary test objects for playing with
script = create.create_script("src.scripts.scripts.DoNothing",
'testscript')
key = 'testscript')
obj = create.create_object("src.objects.objects.Object",
'testobject')
key='testobject')
conf = ConfigValue() # used to access conf values
available_vars = {'self':caller,
'me':caller,
'here':caller.location,
@ -131,7 +134,10 @@ class CmdPy(MuxCommand):
ret = "\n".join("<<< %s" % line for line in errlist if line)
caller.msg(ret)
obj.delete()
script.delete()
try:
script.delete()
except AssertionError: # this is a strange thing; the script looses its id somehow..?
pass
class CmdScripts(MuxCommand):
"""

View file

@ -287,7 +287,7 @@ class LockHandler(object):
"""
self.reset_flag = True
def check(self, accessing_obj, access_type, default=False):
def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False):
"""
Checks a lock of the correct type by passing execution
off to the lock function(s).
@ -295,14 +295,17 @@ class LockHandler(object):
accessing_obj - the object seeking access
access_type - the type of access wanted
default - if no suitable lock type is found, use this
no_superuser_bypass - don't use this unless you really, really need to
"""
if self.reset_flag:
# rebuild cache
self._cache_locks(self.obj.lock_storage)
self.reset_flag = False
if (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser) \
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)):
if (not no_superuser_bypass and (hasattr(accessing_obj, 'player')
and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser))):
# we grant access to superusers and also to protocol instances that not yet has any player assigned to them (the
# latter is a safety feature since superuser cannot be authenticated at some point during the connection).
return True

View file

@ -22,7 +22,8 @@ from src.typeclasses.models import Attribute, TypedObject
from src.typeclasses.typeclass import TypeClass
from src.objects.manager import ObjectManager
from src.config.models import ConfigValue
from src.commands.cmdsethandler import CmdSetHandler
from src.scripts.scripthandler import ScriptHandler
from src.utils import logger
from src.utils.utils import is_iter
@ -216,18 +217,24 @@ class ObjectDB(TypedObject):
# a safety location, this usually don't change much.
db_home = models.ForeignKey('self', related_name="homes_set",
blank=True, null=True)
# database storage of persistant cmdsets.
db_cmdset_storage = models.TextField(null=True)
# Database manager
objects = ObjectManager()
# Add the object-specific handlers
# (scripts and cmdset must be added from
# typeclass, so not added here)
def __init__(self, *args, **kwargs):
"Parent must be initialized first."
TypedObject.__init__(self, *args, **kwargs)
# handlers
self.cmdset = CmdSetHandler(self)
self.cmdset.update(init_mode=True)
self.scripts = ScriptHandler(self)
self.scripts.validate(init_mode=True)
self.nicks = NickHandler(self)
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
# normal python operations (without having to remember to save()
@ -381,6 +388,28 @@ class ObjectDB(TypedObject):
query.delete()
aliases = property(aliases_get, aliases_set, aliases_del)
# cmdset_storage property
#@property
def cmdset_storage_get(self):
"Getter. Allows for value = self.name. Returns a list of cmdset_storage."
if self.db_cmdset_storage:
return [path.strip() for path in self.db_cmdset_storage.split(',')]
return []
#@cmdset_storage.setter
def cmdset_storage_set(self, value):
"Setter. Allows for self.name = value. Stores as a comma-separated string."
if is_iter(value):
value = ",".join([str(val).strip() for val in value])
self.db_cmdset_storage = value
self.save()
#@cmdset_storage.deleter
def cmdset_storage_del(self):
"Deleter. Allows for del self.name"
self.db_cmdset_storage = ""
self.save()
cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del)
class Meta:
"Define Django meta options"
verbose_name = "Object"

View file

@ -15,12 +15,9 @@ That an object is controlled by a player/user is just defined by its
they control by simply linking to a new object's user property.
"""
from django.conf import settings
from src.typeclasses.typeclass import TypeClass
from src.commands.cmdsethandler import CmdSetHandler
from src.scripts.scripthandler import ScriptHandler
from src.objects.exithandler import EXITHANDLER
from src.utils import utils
#
# Base class to inherit from.
@ -33,33 +30,6 @@ class Object(TypeClass):
objects in the game.
"""
def __init__(self, dbobj):
"""
Set up the Object-specific handlers. Note that we must
be careful to run the parent's init function too
or typeclasses won't work!
"""
# initialize typeclass system. This sets up self.dbobj.
super(Object, self).__init__(dbobj)
# create the command- and scripthandlers as needed
try:
dummy = object.__getattribute__(dbobj, 'cmdset')
create_cmdset = type(dbobj.cmdset) != CmdSetHandler
except AttributeError:
create_cmdset = True
try:
dummy = object.__getattribute__(dbobj, 'scripts')
create_scripts = type(dbobj.scripts) != ScriptHandler
except AttributeError:
create_scripts = True
if create_cmdset:
dbobj.cmdset = CmdSetHandler(dbobj)
if utils.inherits_from(self, settings.BASE_CHARACTER_TYPECLASS) or utils.inherits_from(self, Character):
dbobj.cmdset.outside_access = False
if create_scripts:
dbobj.scripts = ScriptHandler(dbobj)
def __eq__(self, other):
"""
This has be located at this level, having it in the
@ -87,11 +57,12 @@ class Object(TypeClass):
dbref = self.dbobj.dbref
self.locks.add("control:id(%s) or perm(Immortals)" % dbref)
self.locks.add("examine:perm(Builders)")
self.locks.add("edit:perm(Wizards)")
self.locks.add("delete:perm(Wizards)")
self.locks.add("get:all()")
self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete
self.locks.add("examine:perm(Builders)") # examine properties
self.locks.add("edit:perm(Wizards)") # edit properties/attributes
self.locks.add("delete:perm(Wizards)") # delete object
self.locks.add("get:all()") # pick up object
self.locks.add("call:true()") # allow to call commands on this object
def at_object_creation(self):
"""
@ -341,11 +312,15 @@ class Character(Object):
Setup character-specific security
"""
super(Character, self).basetype_setup()
self.locks.add("puppet:id(%s) or perm(Immortals); get:false()" % self.dbobj.dbref)
self.locks.add("puppet:id(%s) or perm(Immortals)" % self.dbobj.dbref) # who may become this object's player
self.locks.add("get:false()") # noone can pick up the character
self.locks.add("call:false()") # no commands can be called on character
# add the default cmdset
from settings import CMDSET_DEFAULT
from settings import CMDSET_DEFAULT
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True)
# no other character should be able to call commands on the Character.
self.cmdset.outside_access = False
def at_object_creation(self):
"""
@ -399,11 +374,12 @@ class Exit(Object):
"""
# the lock is open to all by default
super(Exit, self).basetype_setup()
self.locks.add("traverse:all(); get:false()")
self.locks.add("traverse:all()") # who can pass through exit
self.locks.add("get:false()") # noone can pick up the exit
def at_object_creation(self):
"""
Another example just for show; the _destination attribute
An example just for show; the _destination attribute
is usually set at creation time, not as part of the class
definition (unless you want an entire class of exits
all leadning to the same hard-coded place ...)

View file

@ -20,14 +20,14 @@ class ScriptManager(TypedObjectManager):
return []
scripts = self.filter(db_obj=obj)
if key:
return [script for script in scripts if script.key == key]
return scripts.filter(db_key=key)
return scripts
@returns_typeclass_list
def get_all_scripts(self, key=None):
"""
Return all scripts, alternative only
scripts with a certain key/dbref.
scripts with a certain key/dbref or path.
"""
if key:
dbref = self.dbref(key)
@ -39,7 +39,7 @@ class ScriptManager(TypedObjectManager):
# not a dbref. Normal key search
scripts = self.filter(db_key=key)
else:
scripts = self.all()
scripts = list(self.all())
return scripts
def delete_script(self, dbref):
@ -120,7 +120,7 @@ class ScriptManager(TypedObjectManager):
elif obj:
scripts = self.get_all_scripts_on_obj(obj, key=key)
else:
scripts = self.get_all_scripts(key=key)
scripts = self.model.get_all_cached_instances()#get_all_scripts(key=key)
if not scripts:
VALIDATE_ITERATION -= 1
return None, None

View file

@ -28,7 +28,6 @@ from django.conf import settings
from django.db import models
from src.typeclasses.models import Attribute, TypedObject
from src.scripts.manager import ScriptManager
#from src.locks.lockhandler import LockHandler
#------------------------------------------------------------
#

View file

@ -24,22 +24,6 @@ class ScriptHandler(object):
"""
self.obj = obj
# this is required to stop a nasty loop in some situations that
# has the handler infinitely recursively re-added to its object.
self.obj.scripts = self
scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj)
#print "starting scripthandler. %s has scripts %s" % (self.obj, scripts)
if scripts:
okscripts = [script for script in scripts if script.persistent == True]
delscripts = [script for script in scripts if script not in okscripts]
for script in delscripts:
#print "stopping script %s" % script
script.stop()
for script in okscripts:
#print "starting script %s" % script
script.start()
def __str__(self):
"List the scripts tied to this object"
scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj)
@ -72,39 +56,51 @@ class ScriptHandler(object):
script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart)
if not script:
logger.log_errmsg("Script %s failed to be created/start." % scriptclass)
return False
return True
def start(self, scriptkey):
def start(self, scriptid):
"""
Find an already added script and force-start it
"""
scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptkey)
scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid)
num = 0
for script in scripts:
script.start()
num += script.start()
return num
def delete(self, scriptkey):
def delete(self, scriptid):
"""
Forcibly delete a script from this object.
scriptid can be a script key or the path to a script (in the
latter case all scripts with this path will be deleted!)
"""
delscripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptkey)
delscripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid)
if not delscripts:
delscripts = [script for script in ScriptDB.objects.get_all_scripts_on_obj(self.obj) if script.path == scriptid]
num = 0
for script in delscripts:
script.stop()
num += script.stop()
return num
def stop(self, scriptkey):
def stop(self, scriptid):
"""
Alias for delete.
Alias for delete. scriptid can be a script key or a script path string.
"""
self.delete(scriptkey)
return self.delete(scriptid)
def all(self, scriptkey=None):
def all(self, scriptid=None):
"""
Get all scripts stored in the handler, alternatively all matching a key.
"""
return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptkey)
return ScriptDB.objects.get_all_scripts_on_obj(self.obj, key=scriptid)
def validate(self):
def validate(self, init_mode=False):
"""
Runs a validation on this object's scripts only.
This should be called regularly to crank the wheels.
"""
ScriptDB.objects.validate(obj=self.obj)
ScriptDB.objects.validate(obj=self.obj, init_mode=init_mode)

View file

@ -142,7 +142,8 @@ class ScriptClass(TypeClass):
try:
self.delete()
except AssertionError:
pass
return 0
return 1
def is_valid(self):
"placeholder"

View file

@ -753,7 +753,8 @@ class TypedObject(SharedMemoryModel):
for nattr in self.ndb.all():
del nattr
# run hook for this new typeclass
# run hooks for this new typeclass
new_typeclass.basetype_setup()
new_typeclass.at_object_creation()

View file

@ -35,6 +35,13 @@ class MetaTypeClass(type):
printed in a nicer way (it might end up having no name at all
otherwise due to the magics being done with get/setattribute).
"""
def __init__(mcs, *args, **kwargs):
"""
Adds some features to typeclassed objects
"""
super(MetaTypeClass, mcs).__init__(*args, **kwargs)
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
def __str__(cls):
return "%s" % cls.__name__

View file

@ -170,13 +170,13 @@ def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
new_script = typeclass(new_db_object)
# store variables on the typeclass (which means
# it's actually transparently stored on the db object)
if key:
new_db_object.name = key
else:
if not key:
if typeclass and hasattr(typeclass, '__name__'):
new_db_object.name = "%s" % typeclass.__name__
new_script.key = "%s" % typeclass.__name__
else:
new_db_object.name = "#%i" % new_db_object.id
new_script.key = "#%i" % new_db_object.id
# call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom
@ -184,6 +184,9 @@ def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
new_script.at_script_creation()
# custom-given variables override the hook
if key:
new_script.key = key
if locks:
new_script.locks.add(locks)
@ -191,12 +194,9 @@ def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
try:
new_script.obj = obj
except ValueError:
new_script.obj = obj.dbobj
new_script.save()
new_script.obj = obj.dbobj
# a new created script should always be started, so
# we do this now.
# a new created script should usually be started.
if autostart:
new_script.start()

View file

@ -33,11 +33,11 @@ def start_reload_loop():
def run_loop():
""
cemit_info('-'*50)
cemit_info(" Starting asynchronous server reload ...")
cemit_info(" Starting asynchronous server reload.")
reload_modules() # this must be given time to finish
wait_time = 5
cemit_info(" Wait for %ss to give modules time to fully re-cache ..." % wait_time)
cemit_info(" Waiting %ss to give modules time to fully re-cache ..." % wait_time)
time.sleep(wait_time)
reload_scripts()
@ -90,7 +90,7 @@ def reload_modules():
"Check so modpath is not in an unsafe module"
return not any(mpath.startswith(modpath) for mpath in unsafe_modules)
cemit_info("\n Cleaning module caches ...")
cemit_info(" Cleaning module caches ...")
# clean as much of the caches as we can
cache = AppCache()
@ -149,15 +149,15 @@ def reload_scripts(scripts=None, obj=None, key=None,
cleaned out. All persistent scripts are force-started.
"""
cemit_info(" Validating scripts ...")
nr_started, nr_stopped = ScriptDB.objects.validate(scripts=scripts,
obj=obj, key=key,
dbref=dbref,
init_mode=init_mode)
string = " Started %s script(s). Stopped %s invalid script(s)." % \
(nr_started, nr_stopped)
cemit_info(string)
if nr_started or nr_stopped:
string = " Started %s script(s). Stopped %s invalid script(s)." % \
(nr_started, nr_stopped)
cemit_info(string)
def reload_commands():
from src.commands import cmdsethandler
@ -170,12 +170,13 @@ def reset_loop():
cemit_info(" Running resets on database entities ...")
t1 = time.time()
[s.locks.reset() for s in ScriptDB.objects.all()]
[p.locks.reset() for p in PlayerDB.objects.all()]
[h.locks.reset() for h in HelpEntry.objects.all()]
[m.locks.reset() for m in Msg.objects.all()]
[c.locks.reset() for c in Channel.objects.all()]
[s.locks.reset() for s in ScriptDB.objects.all()]
[p.locks.reset() for p in PlayerDB.objects.all()]
[(o.typeclass(o), o.cmdset.reset(), o.locks.reset()) for o in ObjectDB.get_all_cached_instances()]
t2 = time.time()
cemit_info(" ... Loop finished in %g seconds." % (t2-t1))