Multiple fixes and cleanups - command parser excludes inaccessible commands already at parse level now. Fixed the functionality of a few of the lock functions to be more intuitive. Added functionality to the examine command to better show the commands available to an object.

This commit is contained in:
Griatch 2011-06-26 14:35:02 +00:00
parent 334c0b1d08
commit 95d672763b
17 changed files with 207 additions and 165 deletions

View file

@ -121,6 +121,7 @@ class Character(BaseCharacter):
else: else:
self.db.prelogout_location = self.location self.db.prelogout_location = self.location
self.location.msg_contents("%s has entered the game." % self.name) self.location.msg_contents("%s has entered the game." % self.name)
self.location.at_object_receive(self, self.location)
class Room(BaseRoom): class Room(BaseRoom):
""" """

View file

@ -31,16 +31,14 @@ command line. The process is as follows:
by the parser. The result is a list of command matches tied to their respective match object. by the parser. The result is a list of command matches tied to their respective match object.
9) If we found no matches, branch to system command CMD_NOMATCH --> Finished 9) If we found no matches, branch to system command CMD_NOMATCH --> Finished
10) If we were unable to weed out multiple matches, branch CMD_MULTIMATCH --> Finished 10) If we were unable to weed out multiple matches, branch CMD_MULTIMATCH --> Finished
11) If we have a single match, we now check user permissions. 11) We analyze the matched command to determine if it is a channel-type command, that is
not permissions: branch to system command CMD_NOPERM --> Finished
12) We analyze the matched command to determine if it is a channel-type command, that is
a command auto-created to represent a valid comm channel. If so, we see if CMD_CHANNEL is a command auto-created to represent a valid comm channel. If so, we see if CMD_CHANNEL is
custom-defined in the merged cmdset, or we launch the auto-created command custom-defined in the merged cmdset, or we launch the auto-created command
direclty --> Finished direclty --> Finished
13 We next check if this is an exit-type command, that is, a command auto-created to represent 12 We next check if this is an exit-type command, that is, a command auto-created to represent
an exit from this room. If so we check for custom CMD_EXIT in cmdset or launch an exit from this room. If so we check for custom CMD_EXIT in cmdset or launch
the auto-generated command directly --> Finished the auto-generated command directly --> Finished
14) At this point we have found a normal command. We assign useful variables to it, that 13) At this point we have found a normal command. We assign useful variables to it, that
will be available to the command coder at run-time. will be available to the command coder at run-time.
When launching the command (normal, or system command both), two hook functions are called When launching the command (normal, or system command both), two hook functions are called
@ -68,7 +66,6 @@ COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
CMD_NOINPUT = "__noinput_command" CMD_NOINPUT = "__noinput_command"
CMD_NOMATCH = "__nomatch_command" CMD_NOMATCH = "__nomatch_command"
CMD_MULTIMATCH = "__multimatch_command" CMD_MULTIMATCH = "__multimatch_command"
CMD_NOPERM = "__noperm_command"
CMD_CHANNEL = "__send_to_channel" CMD_CHANNEL = "__send_to_channel"
class NoCmdSets(Exception): class NoCmdSets(Exception):
@ -176,21 +173,9 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string and match to available cmdset. # Parse the input string and match to available cmdset.
matches = COMMAND_PARSER(raw_string, cmdset) # This also checks for permissions, so all commands in match
# are commands the caller is allowed to call.
#string ="Command candidates" matches = COMMAND_PARSER(raw_string, cmdset, caller)
#for cand in cmd_candidates:
# string += "\n %s || %s" % (cand.cmdname, cand.args)
#caller.msg(string)
# Try to produce a unique match between the merged
# cmdset and the candidates.
# if unloggedin:
# matches = match_command(cmd_candidates, cmdset)
# else:
# matches = match_command(cmd_candidates, cmdset, caller)
#print "matches: ", matches
# Deal with matches # Deal with matches
if not matches: if not matches:
@ -216,15 +201,6 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
match = matches[0] match = matches[0]
cmdname, args, cmd = match[0], match[1], match[2] cmdname, args, cmd = match[0], match[1], match[2]
# Check so we have permission to use this command.
if not cmd.access(caller):
cmd = cmdset.get(CMD_NOPERM)
if cmd:
sysarg = raw_string
else:
sysarg = "Huh? (type 'help' for help)"
raise ExecSystemCommand(cmd, sysarg)
# Check if this is a Channel match. # Check if this is a Channel match.
if hasattr(cmd, 'is_channel') and cmd.is_channel: if hasattr(cmd, 'is_channel') and cmd.is_channel:
# even if a user-defined syscmd is not defined, the # even if a user-defined syscmd is not defined, the

View file

@ -5,7 +5,7 @@ replacing cmdparser function. The replacement parser must
return a CommandCandidates object. return a CommandCandidates object.
""" """
def cmdparser(raw_string, cmdset, match_index=None): def cmdparser(raw_string, cmdset, caller, match_index=None):
""" """
This function is called by the cmdhandler once it has This function is called by the cmdhandler once it has
gathered all valid cmdsets for the calling player. raw_string gathered all valid cmdsets for the calling player. raw_string
@ -61,6 +61,9 @@ def cmdparser(raw_string, cmdset, match_index=None):
# feed result back to parser iteratively # feed result back to parser iteratively
return cmdparser(new_raw_string, cmdset, match_index=mindex) return cmdparser(new_raw_string, cmdset, match_index=mindex)
# only select command matches we are actually allowed to call.
matches = [match for match in matches if match[2].access(caller, 'cmd')]
if len(matches) > 1: if len(matches) > 1:
# see if it helps to analyze the match with preserved case. # see if it helps to analyze the match with preserved case.
matches = [match for match in matches if raw_string.startswith(match[0])] matches = [match for match in matches if raw_string.startswith(match[0])]

View file

@ -180,6 +180,7 @@ class CmdSet(object):
no_exits = False no_exits = False
no_objs = False no_objs = False
no_channels = False no_channels = False
permanent = False
def __init__(self, cmdsetobj=None, key=None): def __init__(self, cmdsetobj=None, key=None):
""" """
@ -272,6 +273,10 @@ class CmdSet(object):
if thiscmd == cmd: if thiscmd == cmd:
return thiscmd return thiscmd
def count(self):
"Return number of commands in set"
return len(self.commands)
def get_system_cmds(self): def get_system_cmds(self):
""" """
Return system commands in the cmdset, defined as Return system commands in the cmdset, defined as
@ -323,7 +328,7 @@ class CmdSet(object):
""" """
Show all commands in cmdset when printing it. Show all commands in cmdset when printing it.
""" """
return ", ".join([str(cmd) for cmd in self.commands]) return ", ".join([str(cmd) for cmd in sorted(self.commands, key=lambda o:o.key)])
def __iter__(self): def __iter__(self):
""" """

View file

@ -64,7 +64,7 @@ example, you can have a 'On a boat' set, onto which you then tack on
the 'Fishing' set. Fishing from a boat? No problem! the 'Fishing' set. Fishing from a boat? No problem!
""" """
import traceback import traceback
from src.utils import logger from src.utils import logger, utils
from src.commands.cmdset import CmdSet from src.commands.cmdset import CmdSet
from src.server.models import ServerConfig from src.server.models import ServerConfig
@ -163,7 +163,7 @@ class CmdSetHandler(object):
"Display current commands" "Display current commands"
string = "" string = ""
merged = False mergelist = []
if len(self.cmdset_stack) > 1: if len(self.cmdset_stack) > 1:
# We have more than one cmdset in stack; list them all # We have more than one cmdset in stack; list them all
num = 0 num = 0
@ -176,19 +176,19 @@ class CmdSetHandler(object):
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)
mergelist.append(str(snum))
string += "\n" string += "\n"
merged = True
# Display the currently active cmdset, limited by self.obj's permissions # Display the currently active cmdset, limited by self.obj's permissions
mergetype = self.mergetype_stack[-1] mergetype = self.mergetype_stack[-1]
if mergetype != self.current.mergetype: if mergetype != self.current.mergetype:
merged_on = self.cmdset_stack[-2].key merged_on = self.cmdset_stack[-2].key
mergetype = "custom %s on %s" % (mergetype, merged_on) mergetype = "custom %s on %s" % (mergetype, merged_on)
if merged: if mergelist:
string += " <Merged (%s, prio %i)>: %s" % (mergetype, self.current.priority, self.current) string += " <Merged %s (%s, prio %i)>: %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current)
else: else:
string += " <%s (%s, prio %i)>: %s" % (self.current.key, mergetype, self.current.priority, string += " <%s (%s, prio %i)>: %s" % (self.current.key, mergetype, self.current.priority,
", ".join(cmd.key for cmd in self.current if cmd.access(self.obj, "cmd"))) ", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
return string.strip() return string.strip()
def update(self, init_mode=False): def update(self, init_mode=False):
@ -259,6 +259,8 @@ class CmdSetHandler(object):
that has to be documented. that has to be documented.
""" """
if callable(cmdset): if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
raise Exception("Only CmdSets can be added to the cmdsethandler!")
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.
@ -286,6 +288,8 @@ class CmdSetHandler(object):
See also the notes for self.add(), which applies here too. See also the notes for self.add(), which applies here too.
""" """
if callable(cmdset): if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
raise Exception("Only CmdSets can be added to the cmdsethandler!")
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.

View file

@ -31,6 +31,9 @@ class CommandMeta(type):
temp = [] temp = []
if hasattr(mcs, 'permissions'): if hasattr(mcs, 'permissions'):
mcs.locks = mcs.permissions mcs.locks = mcs.permissions
if not hasattr(mcs, 'locks'):
# default if one forgets to define completely
mcs.locks = "cmd:all()"
for lockstring in mcs.locks.split(';'): for lockstring in mcs.locks.split(';'):
if lockstring and not ':' in lockstring: if lockstring and not ':' in lockstring:
lockstring = "cmd:%s" % lockstring lockstring = "cmd:%s" % lockstring

View file

@ -218,8 +218,8 @@ class CmdBatchCommands(MuxCommand):
if not commands: if not commands:
string = "'%s' not found.\nYou have to supply the python path " string = "'%s' not found.\nYou have to supply the python path "
string += "of the file relative to \nyour batch-file directory (%s)." string += "of the file relative to \none of your batch-file directories (%s)."
caller.msg(string % (python_path, settings.BASE_BATCHPROCESS_PATH)) caller.msg(string % (python_path, ", ".join(settings.BASE_BATCHPROCESS_PATHS)))
return return
switches = self.switches switches = self.switches
@ -404,9 +404,10 @@ class CmdStateRR(MuxCommand):
def func(self): def func(self):
caller = self.caller caller = self.caller
if caller.ndb.batch_batchmode == "batch_code": if caller.ndb.batch_batchmode == "batch_code":
BATCHCODE.parse_file(caller.ndb.batch_pythonpath) new_data = BATCHCODE.parse_file(caller.ndb.batch_pythonpath)
else: else:
BATCHCMD.parse_file(caller.ndb.batch_pythonpath) new_data = BATCHCMD.parse_file(caller.ndb.batch_pythonpath)
caller.ndb.batch_stack = new_data
caller.msg(format_code("File reloaded. Staying on same command.")) caller.msg(format_code("File reloaded. Staying on same command."))
show_curr(caller) show_curr(caller)

View file

@ -350,6 +350,7 @@ class CmdCreate(ObjManipCommand):
switch: switch:
drop - automatically drop the new object into your current location (this is not echoed) drop - automatically drop the new object into your current location (this is not echoed)
this also sets the new object's home to the current location rather than to you.
Creates one or more new objects. If typeclass is given, the object Creates one or more new objects. If typeclass is given, the object
is created as a child of this typeclass. The typeclass script is is created as a child of this typeclass. The typeclass script is
@ -405,6 +406,7 @@ class CmdCreate(ObjManipCommand):
obj.db.desc = "You see nothing special." obj.db.desc = "You see nothing special."
if 'drop' in self.switches: if 'drop' in self.switches:
if caller.location: if caller.location:
obj.home = caller.location
obj.move_to(caller.location, quiet=True) obj.move_to(caller.location, quiet=True)
caller.msg(string) caller.msg(string)
@ -482,7 +484,7 @@ class CmdDesc(MuxCommand):
return return
desc = self.rhs desc = self.rhs
else: else:
obj = caller obj = caller.location
desc = self.args desc = self.args
# storing the description # storing the description
obj.db.desc = desc obj.db.desc = desc
@ -494,14 +496,17 @@ class CmdDestroy(MuxCommand):
@destroy - remove objects from the game @destroy - remove objects from the game
Usage: Usage:
@destroy[/<switches>] obj [,obj2, obj3, ...] @destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]
@delete ''
switches: switches:
override - The @destroy command will usually avoid accidentally destroying override - The @destroy command will usually avoid accidentally destroying
player objects. This switch overrides this safety. player objects. This switch overrides this safety.
examples:
@destroy house, roof, door, 44-78
@destroy 5-10, flower, 45
Destroys one or many objects. Destroys one or many objects. If dbrefs are used, a range to delete can be
given, e.g. 4-10. Also the end points will be deleted.
""" """
key = "@destroy" key = "@destroy"
@ -515,22 +520,22 @@ class CmdDestroy(MuxCommand):
caller = self.caller caller = self.caller
if not self.args or not self.lhslist: if not self.args or not self.lhslist:
caller.msg("Usage: @destroy[/switches] obj [,obj2, obj3, ...]") caller.msg("Usage: @destroy[/switches] [obj, obj2, obj3, [dbref-dbref],...]")
return return ""
def delobj(objname):
# helper function for deleting a single object
string = "" string = ""
for objname in self.lhslist:
obj = caller.search(objname) obj = caller.search(objname)
if not obj: if not obj:
self.caller.msg(" (Objects to destroy must either be local or specified with a unique dbref.)") self.caller.msg(" (Objects to destroy must either be local or specified with a unique dbref.)")
return return ""
objname = obj.name objname = obj.name
if not obj.access(caller, 'delete'): if not obj.access(caller, 'delete'):
string += "\nYou don't have permission to delete %s." % objname return "\nYou don't have permission to delete %s." % objname
continue
if obj.player and not 'override' in self.switches: if obj.player and not 'override' in self.switches:
string += "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname return "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname
continue
had_exits = hasattr(obj, "exits") and obj.exits had_exits = hasattr(obj, "exits") and obj.exits
had_objs = hasattr(obj, "contents") and any(obj for obj in obj.contents had_objs = hasattr(obj, "contents") and any(obj for obj in obj.contents
if not (hasattr(obj, "exits") and obj not in obj.exits)) if not (hasattr(obj, "exits") and obj not in obj.exits))
@ -544,6 +549,18 @@ class CmdDestroy(MuxCommand):
string += " Exits to and from %s were destroyed as well." % objname string += " Exits to and from %s were destroyed as well." % objname
if had_objs: if had_objs:
string += " Objects inside %s were moved to their homes." % objname string += " Objects inside %s were moved to their homes." % objname
return string
string = ""
for objname in self.lhslist:
if '-' in objname:
# might be a range of dbrefs
dmin, dmax = [utils.dbref(part) for part in objname.split('-', 1)]
if dmin and dmax:
for dbref in range(int(dmin),int(dmax+1)):
string += delobj(str(dbref))
else:
string += delobj(objname)
if string: if string:
caller.msg(string.strip()) caller.msg(string.strip())
@ -619,7 +636,7 @@ class CmdDig(ObjManipCommand):
to_exit = self.rhs_objs[0] to_exit = self.rhs_objs[0]
if not to_exit["name"]: if not to_exit["name"]:
exit_to_string = \ exit_to_string = \
"\nYou didn't give a name for the exit to the new room." "\nNo exit created to new room."
elif not location: elif not location:
exit_to_string = \ exit_to_string = \
"\nYou cannot create an exit from a None-location." "\nYou cannot create an exit from a None-location."
@ -646,7 +663,7 @@ class CmdDig(ObjManipCommand):
back_exit = self.rhs_objs[1] back_exit = self.rhs_objs[1]
if not back_exit["name"]: if not back_exit["name"]:
exit_back_string = \ exit_back_string = \
"\nYou didn't give a name for the exit back here." "\nNo back exit created."
elif not location: elif not location:
exit_back_string = \ exit_back_string = \
"\nYou cannot create an exit back to a None-location." "\nYou cannot create an exit back to a None-location."
@ -1506,7 +1523,8 @@ class CmdExamine(ObjManipCommand):
"destination":"\n{wDestination{n: %s", "destination":"\n{wDestination{n: %s",
"perms":"\n{wPermissions{n: %s", "perms":"\n{wPermissions{n: %s",
"locks":"\n{wLocks{n:", "locks":"\n{wLocks{n:",
"cmdset":"\n{wCurrent Cmdset (including permission checks){n:\n %s", "cmdset":"\n{wCurrent Cmdset(s){n:\n %s",
"cmdset_avail":"\n{wActual commands available to %s (incl. lock-checks, external cmds etc){n:\n %s",
"scripts":"\n{wScripts{n:\n %s", "scripts":"\n{wScripts{n:\n %s",
"exits":"\n{wExits{n: ", "exits":"\n{wExits{n: ",
"characters":"\n{wCharacters{n: ", "characters":"\n{wCharacters{n: ",
@ -1520,7 +1538,8 @@ class CmdExamine(ObjManipCommand):
"destination":"\nDestination: %s", "destination":"\nDestination: %s",
"perms":"\nPermissions: %s", "perms":"\nPermissions: %s",
"locks":"\nLocks:", "locks":"\nLocks:",
"cmdset":"\nCurrent Cmdset (including permission checks):\n %s", "cmdset":"\nCurrent Cmdset(s):\n %s",
"cmdset_avail":"\nActual commands available to %s (incl. lock-checks, external cmds, etc):\n %s",
"scripts":"\nScripts:\n %s", "scripts":"\nScripts:\n %s",
"exits":"\nExits: ", "exits":"\nExits: ",
"characters":"\nCharacters: ", "characters":"\nCharacters: ",
@ -1556,8 +1575,18 @@ class CmdExamine(ObjManipCommand):
string += headers["locks"] + utils.fill("; ".join([lock for lock in locks.split(';')]), indent=6) string += headers["locks"] + utils.fill("; ".join([lock for lock in locks.split(';')]), indent=6)
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"): if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"):
# list the current cmdsets
cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")]) cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")])
string += headers["cmdset"] % cmdsetstr string += headers["cmdset"] % cmdsetstr
# list the actually available commands
from src.commands.cmdhandler import get_and_merge_cmdsets
avail_cmdset = get_and_merge_cmdsets(obj)
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2)
string += headers["cmdset_avail"] % (obj.key, cmdsetstr)
if hasattr(obj, "scripts") and hasattr(obj.scripts, "all") and obj.scripts.all(): if hasattr(obj, "scripts") and hasattr(obj.scripts, "all") and obj.scripts.all():
string += headers["scripts"] % obj.scripts string += headers["scripts"] % obj.scripts
# add the attributes # add the attributes

View file

@ -230,10 +230,10 @@ class CmdInventory(MuxCommand):
cols = [[],[]] cols = [[],[]]
for item in items: for item in items:
cols[0].append(item.name) cols[0].append(item.name)
desc = utils.crop(item.db.desc) desc = item.db.desc
if not desc: if not desc:
desc = "" desc = ""
cols[1].append(desc) cols[1].append(utils.crop(str(desc)))
# auto-format the columns to make them evenly wide # auto-format the columns to make them evenly wide
ftable = utils.format_table(cols) ftable = utils.format_table(cols)
string = "You are carrying:" string = "You are carrying:"

View file

@ -26,7 +26,6 @@ from src.utils import create
from src.commands.cmdhandler import CMD_NOINPUT from src.commands.cmdhandler import CMD_NOINPUT
from src.commands.cmdhandler import CMD_NOMATCH from src.commands.cmdhandler import CMD_NOMATCH
from src.commands.cmdhandler import CMD_MULTIMATCH from src.commands.cmdhandler import CMD_MULTIMATCH
from src.commands.cmdhandler import CMD_NOPERM
from src.commands.cmdhandler import CMD_CHANNEL from src.commands.cmdhandler import CMD_CHANNEL
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
@ -125,25 +124,7 @@ class SystemMultimatch(MuxCommand):
string = self.format_multimatches(self.caller, self.matches) string = self.format_multimatches(self.caller, self.matches)
self.caller.msg(string) self.caller.msg(string)
# Command called when the command given at the command line
class SystemNoPerm(MuxCommand):
"""
This is called when the user does not have the
correct permissions to use a particular command.
"""
key = CMD_NOPERM
locks = "cmd:all()"
def func(self):
"""
This receives the original raw
input string (the one whose command failed to validate)
as argument.
"""
self.caller.msg("You are not allowed to do that.")
# Command called when the comman given at the command line
# was identified as a channel name, like there existing a # was identified as a channel name, like there existing a
# channel named 'ooc' and the user wrote # channel named 'ooc' and the user wrote
# > ooc Hello! # > ooc Hello!

View file

@ -124,6 +124,7 @@ class CmdScripts(MuxCommand):
Switches: Switches:
stop - stops an existing script stop - stops an existing script
kill - kills a script - without running its cleanup hooks
validate - run a validation on the script(s) validate - run a validation on the script(s)
If no switches are given, this command just views all active If no switches are given, this command just views all active
@ -212,13 +213,17 @@ class CmdScripts(MuxCommand):
caller.msg(string) caller.msg(string)
return return
if self.switches and self.switches[0] in ('stop', 'del', 'delete'): if self.switches and self.switches[0] in ('stop', 'del', 'delete', 'kill'):
# we want to delete something # we want to delete something
if not scripts: if not scripts:
string = "No scripts/objects matching '%s'. " % args string = "No scripts/objects matching '%s'. " % args
string += "Be more specific." string += "Be more specific."
elif len(scripts) == 1: elif len(scripts) == 1:
# we have a unique match! # we have a unique match!
if 'kill' in self.switches:
string = "Killing script '%s'" % scripts[0].key
scripts[0].stop(kill=True)
else:
string = "Stopping script '%s'." % scripts[0].key string = "Stopping script '%s'." % scripts[0].key
scripts[0].stop() scripts[0].stop()
ScriptDB.objects.validate() #just to be sure all is synced ScriptDB.objects.validate() #just to be sure all is synced

View file

@ -217,10 +217,11 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
the one given). the one given).
Searches attributes *and* properties stored on the checking Searches attributes *and* properties stored on the checking
object. The first form works like a flag - if the attribute/property object. The first form works like a flag - if the
exists on the object, it returns True. The second form also requires attribute/property exists on the object, the value is checked for
that the value of the attribute/property matches. Note that all True/False. The second form also requires that the value of the
retrieved values will be converted to strings before doing the comparison. attribute/property matches. Note that all retrieved values will be
converted to strings before doing the comparison.
""" """
# deal with arguments # deal with arguments
if not args: if not args:
@ -259,14 +260,14 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
if hasattr(accessing_obj, attrname): if hasattr(accessing_obj, attrname):
if value: if value:
return valcompare(str(getattr(accessing_obj, attrname)), value, compare) return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
return True return getattr(accessing_obj, attrname) # will return Fail on False value etc
# check attributes, if they exist # check attributes, if they exist
if (hasattr(accessing_obj, 'has_attribute') if (hasattr(accessing_obj, 'has_attribute')
and accessing_obj.has_attribute(attrname)): and accessing_obj.has_attribute(attrname)):
if value: if value:
return (hasattr(accessing_obj, 'get_attribute') return (hasattr(accessing_obj, 'get_attribute')
and valcompare(accessing_obj.get_attribute(attrname), value, compare)) and valcompare(accessing_obj.get_attribute(attrname), value, compare))
return True return accessing_obj.get_attribute(attrname) # fails on False/None values
return False return False
def objattr(accessing_obj, accessed_obj, *args, **kwargs): def objattr(accessing_obj, accessed_obj, *args, **kwargs):
@ -292,8 +293,7 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
locattr(attrname, value, compare=type) locattr(attrname, value, compare=type)
Works like attr, except it looks for an attribute on Works like attr, except it looks for an attribute on
accessing_obj.location, if such an entity exists. Suitable accessing_obj.location, if such an entity exists.
for commands.
""" """
if hasattr(accessing_obj, "location"): if hasattr(accessing_obj, "location"):
@ -348,43 +348,50 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
""" """
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ne'}) return attr(accessing_obj, accessed_obj, *args, **{'compare':'ne'})
def holds(accessing_obj, accessed_obj, objid, *args, **kwargs): def holds(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
holds(object_id) holds() # checks if accessed_obj or accessed_obj.obj is held by accessing_obj
holds(key/dbref) # checks if accessing_obj holds an object with given key/dbref
This is passed if accessing_obj 'contains' an object with the given
key name or dbref.
"""
dbref = utils.dbref(objid)
contains = accessing_obj.contains
if dbref and any((True for obj in contains if obj.id == dbref)):
return True
objid = objid.lower()
return any((True for obj in contains
if obj.name.lower() == objid or objid in [al.lower() for al in obj.aliases]))
def carried(accessing_obj, accessed_obj):
"""
Usage:
carried()
This is passed if accessed_obj is carried by accessing_obj (that is, This is passed if accessed_obj is carried by accessing_obj (that is,
accessed_obj.location == accessing_obj) accessed_obj.location == accessing_obj), or if accessing_obj itself holds an
object matching the given key.
""" """
return hasattr(accessed_obj, "location") and accessed_obj.location == accessing_obj print "holds ..."
try:
# commands and scripts don't have contents, so we are usually looking
# for the contents of their .obj property instead (i.e. the object the
# command/script is attached to).
contents = accessing_obj.contents
except AttributeError:
try:
contents = accessing_obj.obj.contents
except AttributeError:
return False
print "holds", contents, accessing_obj, accessed_obj
def objcarried(accessing_obj, accessed_obj): def check_holds(objid):
""" # helper function. Compares both dbrefs and keys/aliases.
Usage: objid = str(objid)
objcarried() dbref = utils.dbref(objid)
if dbref and any((True for obj in contents if obj.id == dbref)):
return True
objid = objid.lower()
return any((True for obj in contents
if obj.key.lower() == objid or objid in [al.lower() for al in obj.aliases]))
Like carried, except this lock looks for a property "obj" on the accessed_obj if args and args[0]:
and tries to determine if *this* is carried by accessing_obj. This works well return check_holds(args[0])
for accessing commands and scripts. else:
""" try:
return hasattr(accessed_obj, "obj") and accessed_obj.obj and \ if check_holds(accessed_obj.id):
hasattr(accessed_obj.obj, "location") and accessed_obj.obj.location == accessing_obj print "holds: accessed_obj.id - True"
return True
except Exception:
pass
print "holds: accessed_obj.obj.id -", hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
def superuser(*args, **kwargs): def superuser(*args, **kwargs):
""" """
@ -406,7 +413,7 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
A given True/False or integers will be converted properly. A given True/False or integers will be converted properly.
""" """
if not args: if not args or not args[0]:
return False return False
if len(args) < 2: if len(args) < 2:
setting = args[0] setting = args[0]

View file

@ -14,6 +14,7 @@ the database object. Like everything else, they can be accessed
transparently through the decorating TypeClass. transparently through the decorating TypeClass.
""" """
import traceback
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -448,6 +449,13 @@ class ObjectDB(TypedObject):
""" """
return any(self.sessions) return any(self.sessions)
has_player = property(has_player_get) has_player = property(has_player_get)
is_player = property(has_player_get)
#@property
def is_superuser_get(self):
"Check if user has a player, and if so, if it is a superuser."
return any(self.sessions) and self.player.is_superuser
is_superuser = property(is_superuser_get)
#@property #@property
def contents_get(self, exclude=None): def contents_get(self, exclude=None):
@ -612,6 +620,12 @@ class ObjectDB(TypedObject):
quiet: (bool) If true, don't emit left/arrived messages. quiet: (bool) If true, don't emit left/arrived messages.
emit_to_obj: (Object) object to receive error messages emit_to_obj: (Object) object to receive error messages
""" """
def logerr(string=""):
trc = traceback.format_exc()
errstring = "%s%s" % (trc, string)
logger.log_trace()
self.msg(errstring)
errtxt = "Couldn't perform move ('%s'). Contact an admin." errtxt = "Couldn't perform move ('%s'). Contact an admin."
if not emit_to_obj: if not emit_to_obj:
emit_to_obj = self emit_to_obj = self
@ -628,8 +642,9 @@ class ObjectDB(TypedObject):
if not self.at_before_move(destination): if not self.at_before_move(destination):
return return
except Exception: except Exception:
emit_to_obj.msg(errtxt % "at_before_move()") logerr(errtxt % "at_before_move()")
logger.log_trace() #emit_to_obj.msg(errtxt % "at_before_move()")
#logger.log_trace()
return False return False
# Save the old location # Save the old location
@ -648,8 +663,9 @@ class ObjectDB(TypedObject):
try: try:
source_location.at_object_leave(self, destination) source_location.at_object_leave(self, destination)
except Exception: except Exception:
emit_to_obj.msg(errtxt % "at_object_leave()") logerr(errtxt % "at_object_leave()")
logger.log_trace() #emit_to_obj.msg(errtxt % "at_object_leave()")
#logger.log_trace()
return False return False
if not quiet: if not quiet:
@ -657,8 +673,9 @@ class ObjectDB(TypedObject):
try: try:
self.announce_move_from(destination) self.announce_move_from(destination)
except Exception: except Exception:
emit_to_obj.msg(errtxt % "at_announce_move()" ) logerr(errtxt % "at_announce_move()")
logger.log_trace() #emit_to_obj.msg(errtxt % "at_announce_move()" )
#logger.log_trace()
return False return False
# Perform move # Perform move
@ -674,8 +691,19 @@ class ObjectDB(TypedObject):
try: try:
self.announce_move_to(source_location) self.announce_move_to(source_location)
except Exception: except Exception:
emit_to_obj.msg(errtxt % "announce_move_to()") logerr(errtxt % "announce_move_to()")
logger.log_trace() #emit_to_obj.msg(errtxt % "announce_move_to()")
#logger.log_trace()
return False
# Perform eventual extra commands on the receiving location
# (the object has already arrived at this point)
try:
destination.at_object_receive(self, source_location)
except Exception:
logerr(errtxt % "at_object_receive()")
#emit_to_obj.msg(errtxt % "at_object_receive()")
#logger.log_trace()
return False return False
# Execute eventual extra commands on this object after moving it # Execute eventual extra commands on this object after moving it
@ -683,16 +711,9 @@ class ObjectDB(TypedObject):
try: try:
self.at_after_move(source_location) self.at_after_move(source_location)
except Exception: except Exception:
emit_to_obj.msg(errtxt % "at_after_move()") logerr(errtxt % "at_after_move")
logger.log_trace() #emit_to_obj.msg(errtxt % "at_after_move()")
return False #logger.log_trace()
# Perform eventual extra commands on the receiving location
try:
destination.at_object_receive(self, source_location)
except Exception:
emit_to_obj.msg(errtxt % "at_obj_receive()")
logger.log_trace()
return False return False

View file

@ -235,7 +235,7 @@ class Object(TypeClass):
exits = [] exits = []
users = [] users = []
things = [] things = []
for content in self.contents: for content in [con for con in self.contents if con.access(pobject, 'view')]:
if content == pobject: if content == pobject:
continue continue
name = content.name name = content.name

View file

@ -47,11 +47,13 @@ class ScriptHandler(object):
def add(self, scriptclass, key=None, autostart=True): def add(self, scriptclass, key=None, autostart=True):
""" """
Add an script to this object. The scriptclass Add an script to this object.
argument can be either a class object
scriptclass - either a class object
inheriting from Script, an instantiated script object inheriting from Script, an instantiated script object
or a python path to such a class object. or a python path to such a class object.
key - optional identifier for the script (often set in script definition)
autostart - start the script upon adding it
""" """
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:

View file

@ -123,12 +123,15 @@ class ScriptClass(TypeClass):
#print "... Start cancelled (invalid start or already running)." #print "... Start cancelled (invalid start or already running)."
return 0 # this is used by validate() for counting started scripts return 0 # this is used by validate() for counting started scripts
def stop(self): def stop(self, kill=False):
""" """
Called to stop the script from running. Called to stop the script from running.
This also deletes the script. This also deletes the script.
kill - don't call finishing hooks.
""" """
#print "stopping script %s" % self.key #print "stopping script %s" % self.key
if not kill:
try: try:
self.at_stop() self.at_stop()
except Exception: except Exception:

View file

@ -163,13 +163,14 @@ def read_batchfile(pythonpath, file_ending='.py'):
""" """
# open the file # open the file
if pythonpath and not (pythonpath.startswith('src.') or pythonpath.startswith('game.')): if pythonpath and not (pythonpath.startswith('src.') or pythonpath.startswith('game.')
or pythonpath.startswith('contrib.')):
abspaths = [] abspaths = []
for basepath in settings.BASE_BATCHPROCESS_PATHS: for basepath in settings.BASE_BATCHPROCESS_PATHS:
abspaths.append(utils.pypath_to_realpath("%s.%s" % (basepath, pythonpath), file_ending)) abspaths.append(utils.pypath_to_realpath("%s.%s" % (basepath, pythonpath), file_ending))
else: else:
abspaths = [pythonpath] abspaths = [utils.pypath_to_realpath(pythonpath, file_ending)]
fobj = None fobj, lines, err = None, [], None
for file_encoding in ENCODINGS: for file_encoding in ENCODINGS:
# try different encodings, in order # try different encodings, in order
load_errors = [] load_errors = []