diff --git a/game/gamesrc/objects/baseobjects.py b/game/gamesrc/objects/baseobjects.py index 0781d64822..1aa7ea1b3f 100644 --- a/game/gamesrc/objects/baseobjects.py +++ b/game/gamesrc/objects/baseobjects.py @@ -121,7 +121,8 @@ class Character(BaseCharacter): else: self.db.prelogout_location = self.location self.location.msg_contents("%s has entered the game." % self.name) - + self.location.at_object_receive(self, self.location) + class Room(BaseRoom): """ Rooms are like any object, except their location is None diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index e4d929fa37..1ca93789ea 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -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. 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 -11) If we have a single match, we now check user permissions. - 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 +11) 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 custom-defined in the merged cmdset, or we launch the auto-created command 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 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. 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_NOMATCH = "__nomatch_command" CMD_MULTIMATCH = "__multimatch_command" -CMD_NOPERM = "__noperm_command" CMD_CHANNEL = "__send_to_channel" class NoCmdSets(Exception): @@ -176,21 +173,9 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False): raise ExecSystemCommand(syscmd, sysarg) # Parse the input string and match to available cmdset. - matches = COMMAND_PARSER(raw_string, cmdset) - - #string ="Command candidates" - #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 + # This also checks for permissions, so all commands in match + # are commands the caller is allowed to call. + matches = COMMAND_PARSER(raw_string, cmdset, caller) # Deal with matches if not matches: @@ -216,15 +201,6 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False): match = matches[0] 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. if hasattr(cmd, 'is_channel') and cmd.is_channel: # even if a user-defined syscmd is not defined, the diff --git a/src/commands/cmdparser.py b/src/commands/cmdparser.py index 9563774c9e..55b6a67407 100644 --- a/src/commands/cmdparser.py +++ b/src/commands/cmdparser.py @@ -5,7 +5,7 @@ replacing cmdparser function. The replacement parser must 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 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 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: # see if it helps to analyze the match with preserved case. matches = [match for match in matches if raw_string.startswith(match[0])] @@ -82,7 +85,7 @@ def cmdparser(raw_string, cmdset, match_index=None): if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches): # We couldn't separate match by quality, but we have an index argument to # tell us which match to use. - matches = [matches[match_index]] + matches = [matches[match_index]] # no matter what we have at this point, we have to return it. return matches diff --git a/src/commands/cmdset.py b/src/commands/cmdset.py index 2a79d58d91..fe61c08d48 100644 --- a/src/commands/cmdset.py +++ b/src/commands/cmdset.py @@ -180,7 +180,8 @@ class CmdSet(object): no_exits = False no_objs = False no_channels = False - + permanent = False + def __init__(self, cmdsetobj=None, key=None): """ Creates a new CmdSet instance. @@ -272,6 +273,10 @@ class CmdSet(object): if thiscmd == cmd: return thiscmd + def count(self): + "Return number of commands in set" + return len(self.commands) + def get_system_cmds(self): """ Return system commands in the cmdset, defined as @@ -323,7 +328,7 @@ class CmdSet(object): """ 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): """ diff --git a/src/commands/cmdsethandler.py b/src/commands/cmdsethandler.py index 79afeb4287..2b45773b1f 100644 --- a/src/commands/cmdsethandler.py +++ b/src/commands/cmdsethandler.py @@ -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! """ import traceback -from src.utils import logger +from src.utils import logger, utils from src.commands.cmdset import CmdSet from src.server.models import ServerConfig @@ -163,7 +163,7 @@ class CmdSetHandler(object): "Display current commands" string = "" - merged = False + mergelist = [] if len(self.cmdset_stack) > 1: # We have more than one cmdset in stack; list them all num = 0 @@ -176,19 +176,19 @@ class CmdSetHandler(object): string += "\n %i: <%s (%s, prio %i)>: %s" % \ (snum, cmdset.key, mergetype, cmdset.priority, cmdset) + mergelist.append(str(snum)) string += "\n" - merged = True - + # Display the currently active cmdset, limited by self.obj's permissions mergetype = self.mergetype_stack[-1] if mergetype != self.current.mergetype: merged_on = self.cmdset_stack[-2].key mergetype = "custom %s on %s" % (mergetype, merged_on) - if merged: - string += " : %s" % (mergetype, self.current.priority, self.current) + if mergelist: + string += " : %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current) else: 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() def update(self, init_mode=False): @@ -259,6 +259,8 @@ class CmdSetHandler(object): that has to be documented. """ if callable(cmdset): + if not utils.inherits_from(cmdset, CmdSet): + raise Exception("Only CmdSets can be added to the cmdsethandler!") cmdset = cmdset(self.obj) elif isinstance(cmdset, basestring): # 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. """ if callable(cmdset): + if not utils.inherits_from(cmdset, CmdSet): + raise Exception("Only CmdSets can be added to the cmdsethandler!") cmdset = cmdset(self.obj) elif isinstance(cmdset, basestring): # this is (maybe) a python path. Try to import from cache. diff --git a/src/commands/command.py b/src/commands/command.py index 1c8e8af9eb..2605586310 100644 --- a/src/commands/command.py +++ b/src/commands/command.py @@ -31,6 +31,9 @@ class CommandMeta(type): temp = [] if hasattr(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(';'): if lockstring and not ':' in lockstring: lockstring = "cmd:%s" % lockstring diff --git a/src/commands/default/batchprocess.py b/src/commands/default/batchprocess.py index 2631645910..afcf89184a 100644 --- a/src/commands/default/batchprocess.py +++ b/src/commands/default/batchprocess.py @@ -218,8 +218,8 @@ class CmdBatchCommands(MuxCommand): if not commands: string = "'%s' not found.\nYou have to supply the python path " - string += "of the file relative to \nyour batch-file directory (%s)." - caller.msg(string % (python_path, settings.BASE_BATCHPROCESS_PATH)) + string += "of the file relative to \none of your batch-file directories (%s)." + caller.msg(string % (python_path, ", ".join(settings.BASE_BATCHPROCESS_PATHS))) return switches = self.switches @@ -404,9 +404,10 @@ class CmdStateRR(MuxCommand): def func(self): caller = self.caller if caller.ndb.batch_batchmode == "batch_code": - BATCHCODE.parse_file(caller.ndb.batch_pythonpath) + new_data = BATCHCODE.parse_file(caller.ndb.batch_pythonpath) 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.")) show_curr(caller) diff --git a/src/commands/default/building.py b/src/commands/default/building.py index 03e54734e4..c668210425 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -350,6 +350,7 @@ class CmdCreate(ObjManipCommand): switch: 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 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." if 'drop' in self.switches: if caller.location: + obj.home = caller.location obj.move_to(caller.location, quiet=True) caller.msg(string) @@ -482,7 +484,7 @@ class CmdDesc(MuxCommand): return desc = self.rhs else: - obj = caller + obj = caller.location desc = self.args # storing the description obj.db.desc = desc @@ -494,14 +496,17 @@ class CmdDestroy(MuxCommand): @destroy - remove objects from the game Usage: - @destroy[/] obj [,obj2, obj3, ...] - @delete '' - + @destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...] + switches: override - The @destroy command will usually avoid accidentally destroying 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" @@ -515,22 +520,22 @@ class CmdDestroy(MuxCommand): caller = self.caller if not self.args or not self.lhslist: - caller.msg("Usage: @destroy[/switches] obj [,obj2, obj3, ...]") - return + caller.msg("Usage: @destroy[/switches] [obj, obj2, obj3, [dbref-dbref],...]") + return "" - string = "" - for objname in self.lhslist: + def delobj(objname): + # helper function for deleting a single object + string = "" obj = caller.search(objname) if not obj: self.caller.msg(" (Objects to destroy must either be local or specified with a unique dbref.)") - return + return "" objname = obj.name if not obj.access(caller, 'delete'): - string += "\nYou don't have permission to delete %s." % objname - continue + return "\nYou don't have permission to delete %s." % objname if obj.player and not 'override' in self.switches: - string += "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname - continue + return "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname + had_exits = hasattr(obj, "exits") and obj.exits had_objs = hasattr(obj, "contents") and any(obj for obj in obj.contents if not (hasattr(obj, "exits") and obj not in obj.exits)) @@ -544,9 +549,21 @@ class CmdDestroy(MuxCommand): string += " Exits to and from %s were destroyed as well." % objname if had_objs: 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: caller.msg(string.strip()) - + class CmdDig(ObjManipCommand): """ @@ -619,7 +636,7 @@ class CmdDig(ObjManipCommand): to_exit = self.rhs_objs[0] if not to_exit["name"]: 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: exit_to_string = \ "\nYou cannot create an exit from a None-location." @@ -646,7 +663,7 @@ class CmdDig(ObjManipCommand): back_exit = self.rhs_objs[1] if not back_exit["name"]: exit_back_string = \ - "\nYou didn't give a name for the exit back here." + "\nNo back exit created." elif not location: exit_back_string = \ "\nYou cannot create an exit back to a None-location." @@ -1506,7 +1523,8 @@ class CmdExamine(ObjManipCommand): "destination":"\n{wDestination{n: %s", "perms":"\n{wPermissions{n: %s", "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", "exits":"\n{wExits{n: ", "characters":"\n{wCharacters{n: ", @@ -1520,7 +1538,8 @@ class CmdExamine(ObjManipCommand): "destination":"\nDestination: %s", "perms":"\nPermissions: %s", "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", "exits":"\nExits: ", "characters":"\nCharacters: ", @@ -1556,8 +1575,18 @@ class CmdExamine(ObjManipCommand): 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"): - cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")]) + # list the current cmdsets + cmdsetstr = "\n".join([utils.fill(cmdset, indent=2) for cmdset in str(obj.cmdset).split("\n")]) 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(): string += headers["scripts"] % obj.scripts # add the attributes diff --git a/src/commands/default/general.py b/src/commands/default/general.py index 1d3b58f762..7c44c8859f 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -229,11 +229,11 @@ class CmdInventory(MuxCommand): # format item list into nice collumns cols = [[],[]] for item in items: - cols[0].append(item.name) - desc = utils.crop(item.db.desc) + cols[0].append(item.name) + desc = item.db.desc if not desc: desc = "" - cols[1].append(desc) + cols[1].append(utils.crop(str(desc))) # auto-format the columns to make them evenly wide ftable = utils.format_table(cols) string = "You are carrying:" diff --git a/src/commands/default/syscommands.py b/src/commands/default/syscommands.py index 84b05dc493..f5671cd6ff 100644 --- a/src/commands/default/syscommands.py +++ b/src/commands/default/syscommands.py @@ -26,7 +26,6 @@ from src.utils import create from src.commands.cmdhandler import CMD_NOINPUT from src.commands.cmdhandler import CMD_NOMATCH from src.commands.cmdhandler import CMD_MULTIMATCH -from src.commands.cmdhandler import CMD_NOPERM from src.commands.cmdhandler import CMD_CHANNEL from src.commands.default.muxcommand import MuxCommand @@ -124,26 +123,8 @@ class SystemMultimatch(MuxCommand): """ string = self.format_multimatches(self.caller, self.matches) self.caller.msg(string) - -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 +# Command called when the command given at the command line # was identified as a channel name, like there existing a # channel named 'ooc' and the user wrote # > ooc Hello! diff --git a/src/commands/default/system.py b/src/commands/default/system.py index 6f72a11642..88517b4214 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -124,6 +124,7 @@ class CmdScripts(MuxCommand): Switches: stop - stops an existing script + kill - kills a script - without running its cleanup hooks validate - run a validation on the script(s) If no switches are given, this command just views all active @@ -212,15 +213,19 @@ class CmdScripts(MuxCommand): caller.msg(string) 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 if not scripts: string = "No scripts/objects matching '%s'. " % args string += "Be more specific." elif len(scripts) == 1: # we have a unique match! - string = "Stopping script '%s'." % scripts[0].key - scripts[0].stop() + 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 + scripts[0].stop() ScriptDB.objects.validate() #just to be sure all is synced else: # multiple matches. diff --git a/src/locks/lockfuncs.py b/src/locks/lockfuncs.py index 4498126523..88a64bfb5e 100644 --- a/src/locks/lockfuncs.py +++ b/src/locks/lockfuncs.py @@ -217,10 +217,11 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): the one given). Searches attributes *and* properties stored on the checking - object. The first form works like a flag - if the attribute/property - exists on the object, it returns True. The second form also requires - that the value of the attribute/property matches. Note that all - retrieved values will be converted to strings before doing the comparison. + object. The first form works like a flag - if the + attribute/property exists on the object, the value is checked for + True/False. The second form also requires that the value of the + attribute/property matches. Note that all retrieved values will be + converted to strings before doing the comparison. """ # deal with arguments if not args: @@ -258,15 +259,15 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): # first, look for normal properties on the object trying to gain access if hasattr(accessing_obj, attrname): if value: - return valcompare(str(getattr(accessing_obj, attrname)), value, compare) - return True + return valcompare(str(getattr(accessing_obj, attrname)), value, compare) + return getattr(accessing_obj, attrname) # will return Fail on False value etc # check attributes, if they exist if (hasattr(accessing_obj, 'has_attribute') and accessing_obj.has_attribute(attrname)): if value: return (hasattr(accessing_obj, 'get_attribute') and valcompare(accessing_obj.get_attribute(attrname), value, compare)) - return True + return accessing_obj.get_attribute(attrname) # fails on False/None values return False 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) Works like attr, except it looks for an attribute on - accessing_obj.location, if such an entity exists. Suitable - for commands. + accessing_obj.location, if such an entity exists. """ if hasattr(accessing_obj, "location"): @@ -348,44 +348,51 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs): """ 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: - holds(object_id) - - 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() + 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 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): - """ - Usage: - objcarried() - - Like carried, except this lock looks for a property "obj" on the accessed_obj - and tries to determine if *this* is carried by accessing_obj. This works well - for accessing commands and scripts. - """ - return hasattr(accessed_obj, "obj") and accessed_obj.obj and \ - hasattr(accessed_obj.obj, "location") and accessed_obj.obj.location == accessing_obj + def check_holds(objid): + # helper function. Compares both dbrefs and keys/aliases. + objid = str(objid) + 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])) + if args and args[0]: + return check_holds(args[0]) + else: + try: + if check_holds(accessed_obj.id): + 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): """ Only accepts an accesing_obj that is superuser (e.g. user #1) @@ -406,7 +413,7 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs): A given True/False or integers will be converted properly. """ - if not args: + if not args or not args[0]: return False if len(args) < 2: setting = args[0] diff --git a/src/objects/models.py b/src/objects/models.py index 21395c23fd..fdf1c792aa 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -14,6 +14,7 @@ the database object. Like everything else, they can be accessed transparently through the decorating TypeClass. """ +import traceback from django.db import models from django.conf import settings from django.contrib.contenttypes.models import ContentType @@ -448,6 +449,13 @@ class ObjectDB(TypedObject): """ return any(self.sessions) 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 def contents_get(self, exclude=None): @@ -612,6 +620,12 @@ class ObjectDB(TypedObject): quiet: (bool) If true, don't emit left/arrived 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." if not emit_to_obj: emit_to_obj = self @@ -628,8 +642,9 @@ class ObjectDB(TypedObject): if not self.at_before_move(destination): return except Exception: - emit_to_obj.msg(errtxt % "at_before_move()") - logger.log_trace() + logerr(errtxt % "at_before_move()") + #emit_to_obj.msg(errtxt % "at_before_move()") + #logger.log_trace() return False # Save the old location @@ -648,8 +663,9 @@ class ObjectDB(TypedObject): try: source_location.at_object_leave(self, destination) except Exception: - emit_to_obj.msg(errtxt % "at_object_leave()") - logger.log_trace() + logerr(errtxt % "at_object_leave()") + #emit_to_obj.msg(errtxt % "at_object_leave()") + #logger.log_trace() return False if not quiet: @@ -657,8 +673,9 @@ class ObjectDB(TypedObject): try: self.announce_move_from(destination) except Exception: - emit_to_obj.msg(errtxt % "at_announce_move()" ) - logger.log_trace() + logerr(errtxt % "at_announce_move()") + #emit_to_obj.msg(errtxt % "at_announce_move()" ) + #logger.log_trace() return False # Perform move @@ -674,27 +691,31 @@ class ObjectDB(TypedObject): try: self.announce_move_to(source_location) except Exception: - emit_to_obj.msg(errtxt % "announce_move_to()") - logger.log_trace() + logerr(errtxt % "announce_move_to()") + #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 + # Execute eventual extra commands on this object after moving it # (usually calling 'look') try: self.at_after_move(source_location) except Exception: - emit_to_obj.msg(errtxt % "at_after_move()") - logger.log_trace() + logerr(errtxt % "at_after_move") + #emit_to_obj.msg(errtxt % "at_after_move()") + #logger.log_trace() return False - # 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 - # # Object Swap, Delete and Cleanup methods diff --git a/src/objects/objects.py b/src/objects/objects.py index 2adee59951..0b4531a944 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -235,7 +235,7 @@ class Object(TypeClass): exits = [] users = [] things = [] - for content in self.contents: + for content in [con for con in self.contents if con.access(pobject, 'view')]: if content == pobject: continue name = content.name diff --git a/src/scripts/scripthandler.py b/src/scripts/scripthandler.py index 6b9e13b2f8..a5b0c5cb39 100644 --- a/src/scripts/scripthandler.py +++ b/src/scripts/scripthandler.py @@ -47,11 +47,13 @@ class ScriptHandler(object): def add(self, scriptclass, key=None, autostart=True): """ - Add an script to this object. The scriptclass - argument can be either a class object - inheriting from Script, an instantiated script object - or a python path to such a class object. - + Add an script to this object. + + scriptclass - either a class object + inheriting from Script, an instantiated script 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) if not script: diff --git a/src/scripts/scripts.py b/src/scripts/scripts.py index 8148174354..c10f922290 100644 --- a/src/scripts/scripts.py +++ b/src/scripts/scripts.py @@ -123,16 +123,19 @@ class ScriptClass(TypeClass): #print "... Start cancelled (invalid start or already running)." 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. This also deletes the script. + + kill - don't call finishing hooks. """ #print "stopping script %s" % self.key - try: - self.at_stop() - except Exception: - logger.log_trace() + if not kill: + try: + self.at_stop() + except Exception: + logger.log_trace() if self.interval: try: self._stop_task() diff --git a/src/utils/batchprocessors.py b/src/utils/batchprocessors.py index d91ce655b6..a2b3fc9039 100644 --- a/src/utils/batchprocessors.py +++ b/src/utils/batchprocessors.py @@ -163,13 +163,14 @@ def read_batchfile(pythonpath, file_ending='.py'): """ # 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 = [] for basepath in settings.BASE_BATCHPROCESS_PATHS: abspaths.append(utils.pypath_to_realpath("%s.%s" % (basepath, pythonpath), file_ending)) else: - abspaths = [pythonpath] - fobj = None + abspaths = [utils.pypath_to_realpath(pythonpath, file_ending)] + fobj, lines, err = None, [], None for file_encoding in ENCODINGS: # try different encodings, in order load_errors = []