Merge pull request #3179 from volundmush/player_character_management
Removing direct access to Account.db._playable_characters in favor of…
This commit is contained in:
commit
04e0852753
21 changed files with 173 additions and 108 deletions
|
|
@ -622,7 +622,7 @@ 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.account.db._playable_characters = [new_character]
|
caller.account.add_character(new_character)
|
||||||
|
|
||||||
text = "Character created!"
|
text = "Character created!"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -311,12 +311,12 @@ Our rock-paper-scissor setup works like this:
|
||||||
- `defend` does nothing but has a chance to beat `hit`.
|
- `defend` does nothing but has a chance to beat `hit`.
|
||||||
- `flee/disengage` must succeed two times in a row (i.e. not beaten by a `hit` once during the turn). If so the character leaves combat.
|
- `flee/disengage` must succeed two times in a row (i.e. not beaten by a `hit` once during the turn). If so the character leaves combat.
|
||||||
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# mygame/world/rules.py
|
# mygame/world/rules.py
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
# messages
|
# messages
|
||||||
|
|
||||||
def resolve_combat(combat_handler, actiondict):
|
def resolve_combat(combat_handler, actiondict):
|
||||||
|
|
@ -326,7 +326,7 @@ def resolve_combat(combat_handler, actiondict):
|
||||||
for each character:
|
for each character:
|
||||||
{char.id:[(action1, char, target), (action2, char, target)], ...}
|
{char.id:[(action1, char, target), (action2, char, target)], ...}
|
||||||
"""
|
"""
|
||||||
flee = {} # track number of flee commands per character
|
flee = {} # track number of flee commands per character
|
||||||
for isub in range(2):
|
for isub in range(2):
|
||||||
# loop over sub-turns
|
# loop over sub-turns
|
||||||
messages = []
|
messages = []
|
||||||
|
|
@ -403,6 +403,7 @@ This is the last component we need, a command to initiate combat. This will tie
|
||||||
|
|
||||||
from evennia import create_script
|
from evennia import create_script
|
||||||
|
|
||||||
|
|
||||||
class CmdAttack(Command):
|
class CmdAttack(Command):
|
||||||
"""
|
"""
|
||||||
initiates combat
|
initiates combat
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ def creating(request):
|
||||||
# create the character
|
# create the character
|
||||||
char = create.create_object(typeclass=typeclass, key=name,
|
char = create.create_object(typeclass=typeclass, key=name,
|
||||||
home=home, permissions=perms)
|
home=home, permissions=perms)
|
||||||
user.db._playable_characters.append(char)
|
user.add_character(char)
|
||||||
# add the right locks for the character so the account can
|
# add the right locks for the character so the account can
|
||||||
# puppet it
|
# puppet it
|
||||||
char.locks.add(" or ".join([
|
char.locks.add(" or ".join([
|
||||||
|
|
@ -290,7 +290,7 @@ def creating(request):
|
||||||
# create the character
|
# create the character
|
||||||
char = create.create_object(typeclass=typeclass, key=name,
|
char = create.create_object(typeclass=typeclass, key=name,
|
||||||
home=home, permissions=perms)
|
home=home, permissions=perms)
|
||||||
user.db._playable_characters.append(char)
|
user.add_character(char)
|
||||||
# add the right locks for the character so the account can
|
# add the right locks for the character so the account can
|
||||||
# puppet it
|
# puppet it
|
||||||
char.locks.add(" or ".join([
|
char.locks.add(" or ".join([
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,8 @@ def index(request):
|
||||||
def index(request):
|
def index(request):
|
||||||
"""The 'index' view."""
|
"""The 'index' view."""
|
||||||
user = request.user
|
user = request.user
|
||||||
if not user.is_anonymous() and user.db._playable_characters:
|
if not user.is_anonymous() and user.characters:
|
||||||
character = user.db._playable_characters[0]
|
character = user.characters[0]
|
||||||
```
|
```
|
||||||
|
|
||||||
In this second case, it will select the first character of the account.
|
In this second case, it will select the first character of the account.
|
||||||
|
|
|
||||||
|
|
@ -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,22 +293,31 @@ 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:
|
Called after a character is added to this account's list of playable characters.
|
||||||
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
|
Use it to easily implement custom logic when a character is added to an account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character that was added.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def at_post_remove_character(self, character: "DefaultAccount"):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
character (DefaultCharacter): The character that was removed.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def uses_screenreader(self, session=None):
|
def uses_screenreader(self, session=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -743,8 +826,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
)
|
)
|
||||||
if character:
|
if character:
|
||||||
# Update playable character list
|
# Update playable character list
|
||||||
if character not in self.characters:
|
self.characters.add(character)
|
||||||
self.db._playable_characters.append(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
|
||||||
|
|
@ -1483,11 +1565,8 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
else:
|
else:
|
||||||
# In this mode we don't auto-connect but by default end up at a character selection
|
# In this mode we don't auto-connect but by default end up at a character selection
|
||||||
# screen. We execute look on the account.
|
# 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.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):
|
def at_failed_login(self, session, **kwargs):
|
||||||
|
|
@ -1825,11 +1904,8 @@ class DefaultGuest(DefaultAccount):
|
||||||
be on the safe side.
|
be on the safe side.
|
||||||
"""
|
"""
|
||||||
super().at_server_shutdown()
|
super().at_server_shutdown()
|
||||||
characters = self.db._playable_characters
|
for character in self.characters:
|
||||||
if characters:
|
character.delete()
|
||||||
for character in characters:
|
|
||||||
if character:
|
|
||||||
character.delete()
|
|
||||||
|
|
||||||
def at_post_disconnect(self, **kwargs):
|
def at_post_disconnect(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1841,8 +1917,6 @@ class DefaultGuest(DefaultAccount):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
super().at_post_disconnect()
|
super().at_post_disconnect()
|
||||||
characters = self.db._playable_characters
|
for character in self.characters:
|
||||||
for character in characters:
|
character.delete()
|
||||||
if character:
|
|
||||||
character.delete()
|
|
||||||
self.delete()
|
self.delete()
|
||||||
|
|
|
||||||
|
|
@ -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.db._playable_characters = [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.db._playable_characters = [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()
|
||||||
|
|
||||||
|
|
@ -358,19 +358,19 @@ class TestAccountPuppetDeletion(BaseEvenniaTest):
|
||||||
def test_puppet_deletion(self):
|
def test_puppet_deletion(self):
|
||||||
# Check for existing chars
|
# Check for existing chars
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.account.db._playable_characters, "Account should not have any chars by default."
|
self.account.characters, "Account should not have any chars by default."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add char1 to account's playable characters
|
# Add char1 to account's playable characters
|
||||||
self.account.db._playable_characters.append(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
self.assertTrue(self.account.db._playable_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.
|
||||||
self.char1.delete()
|
self.char1.delete()
|
||||||
# Playable char list should be empty.
|
# Playable char list should be empty.
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.account.db._playable_characters,
|
self.account.characters,
|
||||||
f"Playable character list is not empty! {self.account.db._playable_characters}",
|
f"Playable character list is not empty! {self.account.characters}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -383,10 +383,20 @@ 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):
|
||||||
|
self.assertEqual(self.account.characters.all(), [])
|
||||||
|
self.account.characters.add(self.char1)
|
||||||
|
self.assertEqual(self.account.characters.all(), [self.char1])
|
||||||
|
|
||||||
|
def test_remove_character_from_playable_list(self):
|
||||||
|
self.account.characters.add(self.char1)
|
||||||
|
self.assertEqual(self.account.characters.all(), [self.char1])
|
||||||
|
self.account.characters.remove(self.char1)
|
||||||
|
self.assertEqual(self.account.characters.all(), [])
|
||||||
|
|
||||||
def test_puppet_success(self):
|
def test_puppet_success(self):
|
||||||
self.account.msg = MagicMock()
|
self.account.msg = MagicMock()
|
||||||
with patch("evennia.accounts.accounts._MULTISESSION_MODE", 2):
|
with patch("evennia.accounts.accounts._MULTISESSION_MODE", 2):
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,7 @@ class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
super().parse()
|
super().parse()
|
||||||
|
|
||||||
playable = self.account.db._playable_characters
|
playable = self.account.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
|
|
||||||
# store playable property
|
# store playable property
|
||||||
if self.args:
|
if self.args:
|
||||||
self.playable = dict((utils.to_str(char.key.lower()), char) for char in playable).get(
|
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 (
|
if (
|
||||||
not account.is_superuser
|
not account.is_superuser
|
||||||
and not account.check_permstring("Developer")
|
and not account.check_permstring("Developer")
|
||||||
and account.db._playable_characters
|
and account.characters
|
||||||
and len(account.db._playable_characters) >= _MAX_NR_CHARACTERS
|
and len(account.characters) >= _MAX_NR_CHARACTERS
|
||||||
):
|
):
|
||||||
plural = "" if _MAX_NR_CHARACTERS == 1 else "s"
|
plural = "" if _MAX_NR_CHARACTERS == 1 else "s"
|
||||||
self.msg(f"You may only have a maximum of {_MAX_NR_CHARACTERS} character{plural}.")
|
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"
|
"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.db._playable_characters.append(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:
|
||||||
|
|
@ -223,7 +218,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
# use the playable_characters list to search
|
# use the playable_characters list to search
|
||||||
match = [
|
match = [
|
||||||
char
|
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 char.key.lower() == self.args.lower()
|
||||||
]
|
]
|
||||||
if not match:
|
if not match:
|
||||||
|
|
@ -243,9 +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.db._playable_characters = [
|
caller.characters.remove(delobj)
|
||||||
pc for pc in caller.db._playable_characters if pc != 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(
|
||||||
|
|
@ -314,13 +307,13 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
|
||||||
else:
|
else:
|
||||||
# argument given
|
# argument given
|
||||||
|
|
||||||
if account.db._playable_characters:
|
if (playables := account.characters):
|
||||||
# look at the playable_characters list first
|
# look at the playable_characters list first
|
||||||
character_candidates.extend(
|
character_candidates.extend(
|
||||||
utils.make_iter(
|
utils.make_iter(
|
||||||
account.search(
|
account.search(
|
||||||
self.args,
|
self.args,
|
||||||
candidates=account.db._playable_characters,
|
candidates=playables,
|
||||||
search_object=True,
|
search_object=True,
|
||||||
quiet=True,
|
quiet=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.db._playable_characters = [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.db._playable_characters = [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.db._playable_characters = [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.db._playable_characters.append(self.char1)
|
self.account.characters.add(self.char1)
|
||||||
|
|
||||||
# Try deleting as Developer
|
# Try deleting as Developer
|
||||||
self.call(
|
self.call(
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
|
||||||
session = self.session
|
session = self.session
|
||||||
|
|
||||||
# only one character should be in progress at a time, so we check for WIPs first
|
# 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):
|
if len(in_progress):
|
||||||
# we're continuing chargen for a WIP character
|
# we're continuing chargen for a WIP character
|
||||||
|
|
@ -64,7 +64,7 @@ class ContribCmdCharCreate(MuxAccountCommand):
|
||||||
charmax = settings.MAX_NR_CHARACTERS
|
charmax = settings.MAX_NR_CHARACTERS
|
||||||
|
|
||||||
if not account.is_superuser and (
|
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"
|
plural = "" if charmax == 1 else "s"
|
||||||
self.msg(f"You may only create a maximum of {charmax} character{plural}.")
|
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
|
# 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.db._playable_characters.append(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.db._playable_characters = [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.db._playable_characters.append(new_character)
|
caller.characters.add(new_character)
|
||||||
|
|
||||||
text = "Character created!"
|
text = "Character created!"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1149,10 +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
|
||||||
if self in self.account.db._playable_characters:
|
self.account.characters.remove(self)
|
||||||
self.account.db._playable_characters = [
|
|
||||||
x for x in self.account.db._playable_characters if x != self
|
|
||||||
]
|
|
||||||
for session in self.sessions.all():
|
for session in self.sessions.all():
|
||||||
self.account.unpuppet_object(session)
|
self.account.unpuppet_object(session)
|
||||||
|
|
||||||
|
|
@ -2562,8 +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
|
||||||
if obj not in account.characters:
|
account.characters.add(obj)
|
||||||
account.db._playable_characters.append(obj)
|
|
||||||
|
|
||||||
# Add locks
|
# Add locks
|
||||||
if not locks and account:
|
if not locks and account:
|
||||||
|
|
|
||||||
|
|
@ -98,16 +98,15 @@ def create_objects():
|
||||||
|
|
||||||
# Create the in-game god-character for account #1 and set
|
# Create the in-game god-character for account #1 and set
|
||||||
# it to exist in Limbo.
|
# it to exist in Limbo.
|
||||||
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
|
||||||
try:
|
try:
|
||||||
superuser_character = ObjectDB.objects.get(id=1)
|
superuser_character = ObjectDB.objects.get(id=1)
|
||||||
except ObjectDB.DoesNotExist:
|
except ObjectDB.DoesNotExist:
|
||||||
superuser_character = create.create_object(
|
superuser_character, errors = superuser.create_character(
|
||||||
character_typeclass, key=superuser.username, nohome=True
|
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(
|
superuser_character.locks.add(
|
||||||
"examine:perm(Developer);edit:false();delete:false();boot:false();msg:all();puppet:false()"
|
"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("_first_login", True)
|
||||||
superuser.attributes.add("_last_puppet", superuser_character)
|
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
|
room_typeclass = settings.BASE_ROOM_TYPECLASS
|
||||||
try:
|
try:
|
||||||
limbo_obj = ObjectDB.objects.get(id=2)
|
limbo_obj = ObjectDB.objects.get(id=2)
|
||||||
|
|
|
||||||
|
|
@ -647,9 +647,8 @@ class Evennia:
|
||||||
for guest in AccountDB.objects.all().filter(
|
for guest in AccountDB.objects.all().filter(
|
||||||
db_typeclass_path=settings.BASE_GUEST_TYPECLASS
|
db_typeclass_path=settings.BASE_GUEST_TYPECLASS
|
||||||
):
|
):
|
||||||
for character in guest.db._playable_characters:
|
for character in guest.characters:
|
||||||
if character:
|
character.delete()
|
||||||
character.delete()
|
|
||||||
guest.delete()
|
guest.delete()
|
||||||
for mod in SERVER_STARTSTOP_MODULES:
|
for mod in SERVER_STARTSTOP_MODULES:
|
||||||
if hasattr(mod, "at_server_cold_start"):
|
if hasattr(mod, "at_server_cold_start"):
|
||||||
|
|
|
||||||
|
|
@ -444,3 +444,4 @@ class ServerSession(_BASE_SESSION_CLASS):
|
||||||
return self.account.get_display_name(*args, **kwargs)
|
return self.account.get_display_name(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return f"{self.protocol_key}({self.address})"
|
return f"{self.protocol_key}({self.address})"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@ from evennia.utils.utils import to_str
|
||||||
|
|
||||||
MXP_ENABLED = settings.MXP_ENABLED
|
MXP_ENABLED = settings.MXP_ENABLED
|
||||||
|
|
||||||
|
|
||||||
# ANSI definitions
|
# ANSI definitions
|
||||||
|
|
||||||
ANSI_BEEP = "\07"
|
ANSI_BEEP = "\07"
|
||||||
|
|
|
||||||
|
|
@ -2941,3 +2941,4 @@ def str2int(number):
|
||||||
# invalid number-word, raise ValueError
|
# invalid number-word, raise ValueError
|
||||||
raise ValueError(f"String {original_input} cannot be converted to int.")
|
raise ValueError(f"String {original_input} cannot be converted to int.")
|
||||||
return sum(sums)
|
return sum(sums)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
This will:
|
This will:
|
||||||
|
|
||||||
- Set account.db._last_puppet to this object
|
- 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
|
- Change object locks to allow puppeting by account
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -319,10 +319,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
account.db._last_puppet = obj
|
account.db._last_puppet = obj
|
||||||
if not account.db._playable_characters:
|
account.characters.add(obj)
|
||||||
account.db._playable_characters = []
|
|
||||||
if obj not in account.db._playable_characters:
|
|
||||||
account.db._playable_characters.append(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})"
|
||||||
|
|
@ -331,7 +328,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
request,
|
request,
|
||||||
"Did the following (where possible): "
|
"Did the following (where possible): "
|
||||||
f"Set Account.db._last_puppet = {obj}, "
|
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}.",
|
f"Added 'puppet:pid({account.id})' lock to {obj}.",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ class EvenniaWebTest(BaseEvenniaTest):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
# Add chars to account rosters
|
# Add chars to account rosters
|
||||||
self.account.db._playable_characters = [self.char1]
|
self.account.characters.add(self.char1)
|
||||||
self.account2.db._playable_characters = [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
|
||||||
|
|
@ -44,15 +44,15 @@ class EvenniaWebTest(BaseEvenniaTest):
|
||||||
account.permissions.remove("Developer")
|
account.permissions.remove("Developer")
|
||||||
|
|
||||||
# Grant permissions to chars
|
# Grant permissions to chars
|
||||||
for char in account.db._playable_characters:
|
for char in account.characters:
|
||||||
char.locks.add("edit:id(%s) or perm(Admin)" % account.pk)
|
char.locks.add("edit:id(%s) or perm(Admin)" % account.pk)
|
||||||
char.locks.add("delete:id(%s) or perm(Admin)" % account.pk)
|
char.locks.add("delete:id(%s) or perm(Admin)" % account.pk)
|
||||||
char.locks.add("view:all()")
|
char.locks.add("view:all()")
|
||||||
|
|
||||||
def test_valid_chars(self):
|
def test_valid_chars(self):
|
||||||
"Make sure account has playable characters"
|
"Make sure account has playable characters"
|
||||||
self.assertTrue(self.char1 in self.account.db._playable_characters)
|
self.assertTrue(self.char1 in self.account.characters)
|
||||||
self.assertTrue(self.char2 in self.account2.db._playable_characters)
|
self.assertTrue(self.char2 in self.account2.characters)
|
||||||
|
|
||||||
def get_kwargs(self):
|
def get_kwargs(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
@ -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.db._playable_characters = []
|
self.account.characters.remove(self.char1)
|
||||||
|
|
||||||
# Login account
|
# Login account
|
||||||
self.login()
|
self.login()
|
||||||
|
|
@ -233,9 +233,9 @@ class CharacterCreateView(EvenniaWebTest):
|
||||||
|
|
||||||
# Make sure the character was actually created
|
# Make sure the character was actually created
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
len(self.account.db._playable_characters) == 1,
|
len(self.account.characters) == 1,
|
||||||
"Account only has the following characters attributed to it: %s"
|
"Account only has the following characters attributed to it: %s"
|
||||||
% self.account.db._playable_characters,
|
% self.account.characters,
|
||||||
)
|
)
|
||||||
|
|
||||||
@override_settings(MAX_NR_CHARACTERS=5)
|
@override_settings(MAX_NR_CHARACTERS=5)
|
||||||
|
|
@ -252,9 +252,9 @@ class CharacterCreateView(EvenniaWebTest):
|
||||||
|
|
||||||
# Make sure the character was actually created
|
# Make sure the character was actually created
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
len(self.account.db._playable_characters) > 1,
|
len(self.account.characters) > 1,
|
||||||
"Account only has the following characters attributed to it: %s"
|
"Account only has the following characters attributed to it: %s"
|
||||||
% self.account.db._playable_characters,
|
% self.account.characters,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -352,7 +352,7 @@ class CharacterDeleteView(EvenniaWebTest):
|
||||||
|
|
||||||
# Make sure it deleted
|
# Make sure it deleted
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
self.char1 in self.account.db._playable_characters,
|
self.char1 in self.account.characters,
|
||||||
"Char1 is still in Account playable characters list.",
|
"Char1 is still in Account playable characters list.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.db._playable_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():
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ dependencies = [
|
||||||
"anything ==0.2.1",
|
"anything ==0.2.1",
|
||||||
"black >= 22.6",
|
"black >= 22.6",
|
||||||
"isort >= 5.10",
|
"isort >= 5.10",
|
||||||
"parameterized ==0.8.1",
|
"parameterized ==0.8.1"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue