diff --git a/src/commands/default/admin.py b/src/commands/default/admin.py index 40aa928d99..f2b8cca0db 100644 --- a/src/commands/default/admin.py +++ b/src/commands/default/admin.py @@ -7,7 +7,6 @@ Admin commands import time, re from django.conf import settings from django.contrib.auth.models import User -from src.players.models import PlayerDB from src.server.sessionhandler import SESSIONS from src.server.models import ServerConfig from src.utils import utils, prettytable, search @@ -92,7 +91,6 @@ class CmdBoot(MuxCommand): feedback += "\nReason given: %s" % reason for session in boot_list: - name = session.uname session.msg(feedback) pobj.disconnect_session_from_player(session.sessid) @@ -287,12 +285,7 @@ class CmdDelPlayer(MuxCommand): # We use player_search since we want to be sure to find also players # that lack characters. - players = caller.search("*%s" % args) - if not players: - try: - players = PlayerDB.objects.filter(id=args) - except ValueError: - pass + players = caller.search_player(args, quiet=True) if not players: # try to find a user instead of a Player @@ -337,7 +330,6 @@ class CmdDelPlayer(MuxCommand): player = players user = player.user - character = player.character if not player.access(caller, 'delete'): string = "You don't have the permissions to delete that player." @@ -346,17 +338,14 @@ class CmdDelPlayer(MuxCommand): uname = user.username # boot the player then delete - if character and character.has_player: - self.msg("Booting and informing player ...") - string = "\nYour account '%s' is being *permanently* deleted.\n" % uname - if reason: - string += " Reason given:\n '%s'" % reason - character.msg(string) - # we have a bootable object with a connected player - sessions = SESSIONS.sessions_from_player(character.player) - for session in sessions: - session.msg(string) - session.disconnect() + self.msg("Informing and disconnecting player ...") + string = "\nYour account '%s' is being *permanently* deleted.\n" % uname + if reason: + string += " Reason given:\n '%s'" % reason + player.unpuppet_all() + for session in SESSIONS.sessions_from_player(player): + player.msg(string, sessid=session.sessid) + player.disconnect_session_from_player(session.sessid) user.delete() player.delete() self.msg("Player %s was successfully deleted." % uname) @@ -466,7 +455,7 @@ class CmdNewPassword(MuxCommand): return # the player search also matches 'me' etc. - player = caller.search("*%s" % self.lhs, global_search=True, player=True) + player = caller.search_player(self.lhs) if not player: return player.user.set_password(self.rhs) @@ -510,8 +499,10 @@ class CmdPerm(MuxCommand): playermode = 'player' in self.switches or lhs.startswith('*') - # locate the object - obj = caller.search(lhs, global_search=True, player=playermode) + if playermode: + obj = caller.search_player(lhs) + else: + obj = caller.search(lhs, global_search=True) if not obj: return diff --git a/src/commands/default/building.py b/src/commands/default/building.py index 8f4742d7b4..dbb73a692b 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -499,9 +499,9 @@ class CmdDestroy(MuxCommand): def delobj(objname, byref=False): # helper function for deleting a single object string = "" - obj = caller.search(objname, global_dbref=byref) + obj = caller.search(objname) 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 "" if not "override" in self.switches and obj.dbid == int(settings.CHARACTER_DEFAULT_HOME.lstrip("#")): return "\nYou are trying to delete CHARACTER_DEFAULT_HOME. If you want to do this, use the /override switch." @@ -1018,7 +1018,7 @@ class CmdOpen(ObjManipCommand): # check if this exit object already exists at the location. # we need to ignore errors (so no automatic feedback)since we # have to know the result of the search to decide what to do. - exit_obj = caller.search(exit_name, location=location, ignore_errors=True) + exit_obj = caller.search(exit_name, location=location, quiet=True) if len(exit_obj) > 1: # give error message and return caller.search(exit_name, location=location) @@ -1681,8 +1681,10 @@ class CmdExamine(ObjManipCommand): self.player_mode = "player" in self.switches or obj_name.startswith('*') - - obj = caller.search(obj_name, player=self.player_mode, global_dbref=True) + if self.player_mode: + obj = self.search_player(obj_name) + else: + obj = caller.search(obj_name) if not obj: continue diff --git a/src/commands/default/general.py b/src/commands/default/general.py index 837deccd95..1c8c313d10 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -271,7 +271,7 @@ class CmdDrop(MuxCommand): # Because the DROP command by definition looks for items # in inventory, call the search function using location = caller - results = caller.search(self.args, location=caller, ignore_errors=True) + results = caller.search(self.args, location=caller, quiet=True) # now we send it into the error handler (this will output consistent # error messages if there are problems). diff --git a/src/commands/default/help.py b/src/commands/default/help.py index 9ab2e466ed..05b98b20f4 100644 --- a/src/commands/default/help.py +++ b/src/commands/default/help.py @@ -182,7 +182,6 @@ class CmdSetHelp(MuxCommand): def func(self): "Implement the function" - caller = self.caller switches = self.switches lhslist = self.lhslist diff --git a/src/commands/default/player.py b/src/commands/default/player.py index c49bd6de4b..c458d94325 100644 --- a/src/commands/default/player.py +++ b/src/commands/default/player.py @@ -390,7 +390,7 @@ class CmdWho(MuxPlayerCommand): plr_pobject = plr_pobject or session.get_player() table.add_row([utils.crop(plr_pobject.name, width=25), utils.time.format(delta_conn, 0), - utils,time_format(delta_cmd, 1)]) + utils.time_format(delta_cmd, 1)]) string = "{wPlayers:{n\n%s\n%s unique accounts logged in." % (table, nplayers==1 and "One player" or nplayers) self.msg(string) @@ -561,8 +561,6 @@ class CmdColorTest(MuxPlayerCommand): def func(self): "Show color tables" - player = self.caller - if self.args.startswith("a"): # show ansi 16-color table from src.utils import ansi diff --git a/src/commands/default/system.py b/src/commands/default/system.py index 3dcdffeac0..6288b8b095 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -7,7 +7,6 @@ System commands import traceback import os, datetime, time from time import time as timemeasure -from sys import getsizeof import sys import django, twisted diff --git a/src/objects/manager.py b/src/objects/manager.py index 0ab04edce8..711d6c7855 100644 --- a/src/objects/manager.py +++ b/src/objects/manager.py @@ -212,7 +212,7 @@ class ObjectManager(TypedObjectManager): # main search methods and helper functions @returns_typeclass_list - def object_search(self, ostring=None, + def object_search(self, ostring, attribute_name=None, typeclass=None, candidates=None, @@ -268,7 +268,7 @@ class ObjectManager(TypedObjectManager): if candidates: # Convenience check to make sure candidates are really dbobjs - candidates = [cand.dbobj for cand in make_iter(candidates) if _GA(cand, "_hasattr")(cand, "dbobj")] + candidates = [cand.dbobj for cand in make_iter(candidates) if cand] if typeclass: candidates = [cand for cand in candidates if _GA(cand, "db_typeclass_path") in typeclass] diff --git a/src/objects/models.py b/src/objects/models.py index 91e5a53d87..995de18cf3 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -25,6 +25,7 @@ from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache from src.typeclasses.typeclass import TypeClass from src.players.models import PlayerNick from src.objects.manager import ObjectManager +from src.players.models import PlayerDB from src.commands.cmdsethandler import CmdSetHandler from src.commands import cmdhandler from src.scripts.scripthandler import ScriptHandler @@ -569,8 +570,8 @@ class ObjectDB(TypedObject): - - can be used to differentiate between multiple same-named matches global_search (bool): Search all objects globally. This is overruled by "location" keyword. use_nicks (bool): Use nickname-replace (nicktype "object") on the search string - typeclass (str or Typeclass): Limit search only to Objects with this typeclass. May be a list of typeclasses - for a broader search. + typeclass (str or Typeclass, or list of either): Limit search only to Objects with this typeclass. May + be a list of typeclasses for a broader search. location (Object): Specify a location to search, if different from the self's given location plus its contents. This can also be a list of locations. attribute_name (str): Use this named Attribute to match ostring against, instead of object.key. @@ -598,7 +599,7 @@ class ObjectDB(TypedObject): # handle some common self-references: if ostring == _HERE: return self.location - if ostring in (_ME, _SELF, '*' + _ME, '*' + _SELF): + if ostring in (_ME, _SELF): return self if use_nicks: @@ -633,15 +634,25 @@ class ObjectDB(TypedObject): # db manager expects database objects candidates = [obj.dbobj for obj in candidates] - results = ObjectDB.objects.object_search(ostring=ostring, - typeclass=typeclass, + results = ObjectDB.objects.object_search(ostring, attribute_name=attribute_name, + typeclass=typeclass, candidates=candidates, exact=exact) if quiet: return results return _AT_SEARCH_RESULT(self, ostring, results, global_search) + def search_player(self, ostring, quiet=False): + """ + Simple wrapper of the player search also handling me, self + """ + if ostring in (_ME, _SELF) and _GA(self, "db_player"): + return _GA(self, "db_player") + results = PlayerDB.objects.player_search(ostring) + if quiet: + return results + return _AT_SEARCH_RESULT(self, ostring, results, True) # # Execution/action methods diff --git a/src/objects/objects.py b/src/objects/objects.py index d64465bc39..bba4e45549 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -143,58 +143,63 @@ class Object(TypeClass): def search(self, ostring, global_search=False, - global_dbref=False, - attribute_name=None, use_nicks=False, + typeclass=None, location=None, - ignore_errors=False, - player=False): + attribute_name=None, + quiet=False, + exact=False): """ + Returns the typeclass of an Object matching a search string/condition + Perform a standard object search in the database, handling - multiple results and lack thereof gracefully. + multiple results and lack thereof gracefully. By default, only + objects in self's current location or inventory is searched. + Note: to find Players, use eg. ev.player_search. - ostring: (str) The string to match object names against. - Obs - To find a player, append * to the - start of ostring. - global_search(bool): Search all objects, not just the current - location/inventory - attribute_name (string) Which attribute to match - (if None, uses default 'name') - use_nicks (bool) : Use nickname replace (off by default) - location (Object): If None, use caller's current location - ignore_errors (bool): Don't display any error messages even - if there are none/multiple matches - - just return the result as a list. - player (Objectt): Don't search for an Object but a Player. - This will also find players that don't - currently have a character. + Inputs: - Returns - a unique Object/Player match or None. All error - messages are handled by system-commands and the parser-handlers - specified in settings. + ostring (str): Primary search criterion. Will be matched against object.key (with object.aliases second) + unless the keyword attribute_name specifies otherwise. Special strings: + # - search by unique dbref. This is always a global search. + me,self - self-reference to this object + - - can be used to differentiate between multiple same-named matches + global_search (bool): Search all objects globally. This is overruled by "location" keyword. + use_nicks (bool): Use nickname-replace (nicktype "object") on the search string + typeclass (str or Typeclass): Limit search only to Objects with this typeclass. May be a list of typeclasses + for a broader search. + location (Object): Specify a location to search, if different from the self's given location + plus its contents. This can also be a list of locations. + attribute_name (str): Use this named Attribute to match ostring against, instead of object.key. + quiet (bool) - don't display default error messages - return multiple matches as a list and + no matches as None. If not set (default), will echo error messages and return None. + exact (bool) - if unset (default) - prefers to match to beginning of string rather than not matching + at all. If set, requires exact mathing of entire string. - Use * to search for objects controlled by a specific - player. Note that the object controlled by the player will be - returned, not the player object itself. This also means that - this will not find Players without a character. Use the keyword - player=True to find player objects. + Returns: + + quiet=False (default): + no match or multimatch: + auto-echoes errors to self.msg, then returns None + (results are handled by modules set by settings.SEARCH_AT_RESULT + and settings.SEARCH_AT_MULTIMATCH_INPUT) + match: + a unique object match + quiet=True: + no match or multimatch: + returns None or list of multi-matches + match: + a unique object match - Note - for multiple matches, the engine accepts a number - linked to the key in order to separate the matches from - each other without showing the dbref explicitly. Default - syntax for this is 'N-searchword'. So for example, if there - are three objects in the room all named 'ball', you could - address the individual ball as '1-ball', '2-ball', '3-ball' - etc. """ return self.dbobj.search(ostring, - global_search=global_search, - global_dbref=global_dbref, - attribute_name=attribute_name, - use_nicks=use_nicks, - location=location, - ignore_errors=ignore_errors, - player=player) + global_search=global_search, + use_nicks=use_nicks, + typeclass=typeclass, + location=location, + attribute_name=attribute_name, + quiet=quiet, + exact=quiet) def execute_cmd(self, raw_string, sessid=None): """ diff --git a/src/players/manager.py b/src/players/manager.py index cdc22b8cf3..d7b7e2d840 100644 --- a/src/players/manager.py +++ b/src/players/manager.py @@ -160,7 +160,6 @@ class PlayerManager(TypedObjectManager): ostring = a string or database id. """ - ostring = ostring.lstrip("*") dbref = self.dbref(ostring) if dbref or dbref == 0: matches = self.filter(id=dbref) diff --git a/src/players/models.py b/src/players/models.py index d21921e320..96e509c433 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -39,8 +39,13 @@ from src.commands import cmdhandler from src.utils import logger, utils from src.utils.utils import inherits_from, make_iter +from django.utils.translation import ugettext as _ + __all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB") +_ME = _("me") +_SELF = _("self") + _SESSIONS = None _AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) _MULTISESSION_MODE = settings.MULTISESSION_MODE @@ -556,6 +561,10 @@ class PlayerDB(TypedObject): Extra keywords are ignored, but are allowed in call in order to make API more consistent with objects.models.TypedObject.search. """ + # handle me, self + if ostring in (_ME, _SELF, '*' + _ME, '*' + _SELF): + return self + matches = _GA(self, "__class__").objects.player_search(ostring) matches = _AT_SEARCH_RESULT(self, ostring, matches, global_search=True) if matches and return_character: