Break up/extend MULTISESSION_MODE into several new settings

This commit is contained in:
Griatch 2022-09-14 23:46:10 +02:00
parent e9cce46d66
commit a0a0a248a0
13 changed files with 392 additions and 368 deletions

View file

@ -20,14 +20,15 @@ method. Otherwise all text will be returned to all connected sessions.
"""
import time
from codecs import lookup as codecs_lookup
from django.conf import settings
from evennia.server.sessionhandler import SESSIONS
from evennia.utils import utils, create, logger, search
from evennia.utils import create, logger, search, utils
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
_MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS
_MULTISESSION_MODE = settings.MULTISESSION_MODE
_AUTO_PUPPET_ON_LOGIN = settings.AUTO_PUPPET_ON_LOGIN
# limit symbol import for API
__all__ = (
@ -59,11 +60,6 @@ class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS):
super().parse()
if _MULTISESSION_MODE < 2:
# only one character allowed - not used in this mode
self.playable = None
return
playable = self.account.db._playable_characters
if playable is not None:
# clean up list if character object was deleted in between
@ -111,8 +107,14 @@ class CmdOOCLook(MuxAccountLookCommand):
def func(self):
"""implement the ooc look command"""
if _MULTISESSION_MODE < 2:
# only one character allowed
if self.session.puppet:
# if we are puppeting, this is only reached in the case the that puppet
# has no look command on its own.
self.msg("You currently have no ability to look around.")
return
if _AUTO_PUPPET_ON_LOGIN and _MAX_NR_CHARACTERS == 1 and self.playable:
# only one exists and is allowed - simplify
self.msg("You are out-of-character (OOC).\nUse |wic|n to get back into the game.")
return
@ -149,14 +151,16 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
key = self.lhs
desc = self.rhs
charmax = _MAX_NR_CHARACTERS
if not account.is_superuser and (
account.db._playable_characters and len(account.db._playable_characters) >= charmax
):
plural = "" if charmax == 1 else "s"
self.msg(f"You may only create a maximum of {charmax} character{plural}.")
return
if _MAX_NR_CHARACTERS is not None:
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
):
plural = "" if _MAX_NR_CHARACTERS == 1 else "s"
self.msg(f"You may only have a maximum of {_MAX_NR_CHARACTERS} character{plural}.")
return
from evennia.objects.models import ObjectDB
typeclass = settings.BASE_CHARACTER_TYPECLASS
@ -177,8 +181,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
)
# only allow creator (and developers) to puppet this char
new_character.locks.add(
"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)
"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)
if desc:
@ -228,7 +232,8 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
return
elif len(match) > 1:
self.msg(
"Aborting - there are two characters with the same name. Ask an admin to delete the right one."
"Aborting - there are two characters with the same name. Ask an admin to delete the"
" right one."
)
return
else: # one match
@ -419,8 +424,8 @@ class CmdOOC(MuxAccountLookCommand):
account.unpuppet_object(session)
self.msg("\n|GYou go OOC.|n\n")
if _MULTISESSION_MODE < 2:
# only one character allowed
if _AUTO_PUPPET_ON_LOGIN and _MAX_NR_CHARACTERS == 1 and self.playable:
# only one character exists and is allowed - simplify
self.msg("You are out-of-character (OOC).\nUse |wic|n to get back into the game.")
return
@ -917,7 +922,10 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
% (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib))
)
table = self.table_format(table)
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
string = (
"Xterm256 colors (if not all hues show, your client might not report that it can"
" handle xterm256):"
)
string += "\n" + "\n".join("".join(row) for row in table)
table = [[], [], [], [], [], [], [], [], [], [], [], []]
for ibatch in range(4):
@ -985,9 +993,7 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
"""Perform the command"""
account = self.account
permstr = (
account.is_superuser
and " (superuser)"
or "(%s)" % (", ".join(account.permissions.all()))
account.is_superuser and " (superuser)" or "(%s)" % ", ".join(account.permissions.all())
)
if self.cmdstring in ("unquell", "unquell"):
if not account.attributes.get("_quell"):

View file

@ -12,41 +12,34 @@ main test suite started with
"""
import datetime
from unittest.mock import MagicMock, Mock, patch
from anything import Anything
from parameterized import parameterized
from django.conf import settings
from twisted.internet import task
from unittest.mock import patch, Mock, MagicMock
from evennia import DefaultRoom, DefaultExit, ObjectDB
from evennia.commands.default.cmdset_character import CharacterCmdSet
from evennia.utils.test_resources import (
BaseEvenniaTest,
BaseEvenniaCommandTest,
EvenniaCommandTest,
) # noqa
from evennia.commands.default import (
help as help_module,
general,
system,
admin,
account,
building,
batchprocess,
comms,
unloggedin,
syscommands,
from django.test import override_settings
from evennia import (
DefaultCharacter,
DefaultExit,
DefaultObject,
DefaultRoom,
ObjectDB,
search_object,
)
from evennia.commands.default.muxcommand import MuxCommand
from evennia.commands.command import Command, InterruptCommand
from evennia.commands import cmdparser
from evennia.commands.cmdset import CmdSet
from evennia.utils import utils, gametime, create
from evennia.server.sessionhandler import SESSIONS
from evennia import search_object
from evennia import DefaultObject, DefaultCharacter
from evennia.commands.command import Command, InterruptCommand
from evennia.commands.default import account, admin, batchprocess, building, comms, general
from evennia.commands.default import help as help_module
from evennia.commands.default import syscommands, system, unloggedin
from evennia.commands.default.cmdset_character import CharacterCmdSet
from evennia.commands.default.muxcommand import MuxCommand
from evennia.prototypes import prototypes as protlib
from evennia.server.sessionhandler import SESSIONS
from evennia.utils import create, gametime, utils
from evennia.utils.test_resources import BaseEvenniaCommandTest # noqa
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaCommandTest
from parameterized import parameterized
from twisted.internet import task
# ------------------------------------------------------------
# Command testing
@ -232,19 +225,16 @@ class TestHelp(BaseEvenniaCommandTest):
),
(
"test/extra/subsubtopic", # partial subsub-match
"Help for test/creating extra stuff/subsubtopic\n\n" "A subsubtopic text",
"Help for test/creating extra stuff/subsubtopic\n\nA subsubtopic text",
),
(
"test/creating extra/subsub", # partial subsub-match
"Help for test/creating extra stuff/subsubtopic\n\n" "A subsubtopic text",
"Help for test/creating extra stuff/subsubtopic\n\nA subsubtopic text",
),
("test/Something else", "Help for test/something else\n\n" "Something else"), # case
("test/Something else", "Help for test/something else\n\nSomething else"), # case
(
"test/More", # case
"Help for test/more\n\n"
"Another text\n\n"
"Subtopics:\n"
" test/more/second-more",
"Help for test/more\n\nAnother text\n\nSubtopics:\n test/more/second-more",
),
(
"test/More/Second-more",
@ -264,11 +254,11 @@ class TestHelp(BaseEvenniaCommandTest):
),
(
"test/more/second/more again",
"Help for test/more/second-more/more again\n\n" "Even more text.\n",
"Help for test/more/second-more/more again\n\nEven more text.\n",
),
(
"test/more/second/third",
"Help for test/more/second-more/third more\n\n" "Third more text\n",
"Help for test/more/second-more/third more\n\nThird more text\n",
),
]
)
@ -520,7 +510,7 @@ class TestCmdTasks(BaseEvenniaCommandTest):
def test_misformed_command(self):
wanted_msg = (
"Task command misformed.|Proper format tasks[/switch] " "[function name or task id]"
"Task command misformed.|Proper format tasks[/switch] [function name or task id]"
)
self.call(system.CmdTasks(), f"/cancel", wanted_msg)
@ -557,18 +547,49 @@ class TestAdmin(BaseEvenniaCommandTest):
class TestAccount(BaseEvenniaCommandTest):
def test_ooc_look(self):
if settings.MULTISESSION_MODE < 2:
self.call(
account.CmdOOCLook(), "", "You are out-of-character (OOC).", caller=self.account
)
if settings.MULTISESSION_MODE == 2:
self.call(
account.CmdOOCLook(),
"",
"Account TestAccount (you are OutofCharacter)",
caller=self.account,
)
"""
Test different account-specific modes
"""
@parameterized.expand(
# multisession-mode, auto-puppet, max_nr_characters
[
(0, True, 1, "You are out-of-character"),
(1, True, 1, "You are out-of-character"),
(2, True, 1, "You are out-of-character"),
(3, True, 1, "You are out-of-character"),
(0, False, 1, "Account TestAccount"),
(1, False, 1, "Account TestAccount"),
(2, False, 1, "Account TestAccount"),
(3, False, 1, "Account TestAccount"),
(0, True, 2, "Account TestAccount"),
(1, True, 2, "Account TestAccount"),
(2, True, 2, "Account TestAccount"),
(3, True, 2, "Account TestAccount"),
(0, False, 2, "Account TestAccount"),
(1, False, 2, "Account TestAccount"),
(2, False, 2, "Account TestAccount"),
(3, False, 2, "Account TestAccount"),
]
)
def test_ooc_look(self, multisession_mode, auto_puppet, max_nr_chars, expected_result):
self.account.db._playable_characters = [self.char1]
self.account.unpuppet_all()
with self.settings(MULTISESSION=multisession_mode):
# we need to patch the module header instead of settings
with patch("evennia.commands.default.account._MAX_NR_CHARACTERS", new=max_nr_chars):
with patch(
"evennia.commands.default.account._AUTO_PUPPET_ON_LOGIN", new=auto_puppet
):
self.call(
account.CmdOOCLook(),
"",
expected_result,
caller=self.account,
)
def test_ooc(self):
self.call(account.CmdOOC(), "", "You go OOC.", caller=self.account)
@ -901,7 +922,8 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdSetAttribute(),
"Obj/test2[+'three']",
"Attribute Obj/test2[+'three'] [category:None] does not exist. (Nested lookups attempted)",
"Attribute Obj/test2[+'three'] [category:None] does not exist. (Nested lookups"
" attempted)",
)
self.call(
building.CmdSetAttribute(),
@ -1091,7 +1113,8 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdSetAttribute(),
"Obj/test4[0]['one']",
"Attribute Obj/test4[0]['one'] [category:None] does not exist. (Nested lookups attempted)",
"Attribute Obj/test4[0]['one'] [category:None] does not exist. (Nested lookups"
" attempted)",
)
def test_split_nested_attr(self):
@ -1339,7 +1362,8 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdTypeclass(),
"Obj = evennia.objects.objects.DefaultExit",
"Obj already has the typeclass 'evennia.objects.objects.DefaultExit'. Use /force to override.",
"Obj already has the typeclass 'evennia.objects.objects.DefaultExit'. Use /force to"
" override.",
)
self.call(
building.CmdTypeclass(),
@ -1355,9 +1379,9 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdTypeclass(),
"Obj",
"Obj updated its existing typeclass (evennia.objects.objects.DefaultObject).\n"
"Only the at_object_creation hook was run (update mode). Attributes set before swap were not removed\n"
"(use `swap` or `type/reset` to clear all).",
"Obj updated its existing typeclass (evennia.objects.objects.DefaultObject).\nOnly the"
" at_object_creation hook was run (update mode). Attributes set before swap were not"
" removed\n(use `swap` or `type/reset` to clear all).",
cmdstring="update",
)
self.call(
@ -1560,9 +1584,8 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdTeleport(),
"Obj = Room2",
"Obj(#{}) is leaving Room(#{}), heading for Room2(#{}).|Teleported Obj -> Room2.".format(
oid, rid, rid2
),
"Obj(#{}) is leaving Room(#{}), heading for Room2(#{}).|Teleported Obj -> Room2."
.format(oid, rid, rid2),
)
self.call(building.CmdTeleport(), "NotFound = Room", "Could not find 'NotFound'.")
self.call(
@ -1598,7 +1621,7 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdTag(),
"Obj",
"Tags on Obj: 'testtag', 'testtag2', " "'testtag2' (category: category1), 'testtag3'",
"Tags on Obj: 'testtag', 'testtag2', 'testtag2' (category: category1), 'testtag3'",
)
self.call(building.CmdTag(), "/search NotFound", "No objects found with tag 'NotFound'.")
@ -1654,7 +1677,7 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdSpawn(),
"/save {'key':'Test Char', " "'typeclass':'evennia.objects.objects.DefaultCharacter'}",
"/save {'key':'Test Char', 'typeclass':'evennia.objects.objects.DefaultCharacter'}",
"A prototype_key must be given, either as `prototype_key = <prototype>` or as "
"a key 'prototype_key' inside the prototype structure.",
)
@ -1678,7 +1701,8 @@ class TestBuilding(BaseEvenniaCommandTest):
self.call(
building.CmdSpawn(),
"{'prototype_key':'GOBLIN', 'typeclass':'evennia.objects.objects.DefaultCharacter', "
"'key':'goblin', 'location':'%s'}" % spawnLoc.dbref,
"'key':'goblin', 'location':'%s'}"
% spawnLoc.dbref,
"Spawned goblin",
)
goblin = get_object(self, "goblin")
@ -1725,7 +1749,8 @@ class TestBuilding(BaseEvenniaCommandTest):
# Location should be the specified location.
self.call(
building.CmdSpawn(),
"/noloc {'prototype_parent':'TESTBALL', 'key': 'Ball', 'prototype_key': 'foo', 'location':'%s'}"
"/noloc {'prototype_parent':'TESTBALL', 'key': 'Ball', 'prototype_key': 'foo',"
" 'location':'%s'}"
% spawnLoc.dbref,
"Spawned Ball",
)
@ -1785,8 +1810,8 @@ class TestBuilding(BaseEvenniaCommandTest):
import evennia.commands.default.comms as cmd_comms # noqa
from evennia.utils.create import create_channel # noqa
from evennia.comms.comms import DefaultChannel # noqa
from evennia.utils.create import create_channel # noqa
@patch("evennia.commands.default.comms.CHANNEL_DEFAULT_TYPECLASS", DefaultChannel)
@ -1986,7 +2011,8 @@ class TestBatchProcess(BaseEvenniaCommandTest):
self.call(
batchprocess.CmdBatchCommands(),
"batchprocessor.example_batch_cmds",
"Running Batch-command processor - Automatic mode for batchprocessor.example_batch_cmds",
"Running Batch-command processor - Automatic mode for"
" batchprocessor.example_batch_cmds",
)
# we make sure to delete the button again here to stop the running reactor
confirm = building.CmdDestroy.confirm
@ -2018,7 +2044,8 @@ class TestUnconnectedCommand(BaseEvenniaCommandTest):
# instead of using SERVER_START_TIME (0), we use 86400 because Windows won't let us use anything lower
gametime.SERVER_START_TIME = 86400
expected = (
"## BEGIN INFO 1.1\nName: %s\nUptime: %s\nConnected: %d\nVersion: Evennia %s\n## END INFO"
"## BEGIN INFO 1.1\nName: %s\nUptime: %s\nConnected: %d\nVersion: Evennia %s\n## END"
" INFO"
% (
settings.SERVERNAME,
datetime.datetime.fromtimestamp(gametime.SERVER_START_TIME).ctime(),

View file

@ -1,15 +1,16 @@
"""
Commands that are available from the connect screen.
"""
import re
import datetime
import re
from codecs import lookup as codecs_lookup
from django.conf import settings
from evennia.commands.cmdhandler import CMD_LOGINSTART
from evennia.comms.models import ChannelDB
from evennia.server.sessionhandler import SESSIONS
from evennia.utils import class_from_module, create, logger, utils, gametime
from evennia.commands.cmdhandler import CMD_LOGINSTART
from evennia.utils import class_from_module, create, gametime, logger, utils
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -25,7 +26,6 @@ __all__ = (
"CmdUnconnectedScreenreader",
)
MULTISESSION_MODE = settings.MULTISESSION_MODE
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
@ -215,7 +215,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
session.msg("Aborted. If your user name contains spaces, surround it by quotes.")
return
# everything's ok. Create the new account account.
# everything's ok. Create the new player account.
account, errors = Account.create(
username=username, password=password, ip=address, session=session
)
@ -447,7 +447,8 @@ class CmdUnconnectedInfo(COMMAND_DEFAULT_CLASS):
def func(self):
self.caller.msg(
"## BEGIN INFO 1.1\nName: %s\nUptime: %s\nConnected: %d\nVersion: Evennia %s\n## END INFO"
"## BEGIN INFO 1.1\nName: %s\nUptime: %s\nConnected: %d\nVersion: Evennia %s\n## END"
" INFO"
% (
settings.SERVERNAME,
datetime.datetime.fromtimestamp(gametime.SERVER_START_TIME).ctime(),
@ -468,8 +469,8 @@ def _create_account(session, accountname, password, permissions, typeclass=None,
except Exception as e:
session.msg(
"There was an error creating the Account:\n%s\n If this problem persists, contact an admin."
% e
"There was an error creating the Account:\n%s\n If this problem persists, contact an"
" admin." % e
)
logger.log_trace()
return False
@ -490,7 +491,7 @@ def _create_account(session, accountname, password, permissions, typeclass=None,
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 MULTISESSION_MODE < 2 situations.
This is meant for Guest and AUTO_CREATRE_CHARACTER_WITH_ACCOUNT=True situations.
"""
try:
new_character = create.create_object(
@ -512,7 +513,7 @@ def _create_character(session, new_account, typeclass, home, permissions):
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
"There was an error creating the Character:\n%s\n If this problem persists, contact an"
" admin." % e
)
logger.log_trace()