From df29defbcd3d102e2ca0959b02a5bb856e6fc274 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 20 Dec 2009 20:51:26 +0000 Subject: [PATCH] Added gamesrc/utils.py as a convenient shorthand for often-used methods from the engine Added a @deluser command and gave it and @boot an option to give a reason for booting/deleting a player Fixed a bug in @dig that confused exit directions in text Small bug fixes /Griatch --- game/gamesrc/commands/examples/misc_tests.py | 1 + game/gamesrc/utils.py | 123 +++++++++++++++++++ src/commands/objmanip.py | 2 +- src/commands/privileged.py | 76 ++++++++++-- src/comsys.py | 3 +- src/objects/managers/object.py | 6 +- src/objects/models.py | 52 +++++--- src/script_parents/basicobject.py | 4 +- 8 files changed, 239 insertions(+), 28 deletions(-) create mode 100644 game/gamesrc/utils.py diff --git a/game/gamesrc/commands/examples/misc_tests.py b/game/gamesrc/commands/examples/misc_tests.py index 6fef0152b8..a9e154b266 100644 --- a/game/gamesrc/commands/examples/misc_tests.py +++ b/game/gamesrc/commands/examples/misc_tests.py @@ -11,6 +11,7 @@ as 'game.gamesrc.commands.examples.misc_tests'. None of these commands are auto-added to the help database (they have no docstrings) in order to help make it clean. """ + from src.cmdtable import GLOBAL_CMD_TABLE #------------------------------------------------------------ diff --git a/game/gamesrc/utils.py b/game/gamesrc/utils.py new file mode 100644 index 0000000000..6cbbeefe55 --- /dev/null +++ b/game/gamesrc/utils.py @@ -0,0 +1,123 @@ +""" +This module offers a collection of useful general functions from the +game engine to make things easier to find. +Just import game.gamesrc.utils and refer to the globals defined herein. + +Note that this is not intended as a comprehensive collection, merely +a convenient place to refer to for the methods we have found to be +often used. You will still have to refer to the modules +in evennia/src for more specialized operations. + +You will also want to be well familiar with all the facilities each +object offers. The object model is defined in src/objects/models.py. +""" +#------------------------------------------------------------ +# imports +#------------------------------------------------------------ + +from django.conf import settings as in_settings +from src import logger +from src import scheduler as in_scheduler +from src.objects.models import Object +from src import defines_global +from src.cmdtable import GLOBAL_CMD_TABLE as in_GLOBAL_CMD_TABLE +from src.statetable import GLOBAL_STATE_TABLE as in_GLOBAL_STATE_TABLE +from src.events import IntervalEvent as in_IntervalEvent + +#------------------------------------------------------------ +# Import targets +#------------------------------------------------------------ + +settings = in_settings +GLOBAL_CMD_TABLE = in_GLOBAL_CMD_TABLE +GLOBAL_STATE_TABLE = in_GLOBAL_STATE_TABLE + +# Events +scheduler = in_scheduler +IntervalEvent = in_IntervalEvent + + +#------------------------------------------------------------ +# Log to file/stdio +# log_xxxmsg(msg) +#------------------------------------------------------------ + +log_errmsg = logger.log_errmsg +log_warnmsg = logger.log_warnmsg +log_infomsg = logger.log_infomsg + + +#------------------------------------------------------------ +# Search methods +#------------------------------------------------------------ + +# NOTE: All objects also has search_for_object() defined +# directly on themselves, which is a convenient entryway into a +# local and global search with automatic feedback to the +# calling player. + +# def get_object_from_dbref(dbref): +# Returns an object when given a dbref. +get_object_from_dbref = Object.objects.get_object_from_dbref + +# def dbref_search(dbref_string, limit_types=False): +# Searches for a given dbref. +dbref_search = Object.objects.dbref_search + +# def global_object_name_search(ostring, exact_match=True, limit_types=[]): +# Searches through all objects for a name match. +global_object_name_search = Object.objects.global_object_name_search + +# def global_object_script_parent_search(script_parent): +# Searches through all objects returning those which has a certain script parent. +global_object_script_parent_search = Object.objects.global_object_script_parent_search + +# def player_name_search(searcher, ostring): +# Search players by name. +player_name_search = Object.objects.player_name_search + +# def local_and_global_search(searcher, ostring, search_contents=True, +# search_location=True, dbref_only=False, +# limit_types=False, attribute_name=None): +# Searches an object's location then globally for a dbref or name match. +local_and_global_search = Object.objects.local_and_global_search + + +#------------------------------------------------------------ +# Creation commands +#------------------------------------------------------------ + +# def create_object(name, otype, location, owner, home=None, script_parent=None): +# Create a new object +create_object = Object.objects.create_object + +# def copy_object(original_object, new_name=None, new_location=None, reset=False): +# Create and return a new object as a copy of the source object. All will +# be identical to the original except for the dbref. Does not allow the +# copying of Player objects. +copy_object = Object.objects.copy_object + + +#------------------------------------------------------------ +# Validation +#------------------------------------------------------------ + +# NOTE: The easiest way to check if an object +# is of a particular type is to use each object's +# is_X() function, like is_superuser(), is_thing(), +# is_room(), is_player(), is_exit() and get_type(). + +OTYPE_NOTHING = defines_global.OTYPE_NOTHING +OTYPE_PLAYER = defines_global.OTYPE_PLAYER +OTYPE_ROOM = defines_global.OTYPE_ROOM +OTYPE_THING = defines_global.OTYPE_THING +OTYPE_EXIT = defines_global.OTYPE_EXIT +OTYPE_GOING = defines_global.OTYPE_GOING +TYPE_GARBAGE = defines_global.OTYPE_GARBAGE + +NOPERMS_MSG = defines_global.NOPERMS_MSG +NOCONTROL_MSG = defines_global.NOCONTROL_MSG + +# def is_dbref(self, dbstring, require_pound=True): +# Is the input a well-formed dbref number? +is_dbref = Object.objects.is_dbref diff --git a/src/commands/objmanip.py b/src/commands/objmanip.py index a27a43c18d..b42be73823 100644 --- a/src/commands/objmanip.py +++ b/src/commands/objmanip.py @@ -1085,7 +1085,7 @@ def cmd_dig(command): else: ptext += " of default type (parent '%s' failed!)" % script_parent source_object.emit_to("Created exit%s back from %s to %s named '%s'." % \ - (ptext, destination, location, new_object)) + (ptext, location, destination, new_object)) if new_room and 'teleport' in switches: source_object.move_to(new_room) diff --git a/src/commands/privileged.py b/src/commands/privileged.py index 414ed498c6..2e94165957 100644 --- a/src/commands/privileged.py +++ b/src/commands/privileged.py @@ -3,7 +3,7 @@ This file contains commands that require special permissions to use. These are generally @-prefixed commands, but there are exceptions. """ -from django.contrib.auth.models import Permission, Group +from django.contrib.auth.models import Permission, Group, User from django.conf import settings from src.objects.models import Object from src import session_mgr @@ -79,9 +79,14 @@ def cmd_boot(command): @boot Usage - @boot + @boot[/switches] [: reason] + + Switches: + quiet - Silently boot without informing player + port - boot by port number instead of name or dbref - Boot a player object from the server. + Boot a player object from the server. If a reason is + supplied it will be echoed to the user unless /quiet is set. """ source_object = command.source_object switch_quiet = False @@ -96,23 +101,27 @@ def cmd_boot(command): switch_port = True if not command.command_argument: - source_object.emit_to("Who would you like to boot?") + source_object.emit_to("Usage: @boot[/switches] [:reason]") return else: + arg = command.command_argument + reason = "" + if ':' in arg: + arg, reason = [a.strip() for a in arg.split(':',1)] + boot_list = [] if switch_port: # Boot a particular port. sessions = session_mgr.get_session_list(True) for sess in sessions: # Find the session with the matching port number. - if sess.getClientAddress()[1] == int(command.command_argument): + if sess.getClientAddress()[1] == int(arg): boot_list.append(sess) # Match found, kill the loop and continue with booting. break else: # Grab the objects that match - objs = Object.objects.local_and_global_search(source_object, - command.command_argument) + objs = Object.objects.local_and_global_search(source_object, arg) if not objs: source_object.emit_to("No name or dbref match found for booting.") @@ -145,7 +154,10 @@ def cmd_boot(command): # Carry out the booting of the sessions in the boot list. for boot in boot_list: if not switch_quiet: - boot.msg("You have been disconnected by %s." % (source_object.name)) + msg = "You have been disconnected by %s." % (source_object.name) + if reason: + msg += "\n Reason given:\n '%s'" % reason + boot.msg(msg) boot.disconnectClient() session_mgr.remove_session(boot) return @@ -153,6 +165,54 @@ GLOBAL_CMD_TABLE.add_command("@boot", cmd_boot, priv_tuple=("genperms.manage_players",), help_category="Admin") + +def cmd_delplayer(command): + """ + delplayer - delete player from server + + Usage: + @delplayer [: reason] + + Completely deletes a user from the server database, + making their nick and e-mail again available. + """ + source_object = command.source_object + arg = command.command_argument + if not arg: + source_object.emit_to("Usage: @delplayer ") + return + + reason = "" + if ':' in arg: + arg, reason = [a.strip() for a in arg.split(':',1)] + + objs = Object.objects.local_and_global_search(source_object, arg) + if not objs: + source_object.emit_to("No player object matches found for '%s'." % arg) + return + pobj = objs[0] + if not source_object.controls_other(pobj): + if pobj.is_superuser(): + source_object.emit_to("You cannot delete a Superuser.") + return + else: + source_object.emit_to("You do not have permission to delete that player.") + return + # boot the player then delete + source_object.emit_to("Booting and informing player if currently online ...") + name = pobj.get_name() + msg = "\nYour account '%s' is being *permanently* deleted.\n" % name + if reason: + msg += " Reason given:\n '%s'" % reason + pobj.emit_to(msg) + source_object.execute_cmd("@boot %s" % arg) + pobj.delete() + source_object.emit_to("Player %s was successfully deleted." % name) +GLOBAL_CMD_TABLE.add_command("@delplayer", cmd_delplayer, + priv_tuple=("genperms.manage_players",), + help_category="Admin") + + def cmd_newpassword(command): """ @newpassword diff --git a/src/comsys.py b/src/comsys.py index d1e7704d9c..bfaddd7eec 100644 --- a/src/comsys.py +++ b/src/comsys.py @@ -131,7 +131,8 @@ def plr_set_channel_listening(session, alias, listening): alias: (str) The channel alias. listening: (bool) A True or False value to determine listening status. """ - membership = session.pobject.channel_membership_set.get(user_alias=alias) + + membership = session.get_pobject().channel_membership_set.get(user_alias=alias) membership.is_listening = listening membership.save() plr_get_cdict(session).get(alias)[1] = listening diff --git a/src/objects/managers/object.py b/src/objects/managers/object.py index 76259329b6..8f630e9492 100644 --- a/src/objects/managers/object.py +++ b/src/objects/managers/object.py @@ -64,8 +64,10 @@ class ObjectManager(models.Manager): """ Returns an object when given a dbref. """ - if len(dbref)>1 and dbref[0]=="#": - dbref = dbref[1:] + if type(dbref) == type(str()): + if len(dbref)>1 and dbref[0]=="#": + dbref = dbref[1:] + dbref = "%s" % dbref try: return self.get(id=dbref) except self.model.DoesNotExist: diff --git a/src/objects/models.py b/src/objects/models.py index 5f4dd078db..9d18d86fbb 100755 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -573,6 +573,11 @@ class Object(models.Model): # Delete the associated player object permanently. uobj = User.objects.filter(id=self.id) if len(uobj) > 0: + # clean out channel memberships + memberships = self.channel_membership_set.filter(listener=self) + for membership in memberships: + membership.delete() + # delete user uobj[0].delete() # Set the object to type GARBAGE. @@ -1045,16 +1050,27 @@ class Object(models.Model): self.emit_to(lock_desc) else: self.emit_to("That destination is blocked from you.") - return - - # Before the move, call eventual pre-commands. - if self.scriptlink.at_before_move(target) != None: - return + return + + source_location = self.location + owner = self.get_owner() + errtxt = "There was a bug in a move_to() scriptlink. Contact an admin.\n" + # Before the move, call eventual pre-commands. + try: + if self.scriptlink.at_before_move(target) != None: + return + except: + owner.emit_to("%s%s" % (errtxt, traceback.print_exc())) + return + if not quiet: #tell the old room we are leaving - self.scriptlink.announce_move_from(target) - source_location = self.location + try: + self.scriptlink.announce_move_from(target) + except: + owner.emit_to("%s%s" % (errtxt, traceback.print_exc())) + # Perform move self.location = target @@ -1062,15 +1078,23 @@ class Object(models.Model): if not quiet: # Tell the new room we are there. - self.scriptlink.announce_move_to(source_location) - + try: + self.scriptlink.announce_move_to(source_location) + except: + owner.emit_to("%s%s" % (errtxt, traceback.print_exc())) + # Execute eventual extra commands on this object after moving it - self.scriptlink.at_after_move() - + try: + self.scriptlink.at_after_move(source_location) + except: + owner.emit_to("%s%s" % (errtxt, traceback.print_exc())) # Perform eventual extra commands on the receiving location - target.scriptlink.at_obj_receive(self) - - if force_look and self.is_player(): + try: + target.scriptlink.at_obj_receive(self, source_location) + except: + owner.emit_to("%s%s" % (errtxt, traceback.print_exc())) + + if force_look and self.is_player(): self.execute_cmd('look') def dbref_match(self, oname): diff --git a/src/script_parents/basicobject.py b/src/script_parents/basicobject.py index 778e6442d6..c4194e667a 100644 --- a/src/script_parents/basicobject.py +++ b/src/script_parents/basicobject.py @@ -109,7 +109,7 @@ class EvenniaBasicObject(object): if loc.is_player(): loc.emit_to("%s is now in your inventory." % obj.get_name()) - def at_after_move(self): + def at_after_move(self, old_loc=None): """ This hook is called just after the object was successfully moved. No return values. @@ -127,7 +127,7 @@ class EvenniaBasicObject(object): #print "SCRIPT TEST: %s dropped %s." % (pobject, self.scripted_obj) pass - def at_obj_receive(self, object=None): + def at_obj_receive(self, object=None, old_loc=None): """ Called whenever an object is added to the contents of this object. """