Better handle using typeclass-cmd with mismatched db table. Resolve #2350.
This commit is contained in:
parent
e91671c053
commit
5ac69a7b0d
3 changed files with 71 additions and 9 deletions
|
|
@ -78,6 +78,8 @@ Up requirements to Django 3.2+
|
||||||
infinite recursion when wanting to set up Script to delete-on-stop.
|
infinite recursion when wanting to set up Script to delete-on-stop.
|
||||||
- Command executions now done on copies to make sure `yield` don't cause crossovers. Add
|
- Command executions now done on copies to make sure `yield` don't cause crossovers. Add
|
||||||
`Command.retain_instance` flag for reusing the same command instance.
|
`Command.retain_instance` flag for reusing the same command instance.
|
||||||
|
- The `typeclass` command will now correctly search the correct database-table for the target
|
||||||
|
obj (avoids mistakenly assigning an AccountDB-typeclass to a Character etc).
|
||||||
|
|
||||||
### Evennia 0.9.5 (2019-2020)
|
### Evennia 0.9.5 (2019-2020)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1914,7 +1914,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
|
||||||
typeclass[/switch] <object> [= typeclass.path]
|
typeclass[/switch] <object> [= typeclass.path]
|
||||||
typeclass/prototype <object> = prototype_key
|
typeclass/prototype <object> = prototype_key
|
||||||
|
|
||||||
typeclass/list/show [typeclass.path]
|
typeclasses or typeclass/list/show [typeclass.path]
|
||||||
swap - this is a shorthand for using /force/reset flags.
|
swap - this is a shorthand for using /force/reset flags.
|
||||||
update - this is a shorthand for using the /force/reload flag.
|
update - this is a shorthand for using the /force/reload flag.
|
||||||
|
|
||||||
|
|
@ -1956,17 +1956,63 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "typeclass"
|
key = "typeclass"
|
||||||
aliases = ["type", "parent", "swap", "update"]
|
aliases = ["type", "parent", "swap", "update", "typeclasses"]
|
||||||
switch_options = ("show", "examine", "update", "reset", "force", "list", "prototype")
|
switch_options = ("show", "examine", "update", "reset", "force", "list", "prototype")
|
||||||
locks = "cmd:perm(typeclass) or perm(Builder)"
|
locks = "cmd:perm(typeclass) or perm(Builder)"
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
||||||
|
def _generic_search(self, query, typeclass_path):
|
||||||
|
|
||||||
|
caller = self.caller
|
||||||
|
if typeclass_path:
|
||||||
|
# make sure we search the right database table
|
||||||
|
try:
|
||||||
|
new_typeclass = class_from_module(typeclass_path)
|
||||||
|
except ImportError:
|
||||||
|
# this could be a prototype and not a typeclass at all
|
||||||
|
return caller.search(query)
|
||||||
|
|
||||||
|
dbclass = new_typeclass.__dbclass__
|
||||||
|
|
||||||
|
if caller.__dbclass__ == dbclass:
|
||||||
|
# object or account match
|
||||||
|
obj = caller.search(query)
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
elif (self.account and self.account.__dbclass__ == dbclass):
|
||||||
|
# applying account while caller is object
|
||||||
|
caller.msg(f"Trying to search {new_typeclass} with query '{self.lhs}'.")
|
||||||
|
obj = self.account.search(query)
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
elif hasattr(caller, "puppet") and caller.puppet.__dbclass__ == dbclass:
|
||||||
|
# applying object while caller is account
|
||||||
|
caller.msg(f"Trying to search {new_typeclass} with query '{self.lhs}'.")
|
||||||
|
obj = caller.puppet.search(query)
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# other mismatch between caller and specified typeclass
|
||||||
|
caller.msg(f"Trying to search {new_typeclass} with query '{self.lhs}'.")
|
||||||
|
obj = new_typeclass.search(query)
|
||||||
|
if not obj:
|
||||||
|
if isinstance(obj, list):
|
||||||
|
caller.msg(f"Could not find {new_typeclass} with query '{self.lhs}'.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# no rhs, use caller's typeclass
|
||||||
|
obj = caller.search(query)
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"""Implements command"""
|
"""Implements command"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
if "list" in self.switches:
|
if "list" in self.switches or self.cmdname == 'typeclasses':
|
||||||
tclasses = get_all_typeclasses()
|
tclasses = get_all_typeclasses()
|
||||||
contribs = [key for key in sorted(tclasses) if key.startswith("evennia.contrib")] or [
|
contribs = [key for key in sorted(tclasses) if key.startswith("evennia.contrib")] or [
|
||||||
"<None loaded>"
|
"<None loaded>"
|
||||||
|
|
@ -2029,8 +2075,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# get object to swap on
|
obj = self._generic_search(self.lhs, self.rhs)
|
||||||
obj = caller.search(self.lhs)
|
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -2084,10 +2129,8 @@ 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 = "%s already has the typeclass '%s'. Use /force to override." % (
|
string = (f"{obj.name} already has the typeclass '{new_typeclass}'. "
|
||||||
obj.name,
|
"Use /force to override.")
|
||||||
new_typeclass,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
update = "update" in self.switches
|
update = "update" in self.switches
|
||||||
reset = "reset" in self.switches
|
reset = "reset" in self.switches
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,23 @@ class TypedObject(SharedMemoryModel):
|
||||||
# Object manipulation methods
|
# Object manipulation methods
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def search(cls, query, **kwargs):
|
||||||
|
"""
|
||||||
|
Overridden by class children. This implements a common API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query (str): A search query.
|
||||||
|
**kwargs: Other search parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of 0, 1 or more matches, only of this typeclass.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if cls.objects.dbref(query):
|
||||||
|
return [cls.objects.get_id(query)]
|
||||||
|
return list(cls.objects.filter(db_key__lower=query))
|
||||||
|
|
||||||
def is_typeclass(self, typeclass, exact=False):
|
def is_typeclass(self, typeclass, exact=False):
|
||||||
"""
|
"""
|
||||||
Returns true if this object has this type OR has a typeclass
|
Returns true if this object has this type OR has a typeclass
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue