Removing direct access to Account.db._playable_characters in favor of Account.add_character(char) and Account.remove_character(char). Account.characters already handles all filtering so am cleaning up lots of repeated list comprehensions which remove/filter deleted characters.

This commit is contained in:
Andrew Bastien 2023-05-06 02:31:07 -04:00
parent a8cf8e166a
commit f782cd8fc8
10 changed files with 124 additions and 60 deletions

View file

@ -236,6 +236,39 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
return objs
def add_character(self, character: "DefaultCharacter"):
"""
Add a character to this account's list of playable characters.
"""
if character not in self.db._playable_characters:
self.db._playable_characters.append(character)
self.at_post_add_character(character)
def at_post_add_character(self, character: "DefaultCharacter"):
"""
Called after a character is added to this account's list of playable characters.
Use it to easily implement custom logic when a character is added to an account.
"""
pass
def remove_character(self, character):
"""
Remove a character from this account's list of playable characters.
"""
if character in self.db._playable_characters:
self.db._playable_characters.remove(character)
self.at_post_remove_character(character)
def at_post_remove_character(self, character):
"""
Called after a character is removed from this account's list of playable characters.
Use it to easily implement custom logic when a character is removed from an account.
"""
pass
def uses_screenreader(self, session=None):
"""
Shortcut to determine if a session uses a screenreader. If no session given,
@ -743,8 +776,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
)
if character:
# Update playable character list
if character not in self.characters:
self.db._playable_characters.append(character)
self.add_character(character)
# We need to set this to have @ic auto-connect to this character
self.db._last_puppet = character
@ -1483,11 +1515,8 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
else:
# In this mode we don't auto-connect but by default end up at a character selection
# screen. We execute look on the account.
# we make sure to clean up the _playable_characters list in case
# any was deleted in the interim.
self.db._playable_characters = [char for char in self.db._playable_characters if char]
self.msg(
self.at_look(target=self.db._playable_characters, session=session), session=session
self.at_look(target=self.characters, session=session), session=session
)
def at_failed_login(self, session, **kwargs):
@ -1825,11 +1854,8 @@ class DefaultGuest(DefaultAccount):
be on the safe side.
"""
super().at_server_shutdown()
characters = self.db._playable_characters
if characters:
for character in characters:
if character:
character.delete()
for character in self.characters:
character.delete()
def at_post_disconnect(self, **kwargs):
"""
@ -1841,8 +1867,6 @@ class DefaultGuest(DefaultAccount):
"""
super().at_post_disconnect()
characters = self.db._playable_characters
for character in characters:
if character:
character.delete()
for character in self.characters:
character.delete()
self.delete()

View file

@ -60,12 +60,7 @@ class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS):
super().parse()
playable = self.account.db._playable_characters
if playable is not None:
# clean up list if character object was deleted in between
if None in playable:
playable = [character for character in playable if character]
self.account.db._playable_characters = playable
playable = self.account.characters
# store playable property
if self.args:
self.playable = dict((utils.to_str(char.key.lower()), char) for char in playable).get(
@ -155,8 +150,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
if (
not account.is_superuser
and not account.check_permstring("Developer")
and account.db._playable_characters
and len(account.db._playable_characters) >= _MAX_NR_CHARACTERS
and account.characters
and len(account.characters) >= _MAX_NR_CHARACTERS
):
plural = "" if _MAX_NR_CHARACTERS == 1 else "s"
self.msg(f"You may only have a maximum of {_MAX_NR_CHARACTERS} character{plural}.")
@ -184,7 +179,7 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
"puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or"
" perm(Admin)" % (new_character.id, account.id, account.id)
)
account.db._playable_characters.append(new_character)
account.add_character(new_character)
if desc:
new_character.db.desc = desc
elif not new_character.db.desc:
@ -223,7 +218,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
# use the playable_characters list to search
match = [
char
for char in utils.make_iter(account.db._playable_characters)
for char in utils.make_iter(account.characters)
if char.key.lower() == self.args.lower()
]
if not match:
@ -243,9 +238,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
# only take action
delobj = caller.ndb._char_to_delete
key = delobj.key
caller.db._playable_characters = [
pc for pc in caller.db._playable_characters if pc != delobj
]
caller.remove_character(delobj)
delobj.delete()
self.msg(f"Character '{key}' was permanently deleted.")
logger.log_sec(
@ -314,13 +307,13 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
else:
# argument given
if account.db._playable_characters:
if (playables := account.characters):
# look at the playable_characters list first
character_candidates.extend(
utils.make_iter(
account.search(
self.args,
candidates=account.db._playable_characters,
candidates=playables,
search_object=True,
quiet=True,
)

View file

@ -465,3 +465,64 @@ class CmdUnconnectedInfo(COMMAND_DEFAULT_CLASS):
utils.get_evennia_version(),
)
)
def _create_account(session, accountname, password, permissions, typeclass=None, email=None):
"""
Helper function, creates an account of the specified typeclass.
"""
try:
new_account = create.create_account(
accountname, email, password, permissions=permissions, typeclass=typeclass
)
except Exception as e:
session.msg(
"There was an error creating the Account:\n%s\n If this problem persists, contact an"
" admin." % e
)
logger.log_trace()
return False
# This needs to be set so the engine knows this account is
# logging in for the first time. (so it knows to call the right
# hooks during login later)
new_account.db.FIRST_LOGIN = True
# join the new account to the public channel
pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"])
if not pchannel or not pchannel.connect(new_account):
string = "New account '%s' could not connect to public channel!" % new_account.key
logger.log_err(string)
return new_account
def _create_character(session, new_account, typeclass, home, permissions):
"""
Helper function, creates a character based on an account's name.
This is meant for Guest and AUTO_CREATRE_CHARACTER_WITH_ACCOUNT=True situations.
"""
try:
new_character = create.create_object(
typeclass, key=new_account.key, home=home, permissions=permissions
)
# set playable character list
new_account.add_character(new_character)
# allow only the character itself and the account to puppet this character (and Developers).
new_character.locks.add(
"puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer)"
% (new_character.id, new_account.id)
)
# If no description is set, set a default description
if not new_character.db.desc:
new_character.db.desc = "This is a character."
# We need to set this to have ic auto-connect to this character
new_account.db._last_puppet = new_character
except Exception as e:
session.msg(
"There was an error creating the Character:\n%s\n If this problem persists, contact an"
" admin." % e
)
logger.log_trace()

View file

@ -54,7 +54,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
session = self.session
# only one character should be in progress at a time, so we check for WIPs first
in_progress = [chara for chara in account.db._playable_characters if chara.db.chargen_step]
in_progress = [chara for chara in account.characters if chara.db.chargen_step]
if len(in_progress):
# we're continuing chargen for a WIP character
@ -64,7 +64,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
charmax = settings.MAX_NR_CHARACTERS
if not account.is_superuser and (
account.db._playable_characters and len(account.db._playable_characters) >= charmax
account.characters and len(account.characters) >= charmax
):
plural = "" if charmax == 1 else "s"
self.msg(f"You may only create a maximum of {charmax} character{plural}.")
@ -90,7 +90,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
)
# initalize the new character to the beginning of the chargen menu
new_character.db.chargen_step = "menunode_welcome"
account.db._playable_characters.append(new_character)
account.add_character(new_character)
# set the menu node to start at to the character's last saved step
startnode = new_character.db.chargen_step

View file

@ -316,7 +316,7 @@ def node_apply_character(caller, raw_string, **kwargs):
"""
tmp_character = kwargs["tmp_character"]
new_character = tmp_character.apply(caller)
caller.db._playable_characters.append(new_character)
caller.add_character(new_character)
text = "Character created!"

View file

@ -1149,10 +1149,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
# sever the connection (important!)
if self.account:
# Remove the object from playable characters list
if self in self.account.db._playable_characters:
self.account.db._playable_characters = [
x for x in self.account.db._playable_characters if x != self
]
self.account.remove_character(self)
for session in self.sessions.all():
self.account.unpuppet_object(session)
@ -2562,8 +2559,7 @@ class DefaultCharacter(DefaultObject):
obj.db.creator_ip = ip
if account:
obj.db.creator_id = account.id
if obj not in account.characters:
account.db._playable_characters.append(obj)
account.add_character(obj)
# Add locks
if not locks and account:

View file

@ -98,16 +98,15 @@ def create_objects():
# Create the in-game god-character for account #1 and set
# it to exist in Limbo.
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
try:
superuser_character = ObjectDB.objects.get(id=1)
except ObjectDB.DoesNotExist:
superuser_character = create.create_object(
character_typeclass, key=superuser.username, nohome=True
superuser_character, errors = superuser.create_character(
key=superuser.username, nohome=True, description=_("This is User #1.")
)
if errors:
raise Exception(str(errors))
superuser_character.db_typeclass_path = character_typeclass
superuser_character.db.desc = _("This is User #1.")
superuser_character.locks.add(
"examine:perm(Developer);edit:false();delete:false();boot:false();msg:all();puppet:false()"
)
@ -118,11 +117,6 @@ def create_objects():
superuser.attributes.add("_first_login", True)
superuser.attributes.add("_last_puppet", superuser_character)
try:
superuser.db._playable_characters.append(superuser_character)
except AttributeError:
superuser.db_playable_characters = [superuser_character]
room_typeclass = settings.BASE_ROOM_TYPECLASS
try:
limbo_obj = ObjectDB.objects.get(id=2)

View file

@ -647,9 +647,8 @@ class Evennia:
for guest in AccountDB.objects.all().filter(
db_typeclass_path=settings.BASE_GUEST_TYPECLASS
):
for character in guest.db._playable_characters:
if character:
character.delete()
for character in guest.characters:
character.delete()
guest.delete()
for mod in SERVER_STARTSTOP_MODULES:
if hasattr(mod, "at_server_cold_start"):

View file

@ -310,7 +310,7 @@ class ObjectAdmin(admin.ModelAdmin):
This will:
- Set account.db._last_puppet to this object
- Add object to account.db._playable_characters
- Add object to account.characters
- Change object locks to allow puppeting by account
"""
@ -319,10 +319,7 @@ class ObjectAdmin(admin.ModelAdmin):
if account:
account.db._last_puppet = obj
if not account.db._playable_characters:
account.db._playable_characters = []
if obj not in account.db._playable_characters:
account.db._playable_characters.append(obj)
account.add_character(obj)
if not obj.access(account, "puppet"):
lock = obj.locks.get("puppet")
lock += f" or pid({account.id})"
@ -331,7 +328,7 @@ class ObjectAdmin(admin.ModelAdmin):
request,
"Did the following (where possible): "
f"Set Account.db._last_puppet = {obj}, "
f"Added {obj} to Account.db._playable_characters list, "
f"Added {obj} to Account.characters list, "
f"Added 'puppet:pid({account.id})' lock to {obj}.",
)
else:

View file

@ -102,7 +102,7 @@ def collect_topics(account):
cmd_help_topics = []
if not str(account) == "AnonymousUser":
# create list of account and account's puppets
puppets = account.db._playable_characters + [account]
puppets = account.characters + [account]
# add the account's and puppets' commands to cmd_help_topics list
for puppet in puppets:
for cmdset in puppet.cmdset.get():