Added CharactersHandler to account and altered all calls of add/remove characters to use it.
This commit is contained in:
parent
3c4a3f1088
commit
2bf96f7c7f
11 changed files with 112 additions and 63 deletions
|
|
@ -120,6 +120,80 @@ class AccountSessionHandler(object):
|
||||||
return len(self.get())
|
return len(self.get())
|
||||||
|
|
||||||
|
|
||||||
|
class CharactersHandler:
|
||||||
|
"""
|
||||||
|
A simple Handler that lives on DefaultAccount as .characters via @lazy_property used to
|
||||||
|
wrap access to .db._playable_characters.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, owner: "DefaultAccount"):
|
||||||
|
"""
|
||||||
|
Create the CharactersHandler.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
owner: The Account that owns this handler.
|
||||||
|
"""
|
||||||
|
self.owner = owner
|
||||||
|
self._ensure_playable_characters()
|
||||||
|
self._clean()
|
||||||
|
|
||||||
|
def _ensure_playable_characters(self):
|
||||||
|
if self.owner.db._playable_characters is None:
|
||||||
|
self.owner.db._playable_characters = []
|
||||||
|
|
||||||
|
def _clean(self):
|
||||||
|
# Remove all instances of None from the list.
|
||||||
|
self.owner.db._playable_characters = [x for x in self.owner.db._playable_characters if x]
|
||||||
|
|
||||||
|
def add(self, character: "DefaultCharacter"):
|
||||||
|
"""
|
||||||
|
Add a character to this account's list of playable characters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character to add.
|
||||||
|
"""
|
||||||
|
self._clean()
|
||||||
|
if character not in self.owner.db._playable_characters:
|
||||||
|
self.owner.db._playable_characters.append(character)
|
||||||
|
self.owner.at_post_add_character(character)
|
||||||
|
|
||||||
|
def remove(self, character: "DefaultCharacter"):
|
||||||
|
"""
|
||||||
|
Remove a character from this account's list of playable characters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character to remove.
|
||||||
|
"""
|
||||||
|
self._clean()
|
||||||
|
if character in self.owner.db._playable_characters:
|
||||||
|
self.owner.db._playable_characters.remove(character)
|
||||||
|
self.owner.at_post_remove_character(character)
|
||||||
|
|
||||||
|
def all(self) -> list["DefaultCharacter"]:
|
||||||
|
"""
|
||||||
|
Get all playable characters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[DefaultCharacter]: All playable characters.
|
||||||
|
"""
|
||||||
|
self._clean()
|
||||||
|
return list(self.owner.db._playable_characters)
|
||||||
|
|
||||||
|
def count(self) -> int:
|
||||||
|
"""
|
||||||
|
Get the number of playable characters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The number of playable characters.
|
||||||
|
"""
|
||||||
|
return len(self.all())
|
||||||
|
|
||||||
|
__len__ = count
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.all())
|
||||||
|
|
||||||
|
|
||||||
class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
"""
|
"""
|
||||||
This is the base Typeclass for all Accounts. Accounts represent
|
This is the base Typeclass for all Accounts. Accounts represent
|
||||||
|
|
@ -219,56 +293,32 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
load_kwargs={"category": "option"},
|
load_kwargs={"category": "option"},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Do not make this a lazy property; the web UI will not refresh it!
|
@lazy_property
|
||||||
@property
|
|
||||||
def characters(self):
|
def characters(self):
|
||||||
# Get playable characters list
|
return CharactersHandler(self)
|
||||||
objs = self.db._playable_characters or []
|
|
||||||
|
|
||||||
# Rebuild the list if legacy code left null values after deletion
|
def at_post_add_character(self, character: "DefaultCharacter"):
|
||||||
try:
|
|
||||||
if None in objs:
|
|
||||||
objs = [x for x in self.db._playable_characters if x]
|
|
||||||
self.db._playable_characters = objs
|
|
||||||
except Exception as e:
|
|
||||||
logger.log_trace(e)
|
|
||||||
logger.log_err(e)
|
|
||||||
|
|
||||||
return objs
|
|
||||||
|
|
||||||
def add_character_to_playable_list(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_to_playable_list(character)
|
|
||||||
|
|
||||||
def at_post_add_character_to_playable_list(self, character: "DefaultCharacter"):
|
|
||||||
"""
|
"""
|
||||||
Called after a character is added to this account's list of playable characters.
|
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.
|
Use it to easily implement custom logic when a character is added to an account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character that was added.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_character_from_playable_list(self, character):
|
def at_post_remove_character(self, character: "DefaultAccount"):
|
||||||
"""
|
|
||||||
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_from_playable_list(character)
|
|
||||||
|
|
||||||
def at_post_remove_character_from_playable_list(self, character):
|
|
||||||
"""
|
"""
|
||||||
Called after a character is removed from this account's list of playable characters.
|
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.
|
Use it to easily implement custom logic when a character is removed from an account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character that was removed.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def uses_screenreader(self, session=None):
|
def uses_screenreader(self, session=None):
|
||||||
"""
|
"""
|
||||||
Shortcut to determine if a session uses a screenreader. If no session given,
|
Shortcut to determine if a session uses a screenreader. If no session given,
|
||||||
|
|
@ -776,7 +826,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
)
|
)
|
||||||
if character:
|
if character:
|
||||||
# Update playable character list
|
# Update playable character list
|
||||||
self.add_character_to_playable_list(character)
|
self.characters.add(character)
|
||||||
|
|
||||||
# We need to set this to have @ic auto-connect to this character
|
# We need to set this to have @ic auto-connect to this character
|
||||||
self.db._last_puppet = character
|
self.db._last_puppet = character
|
||||||
|
|
|
||||||
|
|
@ -105,14 +105,14 @@ class TestDefaultGuest(BaseEvenniaTest):
|
||||||
def test_at_server_shutdown(self):
|
def test_at_server_shutdown(self):
|
||||||
account, errors = DefaultGuest.create(ip=self.ip)
|
account, errors = DefaultGuest.create(ip=self.ip)
|
||||||
self.char1.delete = MagicMock()
|
self.char1.delete = MagicMock()
|
||||||
account.add_character_to_playable_list(self.char1)
|
account.characters.add(self.char1)
|
||||||
account.at_server_shutdown()
|
account.at_server_shutdown()
|
||||||
self.char1.delete.assert_called()
|
self.char1.delete.assert_called()
|
||||||
|
|
||||||
def test_at_post_disconnect(self):
|
def test_at_post_disconnect(self):
|
||||||
account, errors = DefaultGuest.create(ip=self.ip)
|
account, errors = DefaultGuest.create(ip=self.ip)
|
||||||
self.char1.delete = MagicMock()
|
self.char1.delete = MagicMock()
|
||||||
account.add_character_to_playable_list(self.char1)
|
account.characters.add(self.char1)
|
||||||
account.at_post_disconnect()
|
account.at_post_disconnect()
|
||||||
self.char1.delete.assert_called()
|
self.char1.delete.assert_called()
|
||||||
|
|
||||||
|
|
@ -362,7 +362,7 @@ class TestAccountPuppetDeletion(BaseEvenniaTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add char1 to account's playable characters
|
# Add char1 to account's playable characters
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.assertTrue(self.account.characters, "Char was not added to account.")
|
self.assertTrue(self.account.characters, "Char was not added to account.")
|
||||||
|
|
||||||
# See what happens when we delete char1.
|
# See what happens when we delete char1.
|
||||||
|
|
@ -383,20 +383,19 @@ class TestDefaultAccountEv(BaseEvenniaTest):
|
||||||
def test_characters_property(self):
|
def test_characters_property(self):
|
||||||
"test existence of None in _playable_characters Attr"
|
"test existence of None in _playable_characters Attr"
|
||||||
self.account.db._playable_characters = [self.char1, None]
|
self.account.db._playable_characters = [self.char1, None]
|
||||||
chars = self.account.characters
|
self.assertEqual(self.account.characters.all(), [self.char1])
|
||||||
self.assertEqual(chars, [self.char1])
|
|
||||||
self.assertEqual(self.account.db._playable_characters, [self.char1])
|
self.assertEqual(self.account.db._playable_characters, [self.char1])
|
||||||
|
|
||||||
def test_add_character_to_playable_list(self):
|
def test_add_character_to_playable_list(self):
|
||||||
self.assertEqual(self.account.characters, [])
|
self.assertEqual(self.account.characters.all(), [])
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.assertEqual(self.account.characters, [self.char1])
|
self.assertEqual(self.account.characters.all(), [self.char1])
|
||||||
|
|
||||||
def test_remove_character_from_playable_list(self):
|
def test_remove_character_from_playable_list(self):
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.assertEqual(self.account.characters, [self.char1])
|
self.assertEqual(self.account.characters.all(), [self.char1])
|
||||||
self.account.remove_character_from_playable_list(self.char1)
|
self.account.characters.remove(self.char1)
|
||||||
self.assertEqual(self.account.characters, [])
|
self.assertEqual(self.account.characters.all(), [])
|
||||||
|
|
||||||
def test_puppet_success(self):
|
def test_puppet_success(self):
|
||||||
self.account.msg = MagicMock()
|
self.account.msg = MagicMock()
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||||
"puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer);delete:id(%i) or"
|
"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)
|
" perm(Admin)" % (new_character.id, account.id, account.id)
|
||||||
)
|
)
|
||||||
account.add_character_to_playable_list(new_character)
|
account.characters.add(new_character)
|
||||||
if desc:
|
if desc:
|
||||||
new_character.db.desc = desc
|
new_character.db.desc = desc
|
||||||
elif not new_character.db.desc:
|
elif not new_character.db.desc:
|
||||||
|
|
@ -238,7 +238,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
# only take action
|
# only take action
|
||||||
delobj = caller.ndb._char_to_delete
|
delobj = caller.ndb._char_to_delete
|
||||||
key = delobj.key
|
key = delobj.key
|
||||||
caller.remove_character_from_playable_list(delobj)
|
caller.characters.remove(delobj)
|
||||||
delobj.delete()
|
delobj.delete()
|
||||||
self.msg(f"Character '{key}' was permanently deleted.")
|
self.msg(f"Character '{key}' was permanently deleted.")
|
||||||
logger.log_sec(
|
logger.log_sec(
|
||||||
|
|
|
||||||
|
|
@ -589,7 +589,7 @@ class TestAccount(BaseEvenniaCommandTest):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_ooc_look(self, multisession_mode, auto_puppet, max_nr_chars, expected_result):
|
def test_ooc_look(self, multisession_mode, auto_puppet, max_nr_chars, expected_result):
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.account.unpuppet_all()
|
self.account.unpuppet_all()
|
||||||
|
|
||||||
with self.settings(MULTISESSION=multisession_mode):
|
with self.settings(MULTISESSION=multisession_mode):
|
||||||
|
|
@ -609,14 +609,14 @@ class TestAccount(BaseEvenniaCommandTest):
|
||||||
self.call(account.CmdOOC(), "", "You go OOC.", caller=self.account)
|
self.call(account.CmdOOC(), "", "You go OOC.", caller=self.account)
|
||||||
|
|
||||||
def test_ic(self):
|
def test_ic(self):
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.account.unpuppet_object(self.session)
|
self.account.unpuppet_object(self.session)
|
||||||
self.call(
|
self.call(
|
||||||
account.CmdIC(), "Char", "You become Char.", caller=self.account, receiver=self.char1
|
account.CmdIC(), "Char", "You become Char.", caller=self.account, receiver=self.char1
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_ic__other_object(self):
|
def test_ic__other_object(self):
|
||||||
self.account.add_character_to_playable_list(self.obj1)
|
self.account.characters.add(self.obj1)
|
||||||
self.account.unpuppet_object(self.session)
|
self.account.unpuppet_object(self.session)
|
||||||
self.call(
|
self.call(
|
||||||
account.CmdIC(), "Obj", "You become Obj.", caller=self.account, receiver=self.obj1
|
account.CmdIC(), "Obj", "You become Obj.", caller=self.account, receiver=self.obj1
|
||||||
|
|
@ -670,7 +670,7 @@ class TestAccount(BaseEvenniaCommandTest):
|
||||||
# whether permissions are being checked
|
# whether permissions are being checked
|
||||||
|
|
||||||
# Add char to account playable characters
|
# Add char to account playable characters
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
|
|
||||||
# Try deleting as Developer
|
# Try deleting as Developer
|
||||||
self.call(
|
self.call(
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
|
||||||
)
|
)
|
||||||
# initalize the new character to the beginning of the chargen menu
|
# initalize the new character to the beginning of the chargen menu
|
||||||
new_character.db.chargen_step = "menunode_welcome"
|
new_character.db.chargen_step = "menunode_welcome"
|
||||||
account.add_character_to_playable_list(new_character)
|
account.characters.add(new_character)
|
||||||
|
|
||||||
# set the menu node to start at to the character's last saved step
|
# set the menu node to start at to the character's last saved step
|
||||||
startnode = new_character.db.chargen_step
|
startnode = new_character.db.chargen_step
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class TestCharacterCreator(BaseEvenniaCommandTest):
|
||||||
self.account.swap_typeclass(character_creator.ContribChargenAccount)
|
self.account.swap_typeclass(character_creator.ContribChargenAccount)
|
||||||
|
|
||||||
def test_ooc_look(self):
|
def test_ooc_look(self):
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.account.unpuppet_all()
|
self.account.unpuppet_all()
|
||||||
|
|
||||||
self.char1.db.chargen_step = "start"
|
self.char1.db.chargen_step = "start"
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ def node_apply_character(caller, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
tmp_character = kwargs["tmp_character"]
|
tmp_character = kwargs["tmp_character"]
|
||||||
new_character = tmp_character.apply(caller)
|
new_character = tmp_character.apply(caller)
|
||||||
caller.add_character_to_playable_list(new_character)
|
caller.characters.add(new_character)
|
||||||
|
|
||||||
text = "Character created!"
|
text = "Character created!"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1149,7 +1149,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
# sever the connection (important!)
|
# sever the connection (important!)
|
||||||
if self.account:
|
if self.account:
|
||||||
# Remove the object from playable characters list
|
# Remove the object from playable characters list
|
||||||
self.account.remove_character_from_playable_list(self)
|
self.account.characters.remove(self)
|
||||||
for session in self.sessions.all():
|
for session in self.sessions.all():
|
||||||
self.account.unpuppet_object(session)
|
self.account.unpuppet_object(session)
|
||||||
|
|
||||||
|
|
@ -2559,7 +2559,7 @@ class DefaultCharacter(DefaultObject):
|
||||||
obj.db.creator_ip = ip
|
obj.db.creator_ip = ip
|
||||||
if account:
|
if account:
|
||||||
obj.db.creator_id = account.id
|
obj.db.creator_id = account.id
|
||||||
account.add_character_to_playable_list(obj)
|
account.characters.add(obj)
|
||||||
|
|
||||||
# Add locks
|
# Add locks
|
||||||
if not locks and account:
|
if not locks and account:
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
account.db._last_puppet = obj
|
account.db._last_puppet = obj
|
||||||
account.add_character_to_playable_list(obj)
|
account.characters.add(obj)
|
||||||
if not obj.access(account, "puppet"):
|
if not obj.access(account, "puppet"):
|
||||||
lock = obj.locks.get("puppet")
|
lock = obj.locks.get("puppet")
|
||||||
lock += f" or pid({account.id})"
|
lock += f" or pid({account.id})"
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ class EvenniaWebTest(BaseEvenniaTest):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
# Add chars to account rosters
|
# Add chars to account rosters
|
||||||
self.account.add_character_to_playable_list(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.account2.add_character_to_playable_list(self.char2)
|
self.account2.characters.add(self.char2)
|
||||||
|
|
||||||
for account in (self.account, self.account2):
|
for account in (self.account, self.account2):
|
||||||
# Demote accounts to Player permissions
|
# Demote accounts to Player permissions
|
||||||
|
|
@ -220,7 +220,7 @@ class CharacterCreateView(EvenniaWebTest):
|
||||||
@override_settings(MAX_NR_CHARACTERS=1)
|
@override_settings(MAX_NR_CHARACTERS=1)
|
||||||
def test_valid_access_multisession_0(self):
|
def test_valid_access_multisession_0(self):
|
||||||
"Account1 with no characters should be able to create a new one"
|
"Account1 with no characters should be able to create a new one"
|
||||||
self.account.remove_character_from_playable_list(self.char1)
|
self.account.characters.remove(self.char1)
|
||||||
|
|
||||||
# Login account
|
# Login account
|
||||||
self.login()
|
self.login()
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ def collect_topics(account):
|
||||||
cmd_help_topics = []
|
cmd_help_topics = []
|
||||||
if not str(account) == "AnonymousUser":
|
if not str(account) == "AnonymousUser":
|
||||||
# create list of account and account's puppets
|
# create list of account and account's puppets
|
||||||
puppets = account.characters + [account]
|
puppets = account.characters.all() + [account]
|
||||||
# add the account's and puppets' commands to cmd_help_topics list
|
# add the account's and puppets' commands to cmd_help_topics list
|
||||||
for puppet in puppets:
|
for puppet in puppets:
|
||||||
for cmdset in puppet.cmdset.get():
|
for cmdset in puppet.cmdset.get():
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue