Moved the new hook to ObjManipCommand

This commit is contained in:
Andrew Bastien 2023-11-04 17:40:52 -04:00
parent 0da7f962c2
commit 5278ecb730
2 changed files with 168 additions and 128 deletions

View file

@ -2,6 +2,7 @@
Building and world design commands Building and world design commands
""" """
import re import re
import typing
from django.conf import settings from django.conf import settings
from django.core.paginator import Paginator from django.core.paginator import Paginator
@ -110,6 +111,14 @@ class ObjManipCommand(COMMAND_DEFAULT_CLASS):
# OBS - this is just a parent - it's not intended to actually be # OBS - this is just a parent - it's not intended to actually be
# included in a commandset on its own! # included in a commandset on its own!
# used by get_object_typeclass as defaults.
default_typeclasses = {
"object": settings.BASE_OBJECT_TYPECLASS,
"character": settings.BASE_CHARACTER_TYPECLASS,
"room": settings.BASE_ROOM_TYPECLASS,
"exit": settings.BASE_EXIT_TYPECLASS,
}
def parse(self): def parse(self):
""" """
We need to expand the default parsing to get all We need to expand the default parsing to get all
@ -164,6 +173,48 @@ class ObjManipCommand(COMMAND_DEFAULT_CLASS):
self.lhs_objattr = obj_attrs[0] self.lhs_objattr = obj_attrs[0]
self.rhs_objattr = obj_attrs[1] self.rhs_objattr = obj_attrs[1]
def get_object_typeclass(
self, obj_type: str = "object", typeclass: str = None, method: str = "cmd_create", **kwargs
) -> tuple[typing.Optional["Builder"], list[str]]:
"""
This hook is called by build commands to determine which typeclass to use for a specific purpose. For instance,
when using dig, the system can use this to autodetect which kind of Room typeclass to use based on where the
builder is currently located.
Note: Although intended to be used with typeclasses, as long as this hook returns a class with a create method,
which accepts the same API as DefaultObject.create(), build commands and other places should take it.
Args:
obj_type (str, optional): The type of object that is being created. Defaults to "object". Evennia provides
"room", "exit", and "character" by default, but this can be extended.
typeclass (str, optional): The typeclass that was requested by the player. Defaults to None.
Can also be an actual class.
method (str, optional): The method that is calling this hook. Defaults to "cmd_create".
Others are "cmd_dig", "cmd_open", "cmd_tunnel", etc.
Returns:
results_tuple (tuple[Optional[Builder], list[str]]): A tuple containing the typeclass to use and a list of
errors. (which might be empty.)
"""
found_typeclass = typeclass or self.default_typeclasses.get(obj_type, None)
if not found_typeclass:
return None, [f"No typeclass found for object type '{obj_type}'."]
try:
type_class = (
class_from_module(found_typeclass)
if isinstance(found_typeclass, str)
else found_typeclass
)
except ImportError:
return None, [f"Typeclass '{found_typeclass}' could not be imported."]
if not hasattr(type_class, "create"):
return None, [f"Typeclass '{found_typeclass}' is not creatable."]
return type_class, []
class CmdSetObjAlias(COMMAND_DEFAULT_CLASS): class CmdSetObjAlias(COMMAND_DEFAULT_CLASS):
""" """
@ -194,6 +245,8 @@ class CmdSetObjAlias(COMMAND_DEFAULT_CLASS):
locks = "cmd:perm(setobjalias) or perm(Builder)" locks = "cmd:perm(setobjalias) or perm(Builder)"
help_category = "Building" help_category = "Building"
method_type = "cmd_create"
def func(self): def func(self):
"""Set the aliases.""" """Set the aliases."""
@ -597,19 +650,16 @@ class CmdCreate(ObjManipCommand):
name = objdef["name"] name = objdef["name"]
aliases = objdef["aliases"] aliases = objdef["aliases"]
obj_typeclass, errors = caller.get_object_typeclass(obj_type="object", typeclass=objdef["option"]) obj_typeclass, errors = self.get_object_typeclass(
obj_type="object", typeclass=objdef["option"]
)
if errors: if errors:
self.msg(errors) self.msg(errors)
if not obj_typeclass: if not obj_typeclass:
continue continue
obj, errors = obj_typeclass.create( obj, errors = obj_typeclass.create(
name, name, caller, home=caller, aliases=aliases, report_to=caller, caller=caller
caller,
home=caller,
aliases=aliases,
report_to=caller,
creator=caller
) )
if errors: if errors:
self.msg(errors) self.msg(errors)
@ -896,6 +946,8 @@ class CmdDig(ObjManipCommand):
locks = "cmd:perm(dig) or perm(Builder)" locks = "cmd:perm(dig) or perm(Builder)"
help_category = "Building" help_category = "Building"
method_type = "cmd_dig"
# lockstring of newly created rooms, for easy overloading. # lockstring of newly created rooms, for easy overloading.
# Will be formatted with the {id} of the creating object. # Will be formatted with the {id} of the creating object.
new_room_lockstring = ( new_room_lockstring = (
@ -924,7 +976,9 @@ class CmdDig(ObjManipCommand):
location = caller.location location = caller.location
# Create the new room # Create the new room
room_typeclass, errors = caller.get_object_typeclass(obj_type="room", typeclass=room["option"], method="dig") room_typeclass, errors = self.get_object_typeclass(
obj_type="room", typeclass=room["option"], method=self.method_type
)
if errors: if errors:
self.msg("|rError creating room:|n %s" % errors) self.msg("|rError creating room:|n %s" % errors)
if not room_typeclass: if not room_typeclass:
@ -932,7 +986,11 @@ class CmdDig(ObjManipCommand):
# create room # create room
new_room, errors = room_typeclass.create( new_room, errors = room_typeclass.create(
room["name"], aliases=room["aliases"], report_to=caller, creator=caller, method="dig" room["name"],
aliases=room["aliases"],
report_to=caller,
caller=caller,
method=self.method_type,
) )
if errors: if errors:
self.msg("|rError creating room:|n %s" % errors) self.msg("|rError creating room:|n %s" % errors)
@ -943,9 +1001,7 @@ class CmdDig(ObjManipCommand):
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 = ( room_string = f"Created room {new_room}({new_room.dbref}){alias_string} of type {new_room}."
f"Created room {new_room}({new_room.dbref}){alias_string} of type {new_room}."
)
# create exit to room # create exit to room
@ -960,8 +1016,9 @@ class CmdDig(ObjManipCommand):
exit_to_string = "\nYou cannot create an exit from a None-location." exit_to_string = "\nYou cannot create an exit from a None-location."
else: else:
# Build the exit to the new room from the current one # Build the exit to the new room from the current one
exit_typeclass, errors = caller.get_object_typeclass(obj_type="exit", typeclass=to_exit["option"], exit_typeclass, errors = self.get_object_typeclass(
method="dig") obj_type="exit", typeclass=to_exit["option"], method=self.method_type
)
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)
if not exit_typeclass: if not exit_typeclass:
@ -973,8 +1030,8 @@ class CmdDig(ObjManipCommand):
destination=new_room, destination=new_room,
aliases=to_exit["aliases"], aliases=to_exit["aliases"],
report_to=caller, report_to=caller,
creator=caller, caller=caller,
method="dig" method=self.method_type,
) )
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)
@ -999,8 +1056,9 @@ class CmdDig(ObjManipCommand):
elif not location: elif not location:
exit_back_string = "\nYou cannot create an exit back to a None-location." exit_back_string = "\nYou cannot create an exit back to a None-location."
else: else:
exit_typeclass, errors = caller.get_object_typeclass(obj_type="exit", typeclass=back_exit["option"], exit_typeclass, errors = self.get_object_typeclass(
method="dig") obj_type="exit", typeclass=back_exit["option"], method=self.method_type
)
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)
if not exit_typeclass: if not exit_typeclass:
@ -1011,8 +1069,8 @@ class CmdDig(ObjManipCommand):
destination=location, destination=location,
aliases=back_exit["aliases"], aliases=back_exit["aliases"],
report_to=caller, report_to=caller,
creator=caller, caller=caller,
method="dig" method=self.method_type,
) )
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)
@ -1063,6 +1121,8 @@ class CmdTunnel(COMMAND_DEFAULT_CLASS):
locks = "cmd: perm(tunnel) or perm(Builder)" locks = "cmd: perm(tunnel) or perm(Builder)"
help_category = "Building" help_category = "Building"
method_type = "cmd_tunnel"
# store the direction, full name and its opposite # store the direction, full name and its opposite
directions = { directions = {
"n": ("north", "s"), "n": ("north", "s"),
@ -1449,6 +1509,8 @@ class CmdOpen(ObjManipCommand):
locks = "cmd:perm(open) or perm(Builder)" locks = "cmd:perm(open) or perm(Builder)"
help_category = "Building" help_category = "Building"
method_type = "cmd_open"
new_obj_lockstring = "control:id({id}) or perm(Admin);delete:id({id}) or perm(Admin)" new_obj_lockstring = "control:id({id}) or perm(Admin);delete:id({id}) or perm(Admin)"
# a custom member method to chug out exits and do checks # a custom member method to chug out exits and do checks
@ -1495,7 +1557,9 @@ class CmdOpen(ObjManipCommand):
else: else:
# exit does not exist before. Create a new one. # exit does not exist before. Create a new one.
exit_typeclass, errors = caller.get_object_typeclass(obj_type="exit", typeclass=typeclass, method="open") exit_typeclass, errors = self.get_object_typeclass(
obj_type="exit", typeclass=typeclass, method=self.method_type
)
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)
if not exit_typeclass: if not exit_typeclass:
@ -1505,8 +1569,8 @@ class CmdOpen(ObjManipCommand):
location=location, location=location,
aliases=exit_aliases, aliases=exit_aliases,
report_to=caller, report_to=caller,
creator=caller, caller=caller,
method="open" method=self.method_type,
) )
if errors: if errors:
self.msg("|rError creating exit:|n %s" % errors) self.msg("|rError creating exit:|n %s" % errors)

View file

@ -222,14 +222,6 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
{exits}{characters}{things} {exits}{characters}{things}
{footer} {footer}
""" """
default_typeclasses = {
"object": settings.BASE_OBJECT_TYPECLASS,
"character": settings.BASE_CHARACTER_TYPECLASS,
"room": settings.BASE_ROOM_TYPECLASS,
"exit": settings.BASE_EXIT_TYPECLASS,
}
# on-object properties # on-object properties
@lazy_property @lazy_property
@ -1020,8 +1012,14 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
obj.move_to(home, move_type="teleport") obj.move_to(home, move_type="teleport")
@classmethod @classmethod
def create(cls, key: str, account: "DefaultAccount" = None, creator: "DefaultObject" = None, method: str = "create", def create(
**kwargs): cls,
key: str,
account: "DefaultAccount" = None,
caller: "DefaultObject" = None,
method: str = "create",
**kwargs,
):
""" """
Creates a basic object with default parameters, unless otherwise Creates a basic object with default parameters, unless otherwise
specified or extended. specified or extended.
@ -1034,7 +1032,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
Keyword Args: Keyword Args:
account (Account): Account to attribute this object to. account (Account): Account to attribute this object to.
creator (DefaultObject): The object which is creating this one. caller (DefaultObject): The object which is creating this one.
description (str): Brief description for this object. description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing). ip (str): IP address of creator (for object auditing).
method (str): The method of creation. Defaults to "create". method (str): The method of creation. Defaults to "create".
@ -1610,43 +1608,6 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
""" """
pass pass
def get_object_typeclass(self, obj_type: str = "object", typeclass: str = None, method: str = "create", **kwargs) -> tuple[
typing.Optional["Builder"], list[str]]:
"""
This hook is called by build commands to determine which typeclass to use for a specific purpose. For instance,
when using dig, the system can use this to autodetect which kind of Room typeclass to use based on where the
builder is currently located.
Note: Although intended to be used with typeclasses, as long as this hook returns a class with a create method,
which accepts the same API as DefaultObject.create(), build commands and other places should take it.
Args:
obj_type (str, optional): The type of object that is being created. Defaults to "object". Evennia provides
"room", "exit", and "character" by default, but this can be extended.
typeclass (str, optional): The typeclass that was requested by the player. Defaults to None.
Can also be an actual class.
method (str, optional): The method that is calling this hook. Defaults to "create". Others are "dig", "open",
"tunnel", etc.
Returns:
results_tuple (tuple[Optional[Builder], list[str]]): A tuple containing the typeclass to use and a list of
errors. (which might be empty.)
"""
found_typeclass = typeclass or self.default_typeclasses.get(obj_type, None)
if not found_typeclass:
return None, [f"No typeclass found for object type '{obj_type}'."]
try:
type_class = class_from_module(found_typeclass) if isinstance(found_typeclass, str) else found_typeclass
except ImportError:
return None, [f"Typeclass '{found_typeclass}' could not be imported."]
if not hasattr(type_class, "create"):
return None, [f"Typeclass '{found_typeclass}' is not creatable."]
return type_class, []
def at_pre_puppet(self, account, session=None, **kwargs): def at_pre_puppet(self, account, session=None, **kwargs):
""" """
Called just before an Account connects to this object to puppet Called just before an Account connects to this object to puppet
@ -2768,6 +2729,7 @@ class DefaultCharacter(DefaultObject):
if not self.sessions.count(): if not self.sessions.count():
# only remove this char from grid if no sessions control it anymore. # only remove this char from grid if no sessions control it anymore.
if self.location: if self.location:
def message(obj, from_obj): def message(obj, from_obj):
obj.msg( obj.msg(
_("{name} has left the game{reason}.").format( _("{name} has left the game{reason}.").format(
@ -2829,8 +2791,14 @@ class DefaultRoom(DefaultObject):
) )
@classmethod @classmethod
def create(cls, key: str, account: "DefaultAccount" = None, creator: DefaultObject = None, method: str = "create", def create(
**kwargs): cls,
key: str,
account: "DefaultAccount" = None,
caller: DefaultObject = None,
method: str = "create",
**kwargs,
):
""" """
Creates a basic Room with default parameters, unless otherwise Creates a basic Room with default parameters, unless otherwise
specified or extended. specified or extended.
@ -2844,7 +2812,7 @@ class DefaultRoom(DefaultObject):
account (DefaultAccount, optional): Account to associate this Room with. If account (DefaultAccount, optional): Account to associate this Room with. If
given, it will be given specific control/edit permissions to this given, it will be given specific control/edit permissions to this
object (along with normal Admin perms). If not given, default object (along with normal Admin perms). If not given, default
creator (DefaultObject): The object which is creating this one. caller (DefaultObject): The object which is creating this one.
description (str): Brief description for this object. description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing). ip (str): IP address of creator (for object auditing).
method (str): The method used to create the room. Defaults to "create". method (str): The method used to create the room. Defaults to "create".
@ -3038,8 +3006,16 @@ class DefaultExit(DefaultObject):
# Command hooks # Command hooks
@classmethod @classmethod
def create(cls, key: str, location: DefaultRoom = None, destination: DefaultRoom = None, account: "DefaultAccount" = None, creator: DefaultObject = None, def create(
method: str = "create", **kwargs) -> tuple[typing.Optional["DefaultExit"], list[str]]: cls,
key: str,
location: DefaultRoom = None,
destination: DefaultRoom = None,
account: "DefaultAccount" = None,
caller: DefaultObject = None,
method: str = "create",
**kwargs,
) -> tuple[typing.Optional["DefaultExit"], list[str]]:
""" """
Creates a basic Exit with default parameters, unless otherwise Creates a basic Exit with default parameters, unless otherwise
specified or extended. specified or extended.
@ -3053,7 +3029,7 @@ class DefaultExit(DefaultObject):
Keyword Args: Keyword Args:
account (obj): Account to associate this Exit with. account (obj): Account to associate this Exit with.
creator (ObjectDB): the Object creating this Object. caller (ObjectDB): the Object creating this Object.
description (str): Brief description for this object. description (str): Brief description for this object.
ip (str): IP address of creator (for object auditing). ip (str): IP address of creator (for object auditing).
destination (Room): The room to which this exit should go. destination (Room): The room to which this exit should go.