Make CmdTag prioritise local objects first. Resolve #2665.

This commit is contained in:
Griatch 2022-10-29 16:32:36 +02:00
parent d0f137845d
commit d3ebd3d642

View file

@ -2,33 +2,36 @@
Building and world design commands Building and world design commands
""" """
import re import re
from django.core.paginator import Paginator
from django.conf import settings from django.conf import settings
from django.db.models import Q, Min, Max from django.core.paginator import Paginator
from django.db.models import Max, Min, Q
from evennia import InterruptCommand from evennia import InterruptCommand
from evennia.scripts.models import ScriptDB
from evennia.objects.models import ObjectDB
from evennia.locks.lockhandler import LockException
from evennia.commands.cmdhandler import get_and_merge_cmdsets from evennia.commands.cmdhandler import get_and_merge_cmdsets
from evennia.utils import create, utils, search, logger, funcparser from evennia.locks.lockhandler import LockException
from evennia.objects.models import ObjectDB
from evennia.prototypes import menus as olc_menus
from evennia.prototypes import prototypes as protlib
from evennia.prototypes import spawner
from evennia.scripts.models import ScriptDB
from evennia.utils import create, funcparser, logger, search, utils
from evennia.utils.ansi import raw as ansi_raw
from evennia.utils.dbserialize import deserialize from evennia.utils.dbserialize import deserialize
from evennia.utils.utils import (
inherits_from,
class_from_module,
get_all_typeclasses,
variable_from_module,
dbref,
crop,
interactive,
list_to_string,
display_len,
format_grid,
)
from evennia.utils.eveditor import EvEditor from evennia.utils.eveditor import EvEditor
from evennia.utils.evmore import EvMore from evennia.utils.evmore import EvMore
from evennia.utils.evtable import EvTable from evennia.utils.evtable import EvTable
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus from evennia.utils.utils import (
from evennia.utils.ansi import raw as ansi_raw class_from_module,
crop,
dbref,
display_len,
format_grid,
get_all_typeclasses,
inherits_from,
interactive,
list_to_string,
variable_from_module,
)
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -350,7 +353,9 @@ class CmdCopy(ObjManipCommand):
new_aliases=to_obj_aliases, new_aliases=to_obj_aliases,
) )
if copiedobj: if copiedobj:
string = f"Copied {from_obj_name} to '{to_obj_name}' (aliases: {to_obj_aliases})." string = (
f"Copied {from_obj_name} to '{to_obj_name}' (aliases: {to_obj_aliases})."
)
else: else:
string = f"There was an error copying {from_obj_name} to '{to_obj_name}'." string = f"There was an error copying {from_obj_name} to '{to_obj_name}'."
# we are done, echo to user # we are done, echo to user
@ -491,11 +496,13 @@ class CmdCpAttr(ObjManipCommand):
if clear and not (from_obj == to_obj and from_attr == to_attr): if clear and not (from_obj == to_obj and from_attr == to_attr):
from_obj.attributes.remove(from_attr) from_obj.attributes.remove(from_attr)
result.append( result.append(
f"\nMoved {from_obj.name}.{from_attr} -> {to_obj_name}.{to_attr}. (value: {repr(value)})" f"\nMoved {from_obj.name}.{from_attr} -> {to_obj_name}.{to_attr}. (value:"
f" {repr(value)})"
) )
else: else:
result.append( result.append(
f"\nCopied {from_obj.name}.{from_attr} -> {to_obj.name}.{to_attr}. (value: {repr(value)})" f"\nCopied {from_obj.name}.{from_attr} -> {to_obj.name}.{to_attr}. (value:"
f" {repr(value)})"
) )
caller.msg("".join(result)) caller.msg("".join(result))
@ -609,7 +616,9 @@ class CmdCreate(ObjManipCommand):
if not obj: if not obj:
continue continue
if aliases: if aliases:
string = f"You create a new {obj.typename}: {obj.name} (aliases: {', '.join(aliases)})." string = (
f"You create a new {obj.typename}: {obj.name} (aliases: {', '.join(aliases)})."
)
else: else:
string = f"You create a new {obj.typename}: {obj.name}." string = f"You create a new {obj.typename}: {obj.name}."
# set a default desc # set a default desc
@ -770,7 +779,8 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
return f"\nYou don't have permission to delete {objname}." return f"\nYou don't have permission to delete {objname}."
if obj.account and "override" not in self.switches: if obj.account and "override" not in self.switches:
return ( return (
f"\nObject {objname} is controlled by an active account. Use /override to delete anyway." f"\nObject {objname} is controlled by an active account. Use /override to"
" delete anyway."
) )
if obj.dbid == int(settings.DEFAULT_HOME.lstrip("#")): if obj.dbid == int(settings.DEFAULT_HOME.lstrip("#")):
return ( return (
@ -899,7 +909,7 @@ class CmdDig(ObjManipCommand):
caller = self.caller caller = self.caller
if not self.lhs: if not self.lhs:
string = "Usage: dig[/teleport] <roomname>[;alias;alias...]" "[:parent] [= <exit_there>" string = "Usage: dig[/teleport] <roomname>[;alias;alias...][:parent] [= <exit_there>"
string += "[;alias;alias..][:parent]] " string += "[;alias;alias..][:parent]] "
string += "[, <exit_back_here>[;alias;alias..][:parent]]" string += "[, <exit_back_here>[;alias;alias..][:parent]]"
caller.msg(string) caller.msg(string)
@ -926,7 +936,9 @@ class CmdDig(ObjManipCommand):
alias_string = "" alias_string = ""
if new_room.aliases.all(): if new_room.aliases.all():
alias_string = " (%s)" % ", ".join(new_room.aliases.all()) alias_string = " (%s)" % ", ".join(new_room.aliases.all())
room_string = f"Created room {new_room}({new_room.dbref}){alias_string} of type {typeclass}." room_string = (
f"Created room {new_room}({new_room.dbref}){alias_string} of type {typeclass}."
)
# create exit to room # create exit to room
@ -957,7 +969,10 @@ class CmdDig(ObjManipCommand):
alias_string = "" alias_string = ""
if new_to_exit.aliases.all(): if new_to_exit.aliases.all():
alias_string = " (%s)" % ", ".join(new_to_exit.aliases.all()) alias_string = " (%s)" % ", ".join(new_to_exit.aliases.all())
exit_to_string = "\nCreated Exit from {location.name} to {new_room.name}: {new_to_exit}({new_to_exit.dbref}){alias_string}." exit_to_string = (
"\nCreated Exit from {location.name} to {new_room.name}:"
" {new_to_exit}({new_to_exit.dbref}){alias_string}."
)
# Create exit back from new room # Create exit back from new room
@ -984,7 +999,10 @@ class CmdDig(ObjManipCommand):
alias_string = "" alias_string = ""
if new_back_exit.aliases.all(): if new_back_exit.aliases.all():
alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all()) alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all())
exit_back_string = f"\nCreated Exit back from {new_room.name} to {location.name}: {new_back_exit}({new_back_exit.dbref}){alias_string}." exit_back_string = (
f"\nCreated Exit back from {new_room.name} to {location.name}:"
f" {new_back_exit}({new_back_exit.dbref}){alias_string}."
)
caller.msg(f"{room_string}{exit_to_string}{exit_back_string}") caller.msg(f"{room_string}{exit_to_string}{exit_back_string}")
if new_room and "teleport" in self.switches: if new_room and "teleport" in self.switches:
caller.move_to(new_room, move_type="teleport") caller.move_to(new_room, move_type="teleport")
@ -1148,12 +1166,17 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
return return
string = "" string = ""
note = "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing." note = (
"Note: %s(%s) did not have a destination set before. Make sure you linked the right"
" thing."
)
if not obj.destination: if not obj.destination:
string = note % (obj.name, obj.dbref) string = note % (obj.name, obj.dbref)
if "twoway" in self.switches: if "twoway" in self.switches:
if not (target.location and obj.location): if not (target.location and obj.location):
string = f"To create a two-way link, {obj} and {target} must both have a location" string = (
f"To create a two-way link, {obj} and {target} must both have a location"
)
string += " (i.e. they cannot be rooms, but should be exits)." string += " (i.e. they cannot be rooms, but should be exits)."
self.caller.msg(string) self.caller.msg(string)
return return
@ -1161,7 +1184,10 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
string += note % (target.name, target.dbref) string += note % (target.name, target.dbref)
obj.destination = target.location obj.destination = target.location
target.destination = obj.location target.destination = obj.location
string += f"\nLink created {obj.name} (in {obj.location}) <-> {target.name} (in {target.location}) (two-way)." string += (
f"\nLink created {obj.name} (in {obj.location}) <-> {target.name} (in"
f" {target.location}) (two-way)."
)
else: else:
obj.destination = target obj.destination = target
string += f"\nLink created {obj.name} -> {target} (one way)." string += f"\nLink created {obj.name} -> {target} (one way)."
@ -1269,7 +1295,10 @@ class CmdSetHome(CmdLink):
old_home = obj.home old_home = obj.home
obj.home = new_home obj.home = new_home
if old_home: if old_home:
string = f"Home location of {obj} was changed from {old_home}({old_home.dbref} to {new_home}({new_home.dbref})." string = (
f"Home location of {obj} was changed from {old_home}({old_home.dbref} to"
f" {new_home}({new_home.dbref})."
)
else: else:
string = f"Home location of {obj} was set to {new_home}({new_home.dbref})." string = f"Home location of {obj} was set to {new_home}({new_home.dbref})."
self.caller.msg(string) self.caller.msg(string)
@ -1372,7 +1401,7 @@ class CmdName(ObjManipCommand):
astring = "" astring = ""
if aliases: if aliases:
[obj.aliases.add(alias) for alias in aliases] [obj.aliases.add(alias) for alias in aliases]
astring = " (%s)" % (", ".join(aliases)) astring = " (%s)" % ", ".join(aliases)
# fix for exits - we need their exit-command to change name too # fix for exits - we need their exit-command to change name too
if obj.destination: if obj.destination:
obj.flush_from_cache(force=True) obj.flush_from_cache(force=True)
@ -1420,9 +1449,10 @@ class CmdOpen(ObjManipCommand):
exit_obj = exit_obj[0] exit_obj = exit_obj[0]
if not exit_obj.destination: if not exit_obj.destination:
# we are trying to link a non-exit # we are trying to link a non-exit
caller.msg(f"'{exit_name}' already exists and is not an exit!\nIf you want to convert it " caller.msg(
"to an exit, you must assign an object to the 'destination' property first." f"'{exit_name}' already exists and is not an exit!\nIf you want to convert it "
) "to an exit, you must assign an object to the 'destination' property first."
)
return None return None
# we are re-linking an old exit. # we are re-linking an old exit.
old_destination = exit_obj.destination old_destination = exit_obj.destination
@ -1433,7 +1463,10 @@ class CmdOpen(ObjManipCommand):
exit_obj.destination = destination exit_obj.destination = destination
if exit_aliases: if exit_aliases:
[exit_obj.aliases.add(alias) for alias in exit_aliases] [exit_obj.aliases.add(alias) for alias in exit_aliases]
string += f" Rerouted its old destination '{old_destination.name}' to '{destination.name}' and changed aliases." string += (
f" Rerouted its old destination '{old_destination.name}' to"
f" '{destination.name}' and changed aliases."
)
else: else:
string += " It already points to the correct place." string += " It already points to the correct place."
@ -1456,9 +1489,12 @@ class CmdOpen(ObjManipCommand):
string = ( string = (
"" ""
if not exit_aliases if not exit_aliases
else " (aliases: %s)" % (", ".join([str(e) for e in exit_aliases])) else " (aliases: %s)" % ", ".join([str(e) for e in exit_aliases])
)
string = (
f"Created new Exit '{exit_name}' from {location.name} to"
f" {destination.name}{string}."
) )
string = f"Created new Exit '{exit_name}' from {location.name} to {destination.name}{string}."
else: else:
string = f"Error: Exit '{exit.name}' not created." string = f"Error: Exit '{exit.name}' not created."
# emit results # emit results
@ -1543,8 +1579,7 @@ def _convert_from_string(cmd, strobj):
# treat as string # treat as string
strobj = utils.to_str(strobj) strobj = utils.to_str(strobj)
string = ( string = (
f'|RNote: name "|r{strobj}|R" was converted to a string. ' f'|RNote: name "|r{strobj}|R" was converted to a string. Make sure this is acceptable.'
"Make sure this is acceptable."
) )
cmd.caller.msg(string) cmd.caller.msg(string)
return strobj return strobj
@ -1882,7 +1917,7 @@ class CmdSetAttribute(ObjManipCommand):
return return
if len(attrs) > 1: if len(attrs) > 1:
caller.msg("The Line editor can only be applied " "to one attribute at a time.") caller.msg("The Line editor can only be applied to one attribute at a time.")
return return
if not attrs: if not attrs:
caller.msg( caller.msg(
@ -1954,7 +1989,8 @@ class CmdSetAttribute(ObjManipCommand):
# check if anything was done # check if anything was done
if not result: if not result:
caller.msg( caller.msg(
"No valid attributes were found. Usage: set obj/attr[:category] = value. Use empty value to clear." "No valid attributes were found. Usage: set obj/attr[:category] = value. Use empty"
" value to clear."
) )
else: else:
# send feedback # send feedback
@ -2123,7 +2159,8 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if not obj: if not obj:
return return
caller.msg( caller.msg(
f"{obj.name}'s current typeclass is '{obj.__class__.__module__}.{obj.__class__.__name__}'" f"{obj.name}'s current typeclass is"
f" '{obj.__class__.__module__}.{obj.__class__.__name__}'"
) )
return return
@ -2181,8 +2218,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
is_same = obj.is_typeclass(new_typeclass, exact=True) is_same = obj.is_typeclass(new_typeclass, exact=True)
if is_same and "force" not in self.switches: if is_same and "force" not in self.switches:
string = ( string = (
f"{obj.name} already has the typeclass '{new_typeclass}'. " f"{obj.name} already has the typeclass '{new_typeclass}'. Use /force to override."
"Use /force to override."
) )
else: else:
reset = "reset" in self.switches reset = "reset" in self.switches
@ -2193,11 +2229,10 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if reset: if reset:
answer = yield ( answer = yield (
"|yNote that this will reset the object back to its typeclass' default state, " "|yNote that this will reset the object back to its typeclass' default state,"
"removing any custom locks/perms/attributes etc that may have been added " " removing any custom locks/perms/attributes etc that may have been added by an"
"by an explicit create_object call. Use `update` or type/force instead in order " " explicit create_object call. Use `update` or type/force instead in order to"
"to keep such data. " " keep such data. Continue [Y]/N?|n"
"Continue [Y]/N?|n"
) )
if answer.upper() in ("N", "NO"): if answer.upper() in ("N", "NO"):
caller.msg("Aborted.") caller.msg("Aborted.")
@ -2209,10 +2244,14 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
diff, _ = spawner.prototype_diff_from_object(prototype, obj) diff, _ = spawner.prototype_diff_from_object(prototype, obj)
txt = spawner.format_diff(diff) txt = spawner.format_diff(diff)
prompt = ( prompt = (
f"Applying prototype '{prototype['key']}' over '{obj.name}' will cause the follow changes:\n{txt}\n" f"Applying prototype '{prototype['key']}' over '{obj.name}' will cause the"
f" follow changes:\n{txt}\n"
) )
if not reset: if not reset:
prompt += "\n|yWARNING:|n Use the /reset switch to apply the prototype over a blank state." prompt += (
"\n|yWARNING:|n Use the /reset switch to apply the prototype over a blank"
" state."
)
prompt += "\nAre you sure you want to apply these changes [yes]/no?" prompt += "\nAre you sure you want to apply these changes [yes]/no?"
answer = yield (prompt) answer = yield (prompt)
if answer and answer in ("no", "n"): if answer and answer in ("no", "n"):
@ -2235,7 +2274,10 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if is_same: if is_same:
string = f"{obj.name} updated its existing typeclass ({obj.path}).\n" string = f"{obj.name} updated its existing typeclass ({obj.path}).\n"
else: else:
string = f"{obj.name} changed typeclass from {old_typeclass_path} to {obj.typeclass_path}.\n" string = (
f"{obj.name} changed typeclass from {old_typeclass_path} to"
f" {obj.typeclass_path}.\n"
)
if update: if update:
string += "Only the at_object_creation hook was run (update mode)." string += "Only the at_object_creation hook was run (update mode)."
else: else:
@ -2243,10 +2285,14 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if reset: if reset:
string += " All old attributes where deleted before the swap." string += " All old attributes where deleted before the swap."
else: else:
string += " Attributes set before swap were not removed\n(use `swap` or `type/reset` to clear all)." string += (
" Attributes set before swap were not removed\n(use `swap` or `type/reset` to"
" clear all)."
)
if "prototype" in self.switches and prototype_success: if "prototype" in self.switches and prototype_success:
string += ( string += (
f" Prototype '{prototype['key']}' was successfully applied over the object type." f" Prototype '{prototype['key']}' was successfully applied over the object"
" type."
) )
caller.msg(string) caller.msg(string)
@ -2345,9 +2391,7 @@ class CmdLock(ObjManipCommand):
caller = self.caller caller = self.caller
if not self.args: if not self.args:
string = ( string = "Usage: lock <object>[ = <lockstring>] or lock[/switch] <object>/<access_type>"
"Usage: lock <object>[ = <lockstring>] or lock[/switch] " "<object>/<access_type>"
)
caller.msg(string) caller.msg(string)
return return
@ -2391,7 +2435,7 @@ class CmdLock(ObjManipCommand):
caller.msg( caller.msg(
f"Switch(es) |w{swi}|n can not be used with a " f"Switch(es) |w{swi}|n can not be used with a "
"lock assignment. Use e.g. " "lock assignment. Use e.g. "
"|wlock/del objname/locktype|n instead." "|wlock/del objname/locktype|n instead."
) )
return return
@ -3038,7 +3082,8 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
low, high = sorted(qs.values()) low, high = sorted(qs.values())
if not (low and high): if not (low and high):
raise ValueError( raise ValueError(
f"{self.__class__.__name__}: Min and max ID not returned by aggregation; falling back to queryset slicing." f"{self.__class__.__name__}: Min and max ID not returned by aggregation;"
" falling back to queryset slicing."
) )
except Exception as e: except Exception as e:
logger.log_trace(e) logger.log_trace(e)
@ -3079,7 +3124,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
restrictions = "" restrictions = ""
if self.switches: if self.switches:
restrictions = ", %s" % (", ".join(self.switches)) restrictions = ", %s" % ", ".join(self.switches)
if is_dbref or is_account: if is_dbref or is_account:
if is_dbref: if is_dbref:
@ -3392,13 +3437,12 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
if new_script: if new_script:
caller.msg( caller.msg(
f"Global Script Created - " f"Global Script Created - {new_script.key} ({new_script.typeclass_path})"
f"{new_script.key} ({new_script.typeclass_path})"
) )
ScriptEvMore(caller, [new_script], session=self.session) ScriptEvMore(caller, [new_script], session=self.session)
else: else:
caller.msg( caller.msg(
f"Global Script |rNOT|n Created |r(see log)|n - " f"arguments: {self.args}" f"Global Script |rNOT|n Created |r(see log)|n - arguments: {self.args}"
) )
elif scripts or obj: elif scripts or obj:
@ -3433,9 +3477,7 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
f"{script_key} ({script_typeclass_path})|n" f"{script_key} ({script_typeclass_path})|n"
) )
else: else:
msgs.append( msgs.append(f"{scripttype} {verb} - {script_key} ({script_typeclass_path})")
f"{scripttype} {verb} - " f"{script_key} ({script_typeclass_path})"
)
caller.msg("\n".join(msgs)) caller.msg("\n".join(msgs))
if "delete" not in self.switches: if "delete" not in self.switches:
if script and script.pk: if script and script.pk:
@ -3607,8 +3649,8 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
if obj_to_teleport.has_account: if obj_to_teleport.has_account:
caller.msg( caller.msg(
"Cannot teleport a puppeted object " f"Cannot teleport a puppeted object ({obj_to_teleport.key}, puppeted by"
f"({obj_to_teleport.key}, puppeted by {obj_to_teleport.account}) to a None-location." f" {obj_to_teleport.account}) to a None-location."
) )
return return
caller.msg(f"Teleported {obj_to_teleport} -> None-location.") caller.msg(f"Teleported {obj_to_teleport} -> None-location.")
@ -3648,7 +3690,7 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
# check any locks # check any locks
if not (caller.permissions.check("Admin") or obj_to_teleport.access(caller, "teleport")): if not (caller.permissions.check("Admin") or obj_to_teleport.access(caller, "teleport")):
caller.msg( caller.msg(
f"{obj_to_teleport} 'teleport'-lock blocks you from teleporting " "it anywhere." f"{obj_to_teleport} 'teleport'-lock blocks you from teleporting it anywhere."
) )
return return
@ -3657,7 +3699,7 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
or destination.access(obj_to_teleport, "teleport_here") or destination.access(obj_to_teleport, "teleport_here")
): ):
caller.msg( caller.msg(
f"{destination} 'teleport_here'-lock blocks {obj_to_teleport} from " "moving there." f"{destination} 'teleport_here'-lock blocks {obj_to_teleport} from moving there."
) )
return return
@ -3787,7 +3829,12 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
# no search/deletion # no search/deletion
if self.rhs: if self.rhs:
# = is found; command args are of the form obj = tag # = is found; command args are of the form obj = tag
obj = self.caller.search(self.lhs, global_search=True) # first search locally, then global
obj = self.caller.search(self.lhs, quiet=True)
if not obj:
obj = self.caller.search(self.lhs, global_search=True)
else:
obj = obj[0]
if not obj: if not obj:
return return
tag = self.rhs tag = self.rhs
@ -3804,7 +3851,12 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
self.caller.msg(string) self.caller.msg(string)
else: else:
# no = found - list tags on object # no = found - list tags on object
obj = self.caller.search(self.args, global_search=True) # first search locally, then global
obj = self.caller.search(self.args, quiet=True)
if not obj:
obj = self.caller.search(self.args, global_search=True)
else:
obj = obj[0]
if not obj: if not obj:
return return
tagtuples = obj.tags.all(return_key_and_category=True) tagtuples = obj.tags.all(return_key_and_category=True)
@ -3974,11 +4026,11 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
if not isinstance(prototype, expect): if not isinstance(prototype, expect):
if eval_err: if eval_err:
string = ( string = (
f"{inp}\n{eval_err}\n|RCritical Python syntax error in argument. Only primitive " f"{inp}\n{eval_err}\n|RCritical Python syntax error in argument. Only"
"Python structures are allowed. \nMake sure to use correct " " primitive Python structures are allowed. \nMake sure to use correct"
"Python syntax. Remember especially to put quotes around all " " Python syntax. Remember especially to put quotes around all strings"
"strings inside lists and dicts.|n For more advanced uses, embed " " inside lists and dicts.|n For more advanced uses, embed funcparser"
"funcparser callables ($funcs) in the strings." " callables ($funcs) in the strings."
) )
else: else:
string = f"Expected {expect}, got {type(prototype)}." string = f"Expected {expect}, got {type(prototype)}."
@ -3991,7 +4043,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
# TODO: Exec support is deprecated. Remove completely for 1.0. # TODO: Exec support is deprecated. Remove completely for 1.0.
if "exec" in prototype and not self.caller.check_permstring("Developer"): if "exec" in prototype and not self.caller.check_permstring("Developer"):
self.caller.msg( self.caller.msg(
"Spawn aborted: You are not allowed to " "use the 'exec' prototype key." "Spawn aborted: You are not allowed to use the 'exec' prototype key."
) )
return return
try: try:
@ -4176,7 +4228,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
# store a prototype to the database store # store a prototype to the database store
if not self.args: if not self.args:
caller.msg( caller.msg(
"Usage: spawn/save [<key>[;desc[;tag,tag[,...][;lockstring]]]] = <prototype_dict>" "Usage: spawn/save [<key>[;desc[;tag,tag[,...][;lockstring]]]] ="
" <prototype_dict>"
) )
return return
if self.rhs: if self.rhs:
@ -4336,4 +4389,4 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
# time we try to update objects with this prototype in the future. # time we try to update objects with this prototype in the future.
obj.location = caller.location obj.location = caller.location
except RuntimeError as err: except RuntimeError as err:
caller.msg(err) caller.msg(err)