This commit is contained in:
Jake 2026-03-05 00:10:35 -08:00 committed by GitHub
commit e0bf8fc8c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 57 additions and 39 deletions

View file

@ -250,7 +250,9 @@ def _progressive_cmd_run(cmd, generator, response=None):
if isinstance(value, (int, float)):
utils.delay(value, _progressive_cmd_run, cmd, generator)
elif isinstance(value, str):
_GET_INPUT(cmd.caller, value, _process_input, cmd=cmd, generator=generator)
_GET_INPUT(
cmd.caller, value, _process_input, session=cmd.session, cmd=cmd, generator=generator
)
else:
raise ValueError("unknown type for a yielded value in command: {}".format(type(value)))
@ -698,7 +700,10 @@ def cmdhandler(
# Parse the input string and match to available cmdset.
# This also checks for permissions, so all commands in match
# are commands the caller is allowed to call.
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)
try:
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller, session=session)
except TypeError:
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)
# Deal with matches

View file

@ -62,7 +62,6 @@ def build_matches(raw_string, cmdset, include_prefixes=False):
"""
matches = []
try:
orig_string = raw_string
if not include_prefixes and len(raw_string) > 1:
raw_string = raw_string.lstrip(_CMD_IGNORE_PREFIXES)
search_string = raw_string.lower()
@ -111,7 +110,7 @@ def try_num_differentiators(raw_string):
return None, None
def cmdparser(raw_string, cmdset, caller, match_index=None):
def cmdparser(raw_string, cmdset, caller, match_index=None, session=None, **kwargs):
"""
This function is called by the cmdhandler once it has
gathered and merged all valid cmdsets valid for this particular parsing.
@ -166,7 +165,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
matches = build_matches(raw_string, cmdset, include_prefixes=False)
# only select command matches we are actually allowed to call.
matches = [match for match in matches if match[2].access(caller, "cmd")]
matches = [match for match in matches if match[2].access(caller, "cmd", session=session)]
# try to bring the number of matches down to 1
if len(matches) > 1:

View file

@ -16,7 +16,7 @@ from django.utils.text import slugify
from evennia.locks.lockhandler import LockHandler
from evennia.utils.ansi import ANSIString
from evennia.utils.evtable import EvTable
from evennia.utils.utils import fill, is_iter, lazy_property, make_iter
from evennia.utils.utils import is_iter, lazy_property, make_iter
CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES
_RE_CMD_LOCKFUNC_IN_LOCKSTRING = re.compile(r"(^|;|\s)cmd\:\w+", re.DOTALL)
@ -383,7 +383,7 @@ class Command(metaclass=CommandMeta):
return k, v
return None, None
def access(self, srcobj, access_type="cmd", default=False):
def access(self, srcobj, access_type="cmd", default=False, session=None):
"""
This hook is called by the cmdhandler to determine if srcobj
is allowed to execute this command. It should return a boolean
@ -395,9 +395,10 @@ class Command(metaclass=CommandMeta):
access_type (str, optional): The lock type to check.
default (bool, optional): The fallback result if no lock
of matching `access_type` is found on this Command.
session (Session, optional): The session to pass to lock functions.
"""
return self.lockhandler.check(srcobj, access_type, default=default)
return self.lockhandler.check(srcobj, access_type, default=default, session=session)
def msg(self, text=None, to_obj=None, from_obj=None, session=None, **kwargs):
"""
@ -595,7 +596,7 @@ Command \"{cmdname}\" has no defined `func()` method. Available properties on th
"help-entry-detail",
kwargs={"category": slugify(self.help_category), "topic": slugify(self.key)},
)
except Exception as e:
except Exception:
return "#"
def web_get_admin_url(self):

View file

@ -421,7 +421,9 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
cmdset.make_unique(caller)
# retrieve all available commands and database / file-help topics.
# also check the 'cmd:' lock here
cmd_help_topics = [cmd for cmd in cmdset if cmd and cmd.access(caller, "cmd")]
cmd_help_topics = [
cmd for cmd in cmdset if cmd and cmd.access(caller, "cmd", session=self.session)
]
# get all file-based help entries, checking perms
file_help_topics = {topic.key.lower().strip(): topic for topic in FILE_HELP_ENTRIES.all()}
# get db-based help entries, checking perms

View file

@ -32,7 +32,7 @@ your settings file:
"""
def cmdparser(raw_string, cmdset, caller, match_index=None):
def cmdparser(raw_string, cmdset, caller, match_index=None, **kwargs):
"""
This function is called by the cmdhandler once it has
gathered and merged all valid cmdsets valid for this particular parsing.

View file

@ -518,14 +518,13 @@ def is_ooc(accessing_obj, accessed_obj, *args, **kwargs):
account = obj.account if utils.inherits_from(obj, evennia.DefaultObject) else obj
if not account:
return True
try:
session = accessed_obj.session
except AttributeError:
# note-this doesn't work well
# for high multisession mode. We may need
# to change to sessiondb to resolve this
sessions = session = account.sessions.get()
session = sessions[0] if sessions else None
session = kwargs.get("session", None)
if session is None:
try:
session = accessed_obj.session
except AttributeError:
sessions = account.sessions.get()
session = sessions[0] if sessions else None
if not session:
# this suggests we are not even logged in; treat as ooc.
return True
@ -535,20 +534,6 @@ def is_ooc(accessing_obj, accessed_obj, *args, **kwargs):
return not session.get_puppet()
def objtag(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
objtag(tagkey)
objtag(tagkey, category)
Only true if accessed_obj has the specified tag and optional
category.
"""
tagkey = args[0] if args else None
category = args[1] if len(args) > 1 else None
return bool(accessed_obj.tags.get(tagkey, category=category))
def inside(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:

View file

@ -363,7 +363,7 @@ class LockHandler:
access_type, rhs = [part.strip() for part in lockdef.split(":", 1)]
if not access_type:
err = _(
"Lock: '{lockdef}' has no access_type " "(left-side of colon is empty)."
"Lock: '{lockdef}' has no access_type (left-side of colon is empty)."
).format(lockdef=lockdef)
if validate_only:
return False, err
@ -514,13 +514,15 @@ class LockHandler:
"""
old_lockstring = self.get(access_type)
if not lockstring.strip().lower() in old_lockstring.lower():
if lockstring.strip().lower() not in old_lockstring.lower():
lockstring = "{old} {op} {new}".format(
old=old_lockstring, op=op, new=lockstring.strip()
)
self.add(lockstring)
def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False):
def check(
self, accessing_obj, access_type, default=False, no_superuser_bypass=False, session=None
):
"""
Checks a lock of the correct type by passing execution off to
the lock function(s).
@ -580,7 +582,16 @@ class LockHandler:
evalstring, func_tup, raw_string = self.locks[access_type]
# execute all lock funcs in the correct order, producing a tuple of True/False results.
true_false = tuple(
bool(tup[0](accessing_obj, self.obj, *tup[1], access_type=access_type, **tup[2]))
bool(
tup[0](
accessing_obj,
self.obj,
*tup[1],
access_type=access_type,
session=session,
**tup[2],
)
)
for tup in func_tup
)
# the True/False tuple goes into evalstring, which combines them

View file

@ -7,13 +7,15 @@ the stability and integrity of the codebase during updates.
This module tests the lock functionality of Evennia.
"""
from evennia.server.serversession import ServerSession
from evennia.utils.test_resources import BaseEvenniaTest
try:
# this is a special optimized Django version, only available in current Django devel
from django.utils.unittest import TestCase, override_settings
from django.utils.unittest import override_settings
except ImportError:
from django.test import TestCase, override_settings
from django.test import override_settings
from evennia import settings_default
from evennia.locks import lockfuncs
@ -220,6 +222,19 @@ class TestLockfuncs(BaseEvenniaTest):
self.account.unpuppet_all()
self.assertEqual(True, lockfuncs.is_ooc(self.account, self.char1))
def test_is_ooc__multi_session_mixed(self):
"""Test that two sessions on the same account can have different IC/OOC states."""
ic_session = self.session
# Create a bare unpuppeted session. Full login flow steals the puppet from ic_session in
# MULTISESSION_MODE 0.
ooc_session = ServerSession()
ooc_session.sessid = 2
ooc_session.puppet = None
self.assertFalse(lockfuncs.is_ooc(self.account, self.char1, session=ic_session))
self.assertTrue(lockfuncs.is_ooc(self.account, self.char1, session=ooc_session))
class TestPermissionCheck(BaseEvenniaTest):
"""