mirror of
https://github.com/evennia/evennia.git
synced 2026-04-01 21:47:17 +02:00
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:
parent
e965830735
commit
126e2ea61f
17 changed files with 370 additions and 216 deletions
62
game/gamesrc/scripts/examples/bodyfunctions.py
Normal file
62
game/gamesrc/scripts/examples/bodyfunctions.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ...)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,8 @@ class ScriptClass(TypeClass):
|
|||
try:
|
||||
self.delete()
|
||||
except AssertionError:
|
||||
pass
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def is_valid(self):
|
||||
"placeholder"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue