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_objlist = location.contents + caller.contents
local_objects_cmdsets = [obj.cmdset.current local_objects_cmdsets = [obj.cmdset.current
for obj in local_objlist for obj in local_objlist
if obj.cmdset.outside_access] if obj.locks.check(caller, 'call', no_superuser_bypass=True)]
# print "used objs: %s" % ([obj.name
# for obj in local_objlist
# if obj.cmdset.outside_access])
# Merge all command sets into one # Merge all command sets into one
# (the order matters, the higher-prio cmdsets are merged last) # (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 import traceback
from src.utils import logger from src.utils import logger
from src.utils import create
from src.commands.cmdset import CmdSet from src.commands.cmdset import CmdSet
from src.scripts.scripts import AddCmdSet
CACHED_CMDSETS = {} CACHED_CMDSETS = {}
@ -128,7 +126,7 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
class CmdSetHandler(object): 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. The 'current' cmdset is the merged set currently active for this object.
This is the set the game engine will retrieve when determining which This is the set the game engine will retrieve when determining which
@ -137,35 +135,29 @@ class CmdSetHandler(object):
the 'current' cmdset. the 'current' cmdset.
""" """
def __init__(self, obj, outside_access=True): def __init__(self, obj):
""" """
This method is called whenever an object is recreated. This method is called whenever an object is recreated.
obj - this is a reference to the game object this handler obj - this is a reference to the game object this handler
belongs to. 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 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. # the id of the "merged" current cmdset for easy access.
self.key = None self.key = None
# this holds the "merged" current command set # this holds the "merged" current command set
self.current = None self.current = None
# this holds a history of CommandSets # this holds a history of CommandSets
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")] self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
# this tracks which mergetypes are actually in play in the stack # this tracks which mergetypes are actually in play in the stack
self.mergetype_stack = ["Union"] 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): def __str__(self):
"Display current commands" "Display current commands"
@ -180,8 +172,8 @@ class CmdSetHandler(object):
if mergetype != cmdset.mergetype: if mergetype != cmdset.mergetype:
mergetype = "%s^" % (mergetype) mergetype = "%s^" % (mergetype)
string += "\n %i: <%s (%s, prio %i)>: %s" % \ string += "\n %i: <%s (%s, prio %i)>: %s" % \
(snum, cmdset.key, mergetype, (snum, cmdset.key, mergetype,
cmdset.priority, cmdset) cmdset.priority, cmdset)
string += "\n (combining %i cmdsets):" % (num+1) string += "\n (combining %i cmdsets):" % (num+1)
else: else:
string += "\n " string += "\n "
@ -195,22 +187,40 @@ class CmdSetHandler(object):
mergetype, self.current) mergetype, self.current)
return string.strip() return string.strip()
def update(self): def update(self, init_mode=False):
""" """
Re-adds all sets in the handler to have an updated Re-adds all sets in the handler to have an updated
current set. 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 = [] self.mergetype_stack = []
for cmdset in self.cmdset_stack: for cmdset in self.cmdset_stack:
try: try:
# for cmdset's '+' operator, order matters. # for cmdset's '+' operator, order matters.
updated = cmdset + updated new_current = cmdset + new_current
except TypeError: except TypeError:
continue continue
self.mergetype_stack.append(updated.actual_mergetype) self.mergetype_stack.append(new_current.actual_mergetype)
self.current = updated self.current = new_current
def import_cmdset(self, cmdset_path, emit_to_obj=None): def import_cmdset(self, cmdset_path, emit_to_obj=None):
""" """
load a cmdset from a module. load a cmdset from a module.
@ -247,23 +257,17 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj) cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring): elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache. # 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: if cmdset:
self.cmdset_stack.append(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() 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): def add_default(self, cmdset, emit_to_obj=None, permanent=False):
""" """
@ -281,70 +285,74 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj) cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring): elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache. # 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: if cmdset:
self.cmdset_stack[0] = cmdset if self.cmdset_stack:
self.mergetype_stack[0] = cmdset.mergetype self.cmdset_stack[0] = cmdset
self.update() self.mergetype_stack.insert[0] = cmdset.mergetype
#print "add_default:", permanent else:
if permanent: self.cmdset_stack = [cmdset]
# create a script to automatically add this cmdset at self.mergetype_stack = cmdset.mergetype
# startup. We don't start it here since the cmdset was
# already added above. if permanent:
try: if self.permanent_paths:
cmdset = "%s.%s" % (cmdset.__module__, cmdset.__class__.__name__) self.permanent_paths[0] = cmdset.path
except Exception: else:
#print traceback.format_exc() self.permanent_paths = [cmdset.path]
logger.log_trace() self.obj.cmdset_storage = self.permanent_paths
return else:
#print "cmdset to add:", cmdset if self.permanent_paths:
script = create.create_script(AddCmdSet) self.permanent_paths[0] = ""
script.db.cmdset = cmdset else:
script.db.add_default = True self.permanent_paths = [""]
self.obj.scripts.add(script, key="add_default_cmdset", autostart=False) 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, Remove a cmdset from the handler.
it attempts to remove this. If no key is given,
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 last cmdset in the stack is removed. Whenever
the cmdset_stack changes, the cmdset is updated. the cmdset_stack changes, the cmdset is updated.
The default cmdset (first entry in stack) is never The default cmdset (first entry in stack) is never
removed - remove it explicitly with delete_default. 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: if len(self.cmdset_stack) < 2:
# don't allow deleting default cmdsets here. # don't allow deleting default cmdsets here.
return return
if not key_or_class: if not cmdset:
# remove the last one in the stack (except the default position) # remove the last one in the stack (except the default position)
self.cmdset_stack.pop() self.cmdset_stack.pop()
else: self.permanent_paths.pop()
# argument key is given, is it a key or a class? else:
# try it as a callable
default_cmdset = self.cmdset_stack[0] if callable(cmdset) and hasattr(cmdset, 'path'):
indices = [i+1 for i, cset in enumerate(self.cmdset_stack[1:]) if cset.path == cmdset.path]
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__]
else: else:
# try it as a string # try it as a path or key
self.cmdset_stack = [cmdset for cmdset in self.cmdset_stack[1:] indices = [i+1 for i, cset in enumerate(self.cmdset_stack[1:]) if cset.path == cmdset or cset.key == cmdset]
if cmdset.key != key_or_class]
for i in indices:
self.cmdset_stack.insert(0, default_cmdset) del self.cmdset_stack[i]
del self.permanent_paths[i]
self.obj.cmdset_storage = self.permanent_paths
# re-sync the cmdsethandler. # re-sync the cmdsethandler.
self.update() self.update()
def delete_default(self): def delete_default(self):
"This explicitly deletes the default cmdset. It's the only command that can." "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() self.update()
def all(self): def all(self):
@ -360,6 +368,8 @@ class CmdSetHandler(object):
""" """
self.cmdset_stack = [self.cmdset_stack[0]] self.cmdset_stack = [self.cmdset_stack[0]]
self.mergetype_stack = [self.cmdset_stack[0].mergetype] 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() self.update()
def reset(self): def reset(self):

View file

@ -750,6 +750,40 @@ class CmdLink(MuxCommand):
# give feedback # give feedback
caller.msg(string) 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): class CmdListCmdSets(MuxCommand):
""" """
@ -1585,36 +1619,67 @@ class CmdTeleport(MuxCommand):
caller.msg("Teleported.") caller.msg("Teleported.")
class CmdUnLink(CmdLink): class CmdScript(MuxCommand):
""" """
@unlink - unconnect objects attach scripts
Usage: 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 Attaches the given script to the object and starts it. Script path can be given
it from whatever it was connected to. 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(script) or perm(Wizards)"
locks = "cmd:perm(unlink) or perm(Builders)"
help_key = "Building"
def func(self): def func(self):
""" "Do stuff"
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 caller = self.caller
self.rhs = ""
if not self.rhs:
string = "Usage: @script[/switch] <obj> = <script.path or script key>"
caller.msg(string)
return
# call the @link functionality inp = self.rhs
super(CmdUnLink, self).func() 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.CmdExamine())
self.add(building.CmdTypeclass()) self.add(building.CmdTypeclass())
self.add(building.CmdLock()) self.add(building.CmdLock())
self.add(building.CmdScript())
# Comm commands # Comm commands
self.add(comms.CmdAddCom()) self.add(comms.CmdAddCom())

View file

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

View file

@ -287,7 +287,7 @@ class LockHandler(object):
""" """
self.reset_flag = True 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 Checks a lock of the correct type by passing execution
off to the lock function(s). off to the lock function(s).
@ -295,14 +295,17 @@ class LockHandler(object):
accessing_obj - the object seeking access accessing_obj - the object seeking access
access_type - the type of access wanted access_type - the type of access wanted
default - if no suitable lock type is found, use this 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: if self.reset_flag:
# rebuild cache # rebuild cache
self._cache_locks(self.obj.lock_storage) self._cache_locks(self.obj.lock_storage)
self.reset_flag = False self.reset_flag = False
if (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser) \ if (not no_superuser_bypass and (hasattr(accessing_obj, 'player')
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)): 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 # 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). # latter is a safety feature since superuser cannot be authenticated at some point during the connection).
return True return True

View file

@ -22,7 +22,8 @@ from src.typeclasses.models import Attribute, TypedObject
from src.typeclasses.typeclass import TypeClass from src.typeclasses.typeclass import TypeClass
from src.objects.manager import ObjectManager from src.objects.manager import ObjectManager
from src.config.models import ConfigValue 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 import logger
from src.utils.utils import is_iter from src.utils.utils import is_iter
@ -216,18 +217,24 @@ class ObjectDB(TypedObject):
# a safety location, this usually don't change much. # a safety location, this usually don't change much.
db_home = models.ForeignKey('self', related_name="homes_set", db_home = models.ForeignKey('self', related_name="homes_set",
blank=True, null=True) blank=True, null=True)
# database storage of persistant cmdsets.
db_cmdset_storage = models.TextField(null=True)
# Database manager # Database manager
objects = ObjectManager() objects = ObjectManager()
# Add the object-specific handlers # Add the object-specific handlers
# (scripts and cmdset must be added from
# typeclass, so not added here)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"Parent must be initialized first." "Parent must be initialized first."
TypedObject.__init__(self, *args, **kwargs) 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) self.nicks = NickHandler(self)
# Wrapper properties to easily set database fields. These are # Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using # @property decorators that allows to access these fields using
# normal python operations (without having to remember to save() # normal python operations (without having to remember to save()
@ -381,6 +388,28 @@ class ObjectDB(TypedObject):
query.delete() query.delete()
aliases = property(aliases_get, aliases_set, aliases_del) 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: class Meta:
"Define Django meta options" "Define Django meta options"
verbose_name = "Object" 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. 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.typeclasses.typeclass import TypeClass
from src.commands.cmdsethandler import CmdSetHandler
from src.scripts.scripthandler import ScriptHandler
from src.objects.exithandler import EXITHANDLER from src.objects.exithandler import EXITHANDLER
from src.utils import utils
# #
# Base class to inherit from. # Base class to inherit from.
@ -33,33 +30,6 @@ class Object(TypeClass):
objects in the game. 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): def __eq__(self, other):
""" """
This has be located at this level, having it in the This has be located at this level, having it in the
@ -87,11 +57,12 @@ class Object(TypeClass):
dbref = self.dbobj.dbref dbref = self.dbobj.dbref
self.locks.add("control:id(%s) or perm(Immortals)" % dbref) self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete
self.locks.add("examine:perm(Builders)") self.locks.add("examine:perm(Builders)") # examine properties
self.locks.add("edit:perm(Wizards)") self.locks.add("edit:perm(Wizards)") # edit properties/attributes
self.locks.add("delete:perm(Wizards)") self.locks.add("delete:perm(Wizards)") # delete object
self.locks.add("get:all()") 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): def at_object_creation(self):
""" """
@ -341,11 +312,15 @@ class Character(Object):
Setup character-specific security Setup character-specific security
""" """
super(Character, self).basetype_setup() 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 # add the default cmdset
from settings import CMDSET_DEFAULT from settings import CMDSET_DEFAULT
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True) 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): def at_object_creation(self):
""" """
@ -399,11 +374,12 @@ class Exit(Object):
""" """
# the lock is open to all by default # the lock is open to all by default
super(Exit, self).basetype_setup() 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): 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 is usually set at creation time, not as part of the class
definition (unless you want an entire class of exits definition (unless you want an entire class of exits
all leadning to the same hard-coded place ...) all leadning to the same hard-coded place ...)

View file

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

View file

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

View file

@ -24,22 +24,6 @@ class ScriptHandler(object):
""" """
self.obj = obj 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): def __str__(self):
"List the scripts tied to this object" "List the scripts tied to this object"
scripts = ScriptDB.objects.get_all_scripts_on_obj(self.obj) 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) script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart)
if not script: if not script:
logger.log_errmsg("Script %s failed to be created/start." % scriptclass) 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 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: 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. 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: 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. 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. Runs a validation on this object's scripts only.
This should be called regularly to crank the wheels. 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: try:
self.delete() self.delete()
except AssertionError: except AssertionError:
pass return 0
return 1
def is_valid(self): def is_valid(self):
"placeholder" "placeholder"

View file

@ -753,7 +753,8 @@ class TypedObject(SharedMemoryModel):
for nattr in self.ndb.all(): for nattr in self.ndb.all():
del nattr del nattr
# run hook for this new typeclass # run hooks for this new typeclass
new_typeclass.basetype_setup()
new_typeclass.at_object_creation() 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 printed in a nicer way (it might end up having no name at all
otherwise due to the magics being done with get/setattribute). 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): def __str__(cls):
return "%s" % cls.__name__ 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) new_script = typeclass(new_db_object)
# store variables on the typeclass (which means # store variables on the typeclass (which means
# it's actually transparently stored on the db object) # 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__'): if typeclass and hasattr(typeclass, '__name__'):
new_db_object.name = "%s" % typeclass.__name__ new_script.key = "%s" % typeclass.__name__
else: 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 # call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom # 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() new_script.at_script_creation()
# custom-given variables override the hook # custom-given variables override the hook
if key:
new_script.key = key
if locks: if locks:
new_script.locks.add(locks) new_script.locks.add(locks)
@ -191,12 +194,9 @@ def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
try: try:
new_script.obj = obj new_script.obj = obj
except ValueError: except ValueError:
new_script.obj = obj.dbobj new_script.obj = obj.dbobj
new_script.save()
# a new created script should always be started, so # a new created script should usually be started.
# we do this now.
if autostart: if autostart:
new_script.start() new_script.start()

View file

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