From a6ae6e936ab11d8292dc6ed1021c55a3268bed91 Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 20 Oct 2009 22:21:01 +0000 Subject: [PATCH] Patched the batch-processor's interactive mode so it will not abort if it processes an object/script parent that changes the player's state. Also added a variable BATCH_IMPORT_PATH to config so one can keep all batch scripts in one location and don't have to write the full path to get them. Default is the new directory game/gamesrc/world. Added the permission genperms.admin_nostate so that builders can avoid entering a state when working on a room with a state-changing parent. Superusers have to set the flag ADMIN_NOSTATE on themselves to achieve the same effect (this is necessary since superusers always have all permissions, so they would otherwise never be able to enter states). /Griatch --- .../examples/batch_example.ev} | 0 src/commands/batchprocess.py | 25 +++++++---- src/config_defaults.py | 11 +++-- src/objects/models.py | 43 ++++++++++++++++--- 4 files changed, 63 insertions(+), 16 deletions(-) rename game/gamesrc/{commands/examples/batch_command_example.ev => world/examples/batch_example.ev} (100%) diff --git a/game/gamesrc/commands/examples/batch_command_example.ev b/game/gamesrc/world/examples/batch_example.ev similarity index 100% rename from game/gamesrc/commands/examples/batch_command_example.ev rename to game/gamesrc/world/examples/batch_example.ev diff --git a/src/commands/batchprocess.py b/src/commands/batchprocess.py index 25a05b3d6..a6055007e 100644 --- a/src/commands/batchprocess.py +++ b/src/commands/batchprocess.py @@ -48,6 +48,7 @@ An example batch file is found in game/gamesrc/commands/examples. """ import os import re +from django.conf import settings from src import logger from src import defines_global from src.cmdtable import GLOBAL_CMD_TABLE @@ -55,7 +56,7 @@ from src.statetable import GLOBAL_STATE_TABLE #global defines for storage -STATENAME="interactive batch processor" +STATENAME="_interactive batch processor" CMDSTACKS={} # user:cmdstack pairs (for interactive) STACKPTRS={} # user:stackpointer pairs (for interactive) FILENAMES={} # user:filename pairs (for interactive/reload) @@ -69,8 +70,10 @@ cnorm = r"%cn" def read_batchbuild_file(filename): """ This reads the contents of batchfile. - """ - filename = os.path.abspath(filename) + Filename is considered to be the name of the batch file + relative the directory specified in settings.py + """ + filename = os.path.abspath("%s/%s" % (settings.BATCH_IMPORT_PATH, filename)) try: f = open(filename) except IOError: @@ -191,12 +194,15 @@ def cmd_batchprocess(command): #parse indata file commands = parse_batchbuild_file(filename) if not commands: - source_object.emit_to("'%s'\ncould not be found. Remember that you have to supply the absolute path to the file." % filename) + source_object.emit_to("'%s' not found.\nYou have to supply the real path to the file relative to \nyour batch-file directory (e.g. game/gamesrc/world)." % filename) return switches = command.command_switches if switches and switches[0] in ['inter','interactive']: - #allow more control over how batch file is executed - source_object.set_state(STATENAME) + # allow more control over how batch file is executed + if not source_object.set_state(STATENAME): + source_object.emit_to("You cannot use the interactive mode while you have the flag ADMIN_NOSTATE set.") + return + CMDSTACKS[source_object] = commands STACKPTRS[source_object] = 0 FILENAMES[source_object] = filename @@ -288,7 +294,10 @@ def exit_state(source_object): del FILENAMES[source_object] except KeyError: logger.log_errmsg("Batchprocessor quit error: all state vars could not be deleted.") - source_object.clear_state() + # since clear_state() is protected against exiting the interactive mode + # (to avoid accidental drop-outs by rooms clearing a player's state), + # we have to clear the state directly here. + source_object.state = None def cmd_state_ll(command): """ @@ -517,7 +526,7 @@ def cmd_state_hh(command): #create the state; we want it as open as possible so we can do everything # in our batch processing. GLOBAL_STATE_TABLE.add_state(STATENAME,global_cmds='all', - allow_exits=True,allow_obj_cmds=True) + allow_exits=True,allow_obj_cmds=True,exit_command=True) #add state commands GLOBAL_STATE_TABLE.add_command(STATENAME,"nn",cmd_state_nn) GLOBAL_STATE_TABLE.add_command(STATENAME,"nl",cmd_state_nl) diff --git a/src/config_defaults.py b/src/config_defaults.py index 6427d0780..a4cbbc4e5 100644 --- a/src/config_defaults.py +++ b/src/config_defaults.py @@ -44,7 +44,7 @@ SRC_DIR = os.path.join(BASE_PATH, 'src') # Example: "/home/media/media.lawrence.com" MEDIA_ROOT = os.path.join(GAME_DIR, 'web', 'media') -# Import style path to the script parent module. Must be in the import path. +# Import style path to the directory holding script parents. Must be in the import path. SCRIPT_IMPORT_PATH = 'game.gamesrc.parents' # Default parent associated with non-player objects. This starts from where # the SCRIPT_IMPORT_PATH left off. @@ -52,6 +52,10 @@ SCRIPT_DEFAULT_OBJECT = 'base.basicobject' # Default parent associated with player objects. This starts from where # the SCRIPT_IMPORT_PATH left off. SCRIPT_DEFAULT_PLAYER = 'base.basicplayer' +# Real path to a directory to be searched for batch scripts for the +# batch processor. Specify relative evennia's 'game' directory. +BATCH_IMPORT_PATH = 'gamesrc/world' + # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3', and 'oracle'. DATABASE_ENGINE = 'sqlite3' @@ -125,9 +129,10 @@ PERM_GENPERMS = ( ("announce", "May make announcements to everyone."), ("admin_perm", "Can modify individual permissions."), ("admin_group", "Can manage membership in groups."), - ("process_control", "May shutdown/restart/reload the game"), + ("process_control", "May shutdown/restart/reload the game."), ("manage_players", "Can change passwords, siteban, etc."), - ("game_info", "Can review game metadata"),) + ("game_info", "Can review game metadata."), + ("admin_nostate", "Do not enter states (should be set only temporarily)."),) ## These permissions are not yet used in the default engine. ## ("boot", "May use @boot to kick players"), diff --git a/src/objects/models.py b/src/objects/models.py index 19f389fd9..5ba05a3ce 100755 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -1090,13 +1090,46 @@ class Object(models.Model): """ Only allow setting a state on a player object, otherwise fail silently. - """ - if self.is_player(): - self.state = state_name + This command safeguards the batch processor against dropping + out of interactive mode; it also allows builders to + sidestep room-based states when building (the genperm.admin_nostate + permission is not set on anyone by default, set it temporarily + when building a state-based room). + """ + if not self.is_player(): + return False + + if self.is_superuser(): + # we have to deal with superusers separately since + # they would always appear to have the genperm.admin_nostate + # permission. Instead we expect them to set the flag + # ADMIN_NOSTATE on themselves if they don't want to + # enter states. + nostate = self.has_flag("admin_nostate") + else: + # for other users we request the permission as normal. + nostate = self.has_perm("genperms.admin_nostate") + + # we never enter other states if we are in the interactive batch processor. + nostate = nostate or self.state == "_interactive batch processor" + + if nostate: + return False + self.state = state_name + return True + + def clear_state(self): - "Set to no state (return to normal operation)" - self.state = None + """ + Set to no state (return to normal operation) + + This safeguards the batch processor from exiting its + interactive mode when entering a room cancelling states. + (batch processor clears the state directly instead) + """ + if not self.state == "_interactive batch processor": + self.state = None def purge_object(self): "Completely clears all aspects of the object."