Rework at_before_say/at_say to handle whispers and using kwargs.

This commit is contained in:
Griatch 2017-07-21 12:17:47 +02:00
commit c43059c75f
153 changed files with 4418 additions and 2931 deletions

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
This sub-package contains Evennia's command system. It handles
everything related to parsing input from the player, building cmdsets
everything related to parsing input from the account, building cmdsets
and executing the code associated with a found command class.
commands.default contains all the default "mux-like" commands of

View file

@ -13,7 +13,7 @@ command line. The processing of a command works as follows:
- object cmdsets: all objects at caller's location are scanned for non-empty
cmdsets. This includes cmdsets on exits.
- caller: the caller is searched for its own currently active cmdset.
- player: lastly the cmdsets defined on caller.player are added.
- account: lastly the cmdsets defined on caller.account are added.
3. The collected cmdsets are merged together to a combined, current cmdset.
4. If the input string is empty -> check for CMD_NOINPUT command in
current cmdset or fallback to error message. Exit.
@ -85,7 +85,7 @@ CMD_LOGINSTART = "__unloggedin_look_command"
_SEARCH_AT_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
# Output strings. The first is the IN_GAME_ERRORS return, the second
# is the normal "production message to echo to the player.
# is the normal "production message to echo to the account.
_ERROR_UNTRAPPED = (
"""
@ -128,7 +128,7 @@ likely file a bug report with the Evennia project.
""")
_ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \
"reached for '{raw_string}' ({cmdclass})."
"reached for '{raw_cmdname}' ({cmdclass})."
# delayed imports
@ -210,7 +210,7 @@ def _process_input(caller, prompt, result, cmd, generator):
part of yielding from a Command's `func`.
Args:
caller (Character, Player or Session): the caller.
caller (Character, Account or Session): the caller.
prompt (basestring): The sent prompt.
result (basestring): The unprocessed answer.
cmd (Command): The command itself.
@ -248,20 +248,20 @@ class ErrorReported(Exception):
# Helper function
@inlineCallbacks
def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string):
"""
Gather all relevant cmdsets and merge them.
Args:
caller (Session, Player or Object): The entity executing the command. Which
caller (Session, Account or Object): The entity executing the command. Which
type of object this is depends on the current game state; for example
when the user is not logged in, this will be a Session, when being OOC
it will be a Player and when puppeting an object this will (often) be
it will be an Account and when puppeting an object this will (often) be
a Character Object. In the end it depends on where the cmdset is stored.
session (Session or None): The Session associated with caller, if any.
player (Player or None): The calling Player associated with caller, if any.
account (Account or None): The calling Account associated with caller, if any.
obj (Object or None): The Object associated with caller, if any.
callertype (str): This identifies caller as either "player", "object" or "session"
callertype (str): This identifies caller as either "account", "object" or "session"
to avoid having to do this check internally.
raw_string (str): The input string. This is only used for error reporting.
@ -272,18 +272,18 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
Notes:
The cdmsets are merged in order or generality, so that the
Object's cmdset is merged last (and will thus take precedence
over same-named and same-prio commands on Player and Session).
over same-named and same-prio commands on Account and Session).
"""
try:
@inlineCallbacks
def _get_channel_cmdset(player_or_obj):
def _get_channel_cmdset(account_or_obj):
"""
Helper-method; Get channel-cmdsets
"""
# Create cmdset for all player's available channels
# Create cmdset for all account's available channels
try:
channel_cmdset = yield CHANNELHANDLER.get_cmdset(player_or_obj)
channel_cmdset = yield CHANNELHANDLER.get_cmdset(account_or_obj)
returnValue([channel_cmdset])
except Exception:
_msg_err(caller, _ERROR_CMDSETS)
@ -313,8 +313,8 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
_GA(lobj, "at_cmdset_get")(caller=caller)
except Exception:
logger.log_trace()
# the call-type lock is checked here, it makes sure a player
# is not seeing e.g. the commands on a fellow player (which is why
# the call-type lock is checked here, it makes sure an account
# is not seeing e.g. the commands on a fellow account (which is why
# the no_superuser_bypass must be True)
local_obj_cmdsets = \
yield list(chain.from_iterable(
@ -355,9 +355,9 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
# we are calling the command from the session level
report_to = session
current, cmdsets = yield _get_cmdsets(session)
if player: # this automatically implies logged-in
pcurrent, player_cmdsets = yield _get_cmdsets(player)
cmdsets += player_cmdsets
if account: # this automatically implies logged-in
pcurrent, account_cmdsets = yield _get_cmdsets(account)
cmdsets += account_cmdsets
current = current + pcurrent
if obj:
ocurrent, obj_cmdsets = yield _get_cmdsets(obj)
@ -374,13 +374,13 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
channel_cmdsets = yield _get_channel_cmdset(obj)
cmdsets += channel_cmdsets
if not current.no_channels:
channel_cmdsets = yield _get_channel_cmdset(player)
channel_cmdsets = yield _get_channel_cmdset(account)
cmdsets += channel_cmdsets
elif callertype == "player":
# we are calling the command from the player level
report_to = player
current, cmdsets = yield _get_cmdsets(player)
elif callertype == "account":
# we are calling the command from the account level
report_to = account
current, cmdsets = yield _get_cmdsets(account)
if obj:
ocurrent, obj_cmdsets = yield _get_cmdsets(obj)
current = current + ocurrent
@ -395,7 +395,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
# also objs may have channels
cmdsets += yield _get_channel_cmdset(obj)
if not current.no_channels:
cmdsets += yield _get_channel_cmdset(player)
cmdsets += yield _get_channel_cmdset(account)
elif callertype == "object":
# we are calling the command from the object level
@ -472,22 +472,22 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
This is the main mechanism that handles any string sent to the engine.
Args:
called_by (Session, Player or Object): Object from which this
called_by (Session, Account or Object): Object from which this
command was called. which this was called from. What this is
depends on the game state.
raw_string (str): The command string as given on the command line.
_testing (bool, optional): Used for debug purposes and decides if we
should actually execute the command or not. If True, the
command instance will be returned.
callertype (str, optional): One of "session", "player" or
callertype (str, optional): One of "session", "account" or
"object". These are treated in decending order, so when the
Session is the caller, it will merge its own cmdset into
cmdsets from both Player and eventual puppeted Object (and
cmdsets in its room etc). A Player will only include its own
cmdsets from both Account and eventual puppeted Object (and
cmdsets in its room etc). An Account will only include its own
cmdset and the Objects and so on. Merge order is the same
order, so that Object cmdsets are merged in last, giving them
precendence for same-name and same-prio commands.
session (Session, optional): Relevant if callertype is "player" - the session will help
session (Session, optional): Relevant if callertype is "account" - the session will help
retrieve the correct cmdsets from puppeted objects.
cmdobj (Command, optional): If given a command instance, this will be executed using
`called_by` as the caller, `raw_string` representing its arguments and (optionally)
@ -513,20 +513,22 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
"""
@inlineCallbacks
def _run_command(cmd, cmdname, args, raw_string, cmdset, session, player):
def _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, account):
"""
Helper function: This initializes and runs the Command
instance once the parser has identified it as either a normal
command or one of the system commands.
Args:
cmd (Command): Command object.
cmdname (str): Name of command.
args (str): Extra text entered after the identified command.
raw_string (str): Full input string.
cmd (Command): Command object
cmdname (str): Name of command
args (str): extra text entered after the identified command
raw_cmdname (str): Name of Command, unaffected by eventual
prefix-stripping (if no prefix-stripping, this is the same
as cmdname).
cmdset (CmdSet): Command sert the command belongs to (if any)..
session (Session): Session of caller (if any).
player (Player): Player of caller (if any).
account (Account): Account of caller (if any).
Returns:
deferred (Deferred): this will fire with the return of the
@ -540,12 +542,14 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
try:
# Assign useful variables to the instance
cmd.caller = caller
cmd.cmdstring = cmdname
cmd.cmdname = cmdname
cmd.raw_cmdname = raw_cmdname
cmd.cmdstring = cmdname # deprecated
cmd.args = args
cmd.cmdset = cmdset
cmd.session = session
cmd.player = player
cmd.raw_string = raw_string
cmd.account = account
cmd.raw_string = unformatted_raw_string
#cmd.obj # set via on-object cmdset handler for each command,
# since this may be different for every command when
# merging multuple cmdsets
@ -566,7 +570,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
_COMMAND_NESTING[called_by] += 1
if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
err = _ERROR_RECURSION_LIMIT.format(recursion_limit=_COMMAND_RECURSION_LIMIT,
raw_string=raw_string,
raw_cmdname=raw_cmdname,
cmdclass=cmd.__class__)
raise RuntimeError(err)
@ -614,13 +618,13 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
raw_string = to_unicode(raw_string, force_string=True)
session, player, obj = session, None, None
session, account, obj = session, None, None
if callertype == "session":
session = called_by
player = session.player
account = session.account
obj = session.puppet
elif callertype == "player":
player = called_by
elif callertype == "account":
account = called_by
if session:
obj = yield session.puppet
elif callertype == "object":
@ -629,32 +633,32 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype)
# the caller will be the one to receive messages and excert its permissions.
# we assign the caller with preference 'bottom up'
caller = obj or player or session
# The error_to is the default recipient for errors. Tries to make sure a player
caller = obj or account or session
# The error_to is the default recipient for errors. Tries to make sure an account
# does not get spammed for errors while preserving character mirroring.
error_to = obj or session or player
error_to = obj or session or account
try: # catch bugs in cmdhandler itself
try: # catch special-type commands
if cmdobj:
# the command object is already given
cmd = cmdobj() if callable(cmdobj) else cmdobj
cmdname = cmdobj_key if cmdobj_key else cmd.key
args = raw_string
unformatted_raw_string = "%s%s" % (cmdname, args)
cmdset = None
session = session
player = player
account = account
else:
# no explicit cmdobject given, figure it out
cmdset = yield get_and_merge_cmdsets(caller, session, player, obj,
cmdset = yield get_and_merge_cmdsets(caller, session, account, obj,
callertype, raw_string)
if not cmdset:
# this is bad and shouldn't happen.
raise NoCmdSets
# store the completely unmodified raw string - including
# whitespace and eventual prefixes-to-be-stripped.
unformatted_raw_string = raw_string
raw_string = raw_string.strip()
if not raw_string:
@ -685,7 +689,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
if len(matches) == 1:
# We have a unique command match. But it may still be invalid.
match = matches[0]
cmdname, args, cmd = match[0], match[1], match[2]
cmdname, args, cmd, raw_cmdname = match[0], match[1], match[2], match[5]
if not matches:
# No commands match our entered command
@ -718,7 +722,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
raise ExecSystemCommand(cmd, sysarg)
# A normal command.
ret = yield _run_command(cmd, cmdname, args, unformatted_raw_string, cmdset, session, player)
ret = yield _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, account)
returnValue(ret)
except ErrorReported as exc:
@ -734,7 +738,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
if syscmd:
ret = yield _run_command(syscmd, syscmd.key, sysarg,
unformatted_raw_string, cmdset, session, player)
unformatted_raw_string, cmdset, session, account)
returnValue(ret)
elif sysarg:
# return system arg

View file

@ -12,6 +12,7 @@ from django.conf import settings
from evennia.utils.logger import log_trace
_MULTIMATCH_REGEX = re.compile(settings.SEARCH_MULTIMATCH_REGEX, re.I + re.U)
_CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES
def cmdparser(raw_string, cmdset, caller, match_index=None):
"""
@ -21,7 +22,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
Args:
raw_string (str): The unparsed text entered by the caller.
cmdset (CmdSet): The merged, currently valid cmdset
caller (Session, Player or Object): The caller triggering this parsing.
caller (Session, Account or Object): The caller triggering this parsing.
match_index (int, optional): Index to pick a given match in a
list of same-named command matches. If this is given, it suggests
this is not the first time this function was called: normally
@ -46,7 +47,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
"""
def create_match(cmdname, string, cmdobj):
def create_match(cmdname, string, cmdobj, raw_cmdname):
"""
Builds a command match by splitting the incoming string and
evaluating the quality of the match.
@ -55,51 +56,79 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
cmdname (str): Name of command to check for.
string (str): The string to match against.
cmdobj (str): The full Command instance.
raw_cmdname (str, optional): If CMD_IGNORE_PREFIX is set and the cmdname starts with
one of the prefixes to ignore, this contains the raw, unstripped cmdname,
otherwise it is None.
Returns:
match (tuple): This is on the form (cmdname, args, cmdobj, cmdlen, mratio), where
match (tuple): This is on the form (cmdname, args, cmdobj, cmdlen, mratio, raw_cmdname), where
`cmdname` is the command's name and `args` the rest of the incoming string,
without said command name. `cmdobj` is the Command instance, the cmdlen is
the same as len(cmdname) and mratio is a measure of how big a part of the
full input string the cmdname takes up - an exact match would be 1.0.
full input string the cmdname takes up - an exact match would be 1.0. Finally,
the `raw_cmdname` is the cmdname unmodified by eventual prefix-stripping.
"""
cmdlen, strlen = len(unicode(cmdname)), len(unicode(string))
mratio = 1 - (strlen - cmdlen) / (1.0 * strlen)
args = string[cmdlen:]
return (cmdname, args, cmdobj, cmdlen, mratio)
return (cmdname, args, cmdobj, cmdlen, mratio, raw_cmdname)
def build_matches(raw_string, include_prefixes=False):
l_raw_string = raw_string.lower()
matches = []
try:
if include_prefixes:
# use the cmdname as-is
for cmd in cmdset:
matches.extend([create_match(cmdname, raw_string, cmd, cmdname)
for cmdname in [cmd.key] + cmd.aliases
if cmdname and l_raw_string.startswith(cmdname.lower())
and (not cmd.arg_regex or
cmd.arg_regex.match(l_raw_string[len(cmdname):]))])
else:
# strip prefixes set in settings
for cmd in cmdset:
for raw_cmdname in [cmd.key] + cmd.aliases:
cmdname = raw_cmdname.lstrip(_CMD_IGNORE_PREFIXES) if len(raw_cmdname) > 1 else raw_cmdname
if cmdname and l_raw_string.startswith(cmdname.lower()) and \
(not cmd.arg_regex or cmd.arg_regex.match(l_raw_string[len(cmdname):])):
matches.append(create_match(cmdname, raw_string, cmd, raw_cmdname))
except Exception:
log_trace("cmdhandler error. raw_input:%s" % raw_string)
return matches
def try_num_prefixes(raw_string):
if not matches:
# no matches found
num_ref_match = _MULTIMATCH_REGEX.match(raw_string)
if num_ref_match:
# the user might be trying to identify the command
# with a #num-command style syntax. We expect the regex to
# contain the groups "number" and "name".
mindex, new_raw_string = num_ref_match.group("number"), num_ref_match.group("name")
return mindex, new_raw_string
return None, None
if not raw_string:
return []
matches = []
# match everything that begins with a matching cmdname.
l_raw_string = raw_string.lower()
for cmd in cmdset:
try:
matches.extend([create_match(cmdname, raw_string, cmd)
for cmdname in [cmd.key] + cmd.aliases
if cmdname and l_raw_string.startswith(cmdname.lower())
and (not cmd.arg_regex or
cmd.arg_regex.match(l_raw_string[len(cmdname):]))])
except Exception:
log_trace("cmdhandler error. raw_input:%s" % raw_string)
# find mathces, first using the full name
matches = build_matches(raw_string, include_prefixes=True)
if not matches:
# no matches found
num_ref_match = _MULTIMATCH_REGEX.match(raw_string)
if num_ref_match:
# the user might be trying to identify the command
# with a #num-command style syntax. We expect the regex to
# contain the groups "number" and "name".
mindex, new_raw_string = num_ref_match.group("number"), num_ref_match.group("name")
return cmdparser(new_raw_string, cmdset,
caller, match_index=int(mindex))
# try to match a number 1-cmdname, 2-cmdname etc
mindex, new_raw_string = try_num_prefixes(raw_string)
if mindex is not None:
return cmdparser(new_raw_string, cmdset, caller, match_index=int(mindex))
if _CMD_IGNORE_PREFIXES:
# still no match. Try to strip prefixes
raw_string = raw_string.lstrip(_CMD_IGNORE_PREFIXES) if len(raw_string) > 1 else raw_string
matches = build_matches(raw_string, 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')]
# try to bring the number of matches down to 1
if len(matches) > 1:
# See if it helps to analyze the match with preserved case but only if
# it leaves at least one match.
@ -129,4 +158,3 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
# no matter what we have at this point, we have to return it.
return matches

View file

@ -4,7 +4,7 @@ A Command Set (CmdSet) holds a set of commands. The Cmdsets can be
merged and combined to create new sets of commands in a
non-destructive way. This makes them very powerful for implementing
custom game states where different commands (or different variations
of commands) are available to the players depending on circumstance.
of commands) are available to the accounts depending on circumstance.
The available merge operations are partly borrowed from mathematical
Set theory.
@ -110,9 +110,9 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
merger (i.e. A above) automatically taking
precedence. But if allow_duplicates is true, the
result will be a merger with more than one of each
name match. This will usually lead to the player
name match. This will usually lead to the account
receiving a multiple-match error higher up the road,
but can be good for things like cmdsets on non-player
but can be good for things like cmdsets on non-account
objects in a room, to allow the system to warn that
more than one 'ball' in the room has the same 'kick'
command defined on it, so it may offer a chance to
@ -134,7 +134,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
commands
no_channels - ignore the name of channels when matching against
commands (WARNING- this is dangerous since the
player can then not even ask staff for help if
account can then not even ask staff for help if
something goes wrong)
@ -167,9 +167,9 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
Creates a new CmdSet instance.
Args:
cmdsetobj (Session, Player, Object, optional): This is the database object
cmdsetobj (Session, Account, Object, optional): This is the database object
to which this particular instance of cmdset is related. It
is often a character but may also be a regular object, Player
is often a character but may also be a regular object, Account
or Session.
key (str, optional): The idenfier for this cmdset. This
helps if wanting to selectively remov cmdsets.

View file

@ -9,8 +9,8 @@ intelligent container that, when added to other CmdSet make sure that
same-name commands are treated correctly (usually so there are no
doublets). This temporary but up-to-date merger of CmdSet is jointly
called the Current Cmset. It is this Current CmdSet that the
commandhandler looks through whenever a player enters a command (it
also adds CmdSets from objects in the room in real-time). All player
commandhandler looks through whenever an account enters a command (it
also adds CmdSets from objects in the room in real-time). All account
objects have a 'default cmdset' containing all the normal in-game mud
commands (look etc).
@ -19,12 +19,12 @@ So what is all this cmdset complexity good for?
In its simplest form, a CmdSet has no commands, only a key name. In
this case the cmdset's use is up to each individual game - it can be
used by an AI module for example (mobs in cmdset 'roam' move from room
to room, in cmdset 'attack' they enter combat with players).
to room, in cmdset 'attack' they enter combat with accounts).
Defining commands in cmdsets offer some further powerful game-design
consequences however. Here are some examples:
As mentioned above, all players always have at least the Default
As mentioned above, all accounts always have at least the Default
CmdSet. This contains the set of all normal-use commands in-game,
stuff like look and @desc etc. Now assume our players end up in a dark
room. You don't want the player to be able to do much in that dark
@ -37,7 +37,7 @@ and have this completely replace the default cmdset.
Another example: Say you want your players to be able to go
fishing. You could implement this as a 'fish' command that fails
whenever the player has no fishing rod. Easy enough. But what if you
whenever the account has no fishing rod. Easy enough. But what if you
want to make fishing more complex - maybe you want four-five different
commands for throwing your line, reeling in, etc? Most players won't
(we assume) have fishing gear, and having all those detailed commands
@ -48,7 +48,7 @@ for a minor thing like fishing?
So instead you put all those detailed fishing commands into their own
CommandSet called 'Fishing'. Whenever the player gives the command
'fish' (presumably the code checks there is also water nearby), only
THEN this CommandSet is added to the Cmdhandler of the player. The
THEN this CommandSet is added to the Cmdhandler of the account. The
'throw' command (which normally throws rocks) is replaced by the
custom 'fishing variant' of throw. What has happened is that the
Fishing CommandSet was merged on top of the Default ones, and due to
@ -128,7 +128,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
Args:
path (str): The path to the command set to load.
cmdsetobj (CmdSet): The database object/typeclass on which this cmdset is to be
assigned (this can be also channels and exits, as well as players
assigned (this can be also channels and exits, as well as accounts
but there will always be such an object)
emit_to_obj (Object, optional): If given, error is emitted to
this object (in addition to logging)

View file

@ -152,14 +152,14 @@ class Command(with_metaclass(CommandMeta, object)):
is_exit = False
# define the command not only by key but by the regex form of its arguments
arg_regex = settings.COMMAND_DEFAULT_ARG_REGEX
# whether self.msg sends to all sessions of a related player/object (default
# whether self.msg sends to all sessions of a related account/object (default
# is to only send to the session sending the command).
msg_all_sessions = settings.COMMAND_DEFAULT_MSG_ALL_SESSIONS
# auto-set (by Evennia on command instantiation) are:
# obj - which object this command is defined on
# session - which session is responsible for triggering this command. Only set
# if triggered by a player.
# if triggered by an account.
def __init__(self, **kwargs):
"""
@ -307,7 +307,7 @@ class Command(with_metaclass(CommandMeta, object)):
session=None, **kwargs):
"""
This is a shortcut instead of calling msg() directly on an
object - it will detect if caller is an Object or a Player and
object - it will detect if caller is an Object or an Account and
also appends self.session automatically if self.msg_all_sessions is False.
Args:
@ -340,7 +340,7 @@ class Command(with_metaclass(CommandMeta, object)):
Args:
raw_string (str): Execute this string as a command input.
session (Session, optional): If not given, the current command's Session will be used.
obj (Object or Player, optional): Object or Player on which to call the execute_cmd.
obj (Object or Account, optional): Object or Account on which to call the execute_cmd.
If not given, self.caller will be used.
Kwargs:
@ -443,7 +443,7 @@ class Command(with_metaclass(CommandMeta, object)):
commands the caller can use.
Args:
caller (Object or Player): the caller asking for help on the command.
caller (Object or Account): the caller asking for help on the command.
cmdset (CmdSet): the command set (if you need additional commands).
Returns:

View file

@ -1,19 +1,19 @@
"""
Player (OOC) commands. These are stored on the Player object
and self.caller is thus always a Player, not an Object/Character.
Account (OOC) commands. These are stored on the Account object
and self.caller is thus always an Account, not an Object/Character.
These commands go in the PlayerCmdset and are accessible also
These commands go in the AccountCmdset and are accessible also
when puppeting a Character (although with lower priority)
These commands use the player_caller property which tells the command
These commands use the account_caller property which tells the command
parent (MuxCommand, usually) to setup caller correctly. They use
self.player to make sure to always use the player object rather than
self.account to make sure to always use the account object rather than
self.caller (which change depending on the level you are calling from)
The property self.character can be used to access the character when
these commands are triggered with a connected character (such as the
case of the @ooc command), it is None if we are OOC.
Note that under MULTISESSION_MODE > 2, Player commands should use
Note that under MULTISESSION_MODE > 2, Account commands should use
self.msg() and similar methods to reroute returns to the correct
method. Otherwise all text will be returned to all connected sessions.
@ -36,7 +36,7 @@ __all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit",
"CmdColorTest", "CmdQuell")
class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
class MuxAccountLookCommand(COMMAND_DEFAULT_CLASS):
"""
Custom parent (only) parsing for OOC looking, sets a "playable"
property on the command based on the parsing.
@ -46,19 +46,19 @@ class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
def parse(self):
"""Custom parsing"""
super(MuxPlayerLookCommand, self).parse()
super(MuxAccountLookCommand, self).parse()
if _MULTISESSION_MODE < 2:
# only one character allowed - not used in this mode
self.playable = None
return
playable = self.player.db._playable_characters
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.player.db._playable_characters = playable
self.account.db._playable_characters = playable
# store playable property
if self.args:
self.playable = dict((utils.to_str(char.key.lower()), char)
@ -67,13 +67,13 @@ class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
self.playable = playable
# Obs - these are all intended to be stored on the Player, and as such,
# use self.player instead of self.caller, just to be sure. Also self.msg()
# Obs - these are all intended to be stored on the Account, and as such,
# use self.account instead of self.caller, just to be sure. Also self.msg()
# is used to make sure returns go to the right session
# note that this is inheriting from MuxPlayerLookCommand,
# note that this is inheriting from MuxAccountLookCommand,
# and has the .playable property.
class CmdOOCLook(MuxPlayerLookCommand):
class CmdOOCLook(MuxAccountLookCommand):
"""
look while out-of-character
@ -84,7 +84,7 @@ class CmdOOCLook(MuxPlayerLookCommand):
"""
# This is an OOC version of the look command. Since a
# Player doesn't have an in-game existence, there is no
# Account doesn't have an in-game existence, there is no
# concept of location or "self". If we are controlling
# a character, pass control over to normal look.
@ -94,7 +94,7 @@ class CmdOOCLook(MuxPlayerLookCommand):
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""implement the ooc look command"""
@ -104,8 +104,8 @@ class CmdOOCLook(MuxPlayerLookCommand):
self.msg("You are out-of-character (OOC).\nUse |w@ic|n to get back into the game.")
return
# call on-player look helper method
self.msg(self.player.at_look(target=self.playable, session=self.session))
# call on-account look helper method
self.msg(self.account.at_look(target=self.playable, session=self.session))
class CmdCharCreate(COMMAND_DEFAULT_CLASS):
@ -121,15 +121,15 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
if you want.
"""
key = "@charcreate"
locks = "cmd:pperm(Players)"
locks = "cmd:pperm(Account)"
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""create the new character"""
player = self.player
account = self.account
if not self.args:
self.msg("Usage: @charcreate <charname> [= description]")
return
@ -138,9 +138,9 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1
if not player.is_superuser and \
(player.db._playable_characters and
len(player.db._playable_characters) >= charmax):
if not account.is_superuser and \
(account.db._playable_characters and
len(account.db._playable_characters) >= charmax):
self.msg("You may only create a maximum of %i characters." % charmax)
return
from evennia.objects.models import ObjectDB
@ -156,19 +156,19 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
# create the character
start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
permissions = settings.PERMISSION_PLAYER_DEFAULT
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
new_character = create.create_object(typeclass, key=key,
location=start_location,
home=default_home,
permissions=permissions)
# only allow creator (and immortals) to puppet this char
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, player.id))
player.db._playable_characters.append(new_character)
# 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)" %
(new_character.id, account.id))
account.db._playable_characters.append(new_character)
if desc:
new_character.db.desc = desc
elif not new_character.db.desc:
new_character.db.desc = "This is a Player."
new_character.db.desc = "This is an Account."
self.msg("Created new character %s. Use |w@ic %s|n to enter the game as this character."
% (new_character.key, new_character.key))
@ -183,19 +183,19 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
Permanently deletes one of your characters.
"""
key = "@chardelete"
locks = "cmd:pperm(Players)"
locks = "cmd:pperm(Account)"
help_category = "General"
def func(self):
"""delete the character"""
player = self.player
account = self.account
if not self.args:
self.msg("Usage: @chardelete <charactername>")
return
# use the playable_characters list to search
match = [char for char in utils.make_iter(player.db._playable_characters)
match = [char for char in utils.make_iter(account.db._playable_characters)
if char.key.lower() == self.args.lower()]
if not match:
self.msg("You have no such character to delete.")
@ -219,9 +219,9 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
del caller.ndb._char_to_delete
match = match[0]
player.ndb._char_to_delete = match
account.ndb._char_to_delete = match
prompt = "|rThis will permanently destroy '%s'. This cannot be undone.|n Continue yes/[no]?"
get_input(player, prompt % match.key, _callback)
get_input(account, prompt % match.key, _callback)
class CmdIC(COMMAND_DEFAULT_CLASS):
@ -234,12 +234,12 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
Go in-character (IC) as a given Character.
This will attempt to "become" a different object assuming you have
the right to do so. Note that it's the PLAYER character that puppets
the right to do so. Note that it's the ACCOUNT character that puppets
characters/objects and which needs to have the correct permission!
You cannot become an object that is already controlled by another
player. In principle <character> can be any in-game object as long
as you the player have access right to puppet it.
account. In principle <character> can be any in-game object as long
as you the account have access right to puppet it.
"""
key = "@ic"
@ -249,24 +249,24 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""
Main puppet method
"""
player = self.player
account = self.account
session = self.session
new_character = None
if not self.args:
new_character = player.db._last_puppet
new_character = account.db._last_puppet
if not new_character:
self.msg("Usage: @ic <character>")
return
if not new_character:
# search for a matching character
new_character = [char for char in search.object_search(self.args) if char.access(player, "puppet")]
new_character = [char for char in search.object_search(self.args) if char.access(account, "puppet")]
if not new_character:
self.msg("That is not a valid character choice.")
return
@ -277,15 +277,15 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
else:
new_character = new_character[0]
try:
player.puppet_object(session, new_character)
player.db._last_puppet = new_character
account.puppet_object(session, new_character)
account.db._last_puppet = new_character
except RuntimeError as exc:
self.msg("|rYou cannot become |C%s|n: %s" % (new_character.name, exc))
# note that this is inheriting from MuxPlayerLookCommand,
# note that this is inheriting from MuxAccountLookCommand,
# and as such has the .playable property.
class CmdOOC(MuxPlayerLookCommand):
class CmdOOC(MuxAccountLookCommand):
"""
stop puppeting and go ooc
@ -298,30 +298,30 @@ class CmdOOC(MuxPlayerLookCommand):
"""
key = "@ooc"
locks = "cmd:pperm(Players)"
locks = "cmd:pperm(Account)"
aliases = "@unpuppet"
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""Implement function"""
player = self.player
account = self.account
session = self.session
old_char = player.get_puppet(session)
old_char = account.get_puppet(session)
if not old_char:
string = "You are already OOC."
self.msg(string)
return
player.db._last_puppet = old_char
account.db._last_puppet = old_char
# disconnect
try:
player.unpuppet_object(session)
account.unpuppet_object(session)
self.msg("\n|GYou go OOC.|n\n")
if _MULTISESSION_MODE < 2:
@ -329,7 +329,7 @@ class CmdOOC(MuxPlayerLookCommand):
self.msg("You are out-of-character (OOC).\nUse |w@ic|n to get back into the game.")
return
self.msg(player.at_look(target=self.playable, session=session))
self.msg(account.at_look(target=self.playable, session=session))
except RuntimeError as exc:
self.msg("|rCould not unpuppet from |c%s|n: %s" % (old_char, exc))
@ -350,19 +350,19 @@ class CmdSessions(COMMAND_DEFAULT_CLASS):
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""Implement function"""
player = self.player
sessions = player.sessions.all()
account = self.account
sessions = account.sessions.all()
table = evtable.EvTable("|wsessid",
"|wprotocol",
"|whost",
"|wpuppet/character",
"|wlocation")
for sess in sorted(sessions, key=lambda x: x.sessid):
char = player.get_puppet(sess)
char = account.get_puppet(sess)
table.add_row(str(sess.sessid), str(sess.protocol_key),
type(sess.address) == tuple and sess.address[0] or sess.address,
char and str(char) or "None",
@ -387,27 +387,27 @@ class CmdWho(COMMAND_DEFAULT_CLASS):
locks = "cmd:all()"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""
Get all connected players by polling session.
Get all connected accounts by polling session.
"""
player = self.player
account = self.account
session_list = SESSIONS.get_sessions()
session_list = sorted(session_list, key=lambda o: o.player.key)
session_list = sorted(session_list, key=lambda o: o.account.key)
if self.cmdstring == "doing":
show_session_data = False
else:
show_session_data = player.check_permstring("Immortals") or player.check_permstring("Wizards")
show_session_data = account.check_permstring("Developer") or account.check_permstring("Admins")
nplayers = (SESSIONS.player_count())
naccounts = (SESSIONS.account_count())
if show_session_data:
# privileged info
table = evtable.EvTable("|wPlayer Name",
table = evtable.EvTable("|wAccount Name",
"|wOn for",
"|wIdle",
"|wPuppeting",
@ -420,10 +420,10 @@ class CmdWho(COMMAND_DEFAULT_CLASS):
continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
player = session.get_player()
account = session.get_account()
puppet = session.get_puppet()
location = puppet.location.key if puppet and puppet.location else "None"
table.add_row(utils.crop(player.name, width=25),
table.add_row(utils.crop(account.name, width=25),
utils.time_format(delta_conn, 0),
utils.time_format(delta_cmd, 1),
utils.crop(puppet.key if puppet else "None", width=25),
@ -433,19 +433,19 @@ class CmdWho(COMMAND_DEFAULT_CLASS):
isinstance(session.address, tuple) and session.address[0] or session.address)
else:
# unprivileged
table = evtable.EvTable("|wPlayer name", "|wOn for", "|wIdle")
table = evtable.EvTable("|wAccount name", "|wOn for", "|wIdle")
for session in session_list:
if not session.logged_in:
continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
player = session.get_player()
table.add_row(utils.crop(player.key, width=25),
account = session.get_account()
table.add_row(utils.crop(account.key, width=25),
utils.time_format(delta_conn, 0),
utils.time_format(delta_cmd, 1))
is_one = nplayers == 1
self.msg("|wPlayers:|n\n%s\n%s unique account%s logged in."
% (table, "One" if is_one else nplayers, "" if is_one else "s"))
is_one = naccounts == 1
self.msg("|wAccounts:|n\n%s\n%s unique account%s logged in."
% (table, "One" if is_one else naccounts, "" if is_one else "s"))
class CmdOption(COMMAND_DEFAULT_CLASS):
@ -470,7 +470,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
locks = "cmd:all()"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""
@ -585,15 +585,15 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
# a valid setting
if "save" in self.switches:
# save this option only
saved_options = self.player.attributes.get("_saved_protocol_flags", default={})
saved_options = self.account.attributes.get("_saved_protocol_flags", default={})
saved_options.update(optiondict)
self.player.attributes.add("_saved_protocol_flags", saved_options)
self.account.attributes.add("_saved_protocol_flags", saved_options)
for key in optiondict:
self.msg("|gSaved option %s.|n" % key)
if "clear" in self.switches:
# clear this save
for key in optiondict:
self.player.attributes.get("_saved_protocol_flags", {}).pop(key, None)
self.account.attributes.get("_saved_protocol_flags", {}).pop(key, None)
self.msg("|gCleared saved %s." % key)
self.session.update_flags(**optiondict)
@ -608,27 +608,27 @@ class CmdPassword(COMMAND_DEFAULT_CLASS):
Changes your password. Make sure to pick a safe one.
"""
key = "@password"
locks = "cmd:pperm(Players)"
locks = "cmd:pperm(Account)"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""hook function."""
player = self.player
account = self.account
if not self.rhs:
self.msg("Usage: @password <oldpass> = <newpass>")
return
oldpass = self.lhslist[0] # Both of these are
newpass = self.rhslist[0] # already stripped by parse()
if not player.check_password(oldpass):
if not account.check_password(oldpass):
self.msg("The specified old password isn't correct.")
elif len(newpass) < 3:
self.msg("Passwords must be at least three characters long.")
else:
player.set_password(newpass)
player.save()
account.set_password(newpass)
account.save()
self.msg("Password changed.")
@ -646,30 +646,29 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
game. Use the /all switch to disconnect from all sessions.
"""
key = "@quit"
aliases = "quit"
locks = "cmd:all()"
# this is used by the parent
player_caller = True
account_caller = True
def func(self):
"""hook function"""
player = self.player
account = self.account
if 'all' in self.switches:
player.msg("|RQuitting|n all sessions. Hope to see you soon again.", session=self.session)
for session in player.sessions.all():
player.disconnect_session_from_player(session)
account.msg("|RQuitting|n all sessions. Hope to see you soon again.", session=self.session)
for session in account.sessions.all():
account.disconnect_session_from_account(session)
else:
nsess = len(player.sessions.all())
nsess = len(account.sessions.all())
if nsess == 2:
player.msg("|RQuitting|n. One session is still connected.", session=self.session)
account.msg("|RQuitting|n. One session is still connected.", session=self.session)
elif nsess > 2:
player.msg("|RQuitting|n. %i sessions are still connected." % (nsess-1), session=self.session)
account.msg("|RQuitting|n. %i sessions are still connected." % (nsess-1), session=self.session)
else:
# we are quitting the last available session
player.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session)
player.disconnect_session_from_player(self.session)
account.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session)
account.disconnect_session_from_account(self.session)
class CmdColorTest(COMMAND_DEFAULT_CLASS):
@ -686,12 +685,19 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
color - if not you will see rubbish appear.
"""
key = "@color"
aliases = "color"
locks = "cmd:all()"
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
# the slices of the ANSI_PARSER lists to use for retrieving the
# relevant color tags to display. Replace if using another schema.
# This command can only show one set of markup.
slice_bright_fg = slice(7, 15) # from ANSI_PARSER.ansi_map
slice_dark_fg = slice(15, 23) # from ANSI_PARSER.ansi_map
slice_dark_bg = slice(-8, None) # from ANSI_PARSER.ansi_map
slice_bright_bg = slice(None, None) # from ANSI_PARSER.ansi_xterm256_bright_bg_map
def table_format(self, table):
"""
@ -718,14 +724,16 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
ap = ansi.ANSI_PARSER
# ansi colors
# show all ansi color-related codes
col1 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[48:56]]
col2 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[56:64]]
col3 = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
for code, _ in ap.ext_ansi_map[-8:]]
col4 = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
for code, _ in ap.ansi_bright_bgs[-8:]]
col2.extend(["" for _ in range(len(col1)-len(col2))])
table = utils.format_table([col1, col2, col4, col3])
bright_fg = ["%s%s|n" % (code, code.replace("|", "||"))
for code, _ in ap.ansi_map[self.slice_bright_fg]]
dark_fg = ["%s%s|n" % (code, code.replace("|", "||"))
for code, _ in ap.ansi_map[self.slice_dark_fg]]
dark_bg = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
for code, _ in ap.ansi_map[self.slice_dark_bg]]
bright_bg = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
for code, _ in ap.ansi_xterm256_bright_bg_map[self.slice_bright_bg]]
dark_fg.extend(["" for _ in range(len(bright_fg)-len(dark_fg))])
table = utils.format_table([bright_fg, dark_fg, bright_bg, dark_bg])
string = "ANSI colors:"
for row in table:
string += "\n " + " ".join(row)
@ -776,30 +784,30 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
class CmdQuell(COMMAND_DEFAULT_CLASS):
"""
use character's permissions instead of player's
use character's permissions instead of account's
Usage:
quell
unquell
Normally the permission level of the Player is used when puppeting a
Normally the permission level of the Account is used when puppeting a
Character/Object to determine access. This command will switch the lock
system to make use of the puppeted Object's permissions instead. This is
useful mainly for testing.
Hierarchical permission quelling only work downwards, thus a Player cannot
Hierarchical permission quelling only work downwards, thus an Account cannot
use a higher-permission Character to escalate their permission level.
Use the unquell command to revert back to normal operation.
"""
key = "@quell"
aliases = ["@unquell"]
locks = "cmd:pperm(Players)"
locks = "cmd:pperm(Account)"
help_category = "General"
# this is used by the parent
player_caller = True
account_caller = True
def _recache_locks(self, player):
def _recache_locks(self, account):
"""Helper method to reset the lockhandler on an already puppeted object"""
if self.session:
char = self.session.puppet
@ -808,31 +816,31 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
# the lock caches (otherwise the superuser status change
# won't be visible until repuppet)
char.locks.reset()
player.locks.reset()
account.locks.reset()
def func(self):
"""Perform the command"""
player = self.player
permstr = player.is_superuser and " (superuser)" or "(%s)" % (", ".join(player.permissions.all()))
account = self.account
permstr = account.is_superuser and " (superuser)" or "(%s)" % (", ".join(account.permissions.all()))
if self.cmdstring == '@unquell':
if not player.attributes.get('_quell'):
self.msg("Already using normal Player permissions %s." % permstr)
if not account.attributes.get('_quell'):
self.msg("Already using normal Account permissions %s." % permstr)
else:
player.attributes.remove('_quell')
self.msg("Player permissions %s restored." % permstr)
account.attributes.remove('_quell')
self.msg("Account permissions %s restored." % permstr)
else:
if player.attributes.get('_quell'):
self.msg("Already quelling Player %s permissions." % permstr)
if account.attributes.get('_quell'):
self.msg("Already quelling Account %s permissions." % permstr)
return
player.attributes.add('_quell', True)
account.attributes.add('_quell', True)
puppet = self.session.puppet
if puppet:
cpermstr = "(%s)" % ", ".join(puppet.permissions.all())
cpermstr = "Quelling to current puppet's permissions %s." % cpermstr
cpermstr += "\n(Note: If this is higher than Player permissions %s," \
cpermstr += "\n(Note: If this is higher than Account permissions %s," \
" the lowest of the two will be used.)" % permstr
cpermstr += "\nUse @unquell to return to normal permission usage."
self.msg(cpermstr)
else:
self.msg("Quelling Player permissions%s. Use @unquell to get them back." % permstr)
self._recache_locks(player)
self.msg("Quelling Account permissions%s. Use @unquell to get them back." % permstr)
self._recache_locks(account)

View file

@ -16,27 +16,27 @@ COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
# limit members for API inclusion
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelPlayer",
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelAccount",
"CmdEmit", "CmdNewPassword", "CmdPerm", "CmdWall")
class CmdBoot(COMMAND_DEFAULT_CLASS):
"""
kick a player from the server.
kick an account from the server.
Usage
@boot[/switches] <player obj> [: reason]
@boot[/switches] <account obj> [: reason]
Switches:
quiet - Silently boot without informing player
quiet - Silently boot without informing account
sid - boot by session id instead of name or dbref
Boot a player object from the server. If a reason is
Boot an account object from the server. If a reason is
supplied it will be echoed to the user unless /quiet is set.
"""
key = "@boot"
locks = "cmd:perm(boot) or perm(Wizards)"
locks = "cmd:perm(boot) or perm(Admin)"
help_category = "Admin"
def func(self):
@ -45,7 +45,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
args = self.args
if not args:
caller.msg("Usage: @boot[/switches] <player> [:reason]")
caller.msg("Usage: @boot[/switches] <account> [:reason]")
return
if ':' in args:
@ -64,10 +64,10 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
boot_list.append(sess)
break
else:
# Boot by player object
pobj = search.player_search(args)
# Boot by account object
pobj = search.account_search(args)
if not pobj:
caller.msg("Player %s was not found." % args)
caller.msg("Account %s was not found." % args)
return
pobj = pobj[0]
if not pobj.access(caller, 'boot'):
@ -75,12 +75,12 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
caller.msg(string)
return
# we have a bootable object with a connected user
matches = SESSIONS.sessions_from_player(pobj)
matches = SESSIONS.sessions_from_account(pobj)
for match in matches:
boot_list.append(match)
if not boot_list:
caller.msg("No matching sessions found. The Player does not seem to be online.")
caller.msg("No matching sessions found. The Account does not seem to be online.")
return
# Carry out the booting of the sessions in the boot list.
@ -93,7 +93,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
for session in boot_list:
session.msg(feedback)
session.player.disconnect_session_from_player(session)
session.account.disconnect_session_from_account(session)
# regex matching IP addresses with wildcards, eg. 233.122.4.*
@ -118,7 +118,7 @@ def list_bans(banlist):
class CmdBan(COMMAND_DEFAULT_CLASS):
"""
ban a player from the server
ban an account from the server
Usage:
@ban [<name or ip> [: reason]]
@ -128,8 +128,8 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
This command bans a user from accessing the game. Supply an optional
reason to be able to later remember why the ban was put in place.
It is often preferable to ban a player from the server than to
delete a player with @delplayer. If banned by name, that player
It is often preferable to ban an account from the server than to
delete an account with @delaccount. If banned by name, that account
account can no longer be logged into.
IP (Internet Protocol) address banning allows blocking all access
@ -151,7 +151,7 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
"""
key = "@ban"
aliases = ["@bans"]
locks = "cmd:perm(ban) or perm(Immortals)"
locks = "cmd:perm(ban) or perm(Developer)"
help_category = "Admin"
def func(self):
@ -206,19 +206,19 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
class CmdUnban(COMMAND_DEFAULT_CLASS):
"""
remove a ban from a player
remove a ban from an account
Usage:
@unban <banid>
This will clear a player name/ip ban previously set with the @ban
This will clear an account name/ip ban previously set with the @ban
command. Use this command without an argument to view a numbered
list of bans. Use the numbers in this list to select which one to
unban.
"""
key = "@unban"
locks = "cmd:perm(unban) or perm(Immortals)"
locks = "cmd:perm(unban) or perm(Developer)"
help_category = "Admin"
def func(self):
@ -249,23 +249,23 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
(num, " ".join([s for s in ban[:2]])))
class CmdDelPlayer(COMMAND_DEFAULT_CLASS):
class CmdDelAccount(COMMAND_DEFAULT_CLASS):
"""
delete a player from the server
delete an account from the server
Usage:
@delplayer[/switch] <name> [: reason]
@delaccount[/switch] <name> [: reason]
Switch:
delobj - also delete the player's currently
delobj - also delete the account's currently
assigned in-game object.
Completely deletes a user from the server database,
making their nick and e-mail again available.
"""
key = "@delplayer"
locks = "cmd:perm(delplayer) or perm(Immortals)"
key = "@delaccount"
locks = "cmd:perm(delaccount) or perm(Developer)"
help_category = "Admin"
def func(self):
@ -274,49 +274,49 @@ class CmdDelPlayer(COMMAND_DEFAULT_CLASS):
caller = self.caller
args = self.args
if hasattr(caller, 'player'):
caller = caller.player
if hasattr(caller, 'account'):
caller = caller.account
if not args:
self.msg("Usage: @delplayer <player/user name or #id> [: reason]")
self.msg("Usage: @delaccount <account/user name or #id> [: reason]")
return
reason = ""
if ':' in args:
args, reason = [arg.strip() for arg in args.split(':', 1)]
# We use player_search since we want to be sure to find also players
# We use account_search since we want to be sure to find also accounts
# that lack characters.
players = search.player_search(args)
accounts = search.account_search(args)
if not players:
self.msg('Could not find a player by that name.')
if not accounts:
self.msg('Could not find an account by that name.')
return
if len(players) > 1:
if len(accounts) > 1:
string = "There were multiple matches:\n"
string += "\n".join(" %s %s" % (player.id, player.key) for player in players)
string += "\n".join(" %s %s" % (account.id, account.key) for account in accounts)
self.msg(string)
return
# one single match
player = players.pop()
account = accounts.pop()
if not player.access(caller, 'delete'):
string = "You don't have the permissions to delete that player."
if not account.access(caller, 'delete'):
string = "You don't have the permissions to delete that account."
self.msg(string)
return
uname = player.username
# boot the player then delete
self.msg("Informing and disconnecting player ...")
uname = account.username
# boot the account then delete
self.msg("Informing and disconnecting account ...")
string = "\nYour account '%s' is being *permanently* deleted.\n" % uname
if reason:
string += " Reason given:\n '%s'" % reason
player.msg(string)
player.delete()
self.msg("Player %s was successfully deleted." % uname)
account.msg(string)
account.delete()
self.msg("Account %s was successfully deleted." % uname)
class CmdEmit(COMMAND_DEFAULT_CLASS):
@ -330,18 +330,18 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
Switches:
room : limit emits to rooms only (default)
players : limit emits to players only
accounts : limit emits to accounts only
contents : send to the contents of matched objects too
Emits a message to the selected objects or to
your immediate surroundings. If the object is a room,
send to its contents. @remit and @pemit are just
limited forms of @emit, for sending to rooms and
to players respectively.
to accounts respectively.
"""
key = "@emit"
aliases = ["@pemit", "@remit"]
locks = "cmd:perm(emit) or perm(Builders)"
locks = "cmd:perm(emit) or perm(Builder)"
help_category = "Admin"
def func(self):
@ -359,7 +359,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
return
rooms_only = 'rooms' in self.switches
players_only = 'players' in self.switches
accounts_only = 'accounts' in self.switches
send_to_contents = 'contents' in self.switches
# we check which command was used to force the switches
@ -367,7 +367,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
rooms_only = True
send_to_contents = True
elif self.cmdstring == '@pemit':
players_only = True
accounts_only = True
if not self.rhs:
message = self.args
@ -384,8 +384,8 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
if rooms_only and obj.location is not None:
caller.msg("%s is not a room. Ignored." % objname)
continue
if players_only and not obj.has_player:
caller.msg("%s has no active player. Ignored." % objname)
if accounts_only and not obj.has_account:
caller.msg("%s has no active account. Ignored." % objname)
continue
if obj.access(caller, 'tell'):
obj.msg(message)
@ -400,16 +400,16 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
class CmdNewPassword(COMMAND_DEFAULT_CLASS):
"""
change the password of a player
change the password of an account
Usage:
@userpassword <user obj> = <new password>
Set a player's password.
Set an account's password.
"""
key = "@userpassword"
locks = "cmd:perm(newpassword) or perm(Wizards)"
locks = "cmd:perm(newpassword) or perm(Admin)"
help_category = "Admin"
def func(self):
@ -421,36 +421,36 @@ class CmdNewPassword(COMMAND_DEFAULT_CLASS):
self.msg("Usage: @userpassword <user obj> = <new password>")
return
# the player search also matches 'me' etc.
player = caller.search_player(self.lhs)
if not player:
# the account search also matches 'me' etc.
account = caller.search_account(self.lhs)
if not account:
return
player.set_password(self.rhs)
player.save()
self.msg("%s - new password set to '%s'." % (player.name, self.rhs))
if player.character != caller:
player.msg("%s has changed your password to '%s'." % (caller.name,
account.set_password(self.rhs)
account.save()
self.msg("%s - new password set to '%s'." % (account.name, self.rhs))
if account.character != caller:
account.msg("%s has changed your password to '%s'." % (caller.name,
self.rhs))
class CmdPerm(COMMAND_DEFAULT_CLASS):
"""
set the permissions of a player/object
set the permissions of an account/object
Usage:
@perm[/switch] <object> [= <permission>[,<permission>,...]]
@perm[/switch] *<player> [= <permission>[,<permission>,...]]
@perm[/switch] *<account> [= <permission>[,<permission>,...]]
Switches:
del : delete the given permission from <object> or <player>.
player : set permission on a player (same as adding * to name)
del : delete the given permission from <object> or <account>.
account : set permission on an account (same as adding * to name)
This command sets/clears individual permission strings on an object
or player. If no permission is given, list all permissions on <object>.
or account. If no permission is given, list all permissions on <object>.
"""
key = "@perm"
aliases = "@setperm"
locks = "cmd:perm(perm) or perm(Immortals)"
locks = "cmd:perm(perm) or perm(Developer)"
help_category = "Admin"
def func(self):
@ -465,11 +465,11 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
caller.msg(string)
return
playermode = 'player' in self.switches or lhs.startswith('*')
accountmode = 'account' in self.switches or lhs.startswith('*')
lhs = lhs.lstrip("*")
if playermode:
obj = caller.search_player(lhs)
if accountmode:
obj = caller.search_account(lhs)
else:
obj = caller.search(lhs, global_search=True)
if not obj:
@ -485,19 +485,19 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
string += "<None>"
else:
string += ", ".join(obj.permissions.all())
if (hasattr(obj, 'player') and
hasattr(obj.player, 'is_superuser') and
obj.player.is_superuser):
if (hasattr(obj, 'account') and
hasattr(obj.account, 'is_superuser') and
obj.account.is_superuser):
string += "\n(... but this object is currently controlled by a SUPERUSER! "
string += "All access checks are passed automatically.)"
caller.msg(string)
return
# we supplied an argument on the form obj = perm
locktype = "edit" if playermode else "control"
locktype = "edit" if accountmode else "control"
if not obj.access(caller, locktype):
caller.msg("You are not allowed to edit this %s's permissions."
% ("player" if playermode else "object"))
% ("account" if accountmode else "object"))
return
caller_result = []
@ -528,7 +528,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
caller_result.append("\nPermission '%s' is already defined on %s." % (rhs, obj.name))
else:
obj.permissions.add(perm)
plystring = "the Player" if playermode else "the Object/Character"
plystring = "the Account" if accountmode else "the Object/Character"
caller_result.append("\nPermission '%s' given to %s (%s)." % (rhs, obj.name, plystring))
target_result.append("\n%s gives you (%s, %s) the permission '%s'."
% (caller.name, obj.name, plystring, rhs))
@ -544,10 +544,10 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
Usage:
@wall <message>
Announces a message to all connected players.
Announces a message to all connected accounts.
"""
key = "@wall"
locks = "cmd:perm(wall) or perm(Wizards)"
locks = "cmd:perm(wall) or perm(Admin)"
help_category = "Admin"
def func(self):
@ -556,5 +556,5 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
self.caller.msg("Usage: @wall <message>")
return
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
self.msg("Announcing to all connected players ...")
self.msg("Announcing to all connected accounts ...")
SESSIONS.announce_all(message)

View file

@ -237,7 +237,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
"""
key = "@batchcommands"
aliases = ["@batchcommand", "@batchcmd"]
locks = "cmd:perm(batchcommands) or superuser()"
locks = "cmd:perm(batchcommands) or perm(Developer)"
help_category = "Building"
def func(self):

View file

@ -117,7 +117,7 @@ class CmdSetObjAlias(COMMAND_DEFAULT_CLASS):
key = "@alias"
aliases = "@setobjalias"
locks = "cmd:perm(setobjalias) or perm(Builders)"
locks = "cmd:perm(setobjalias) or perm(Builder)"
help_category = "Building"
def func(self):
@ -196,7 +196,7 @@ class CmdCopy(ObjManipCommand):
"""
key = "@copy"
locks = "cmd:perm(copy) or perm(Builders)"
locks = "cmd:perm(copy) or perm(Builder)"
help_category = "Building"
def func(self):
@ -276,7 +276,7 @@ class CmdCpAttr(ObjManipCommand):
If you don't supply a source object, yourself is used.
"""
key = "@cpattr"
locks = "cmd:perm(cpattr) or perm(Builders)"
locks = "cmd:perm(cpattr) or perm(Builder)"
help_category = "Building"
def check_from_attr(self, obj, attr, clear=False):
@ -418,7 +418,7 @@ class CmdMvAttr(ObjManipCommand):
object. If you don't supply a source object, yourself is used.
"""
key = "@mvattr"
locks = "cmd:perm(mvattr) or perm(Builders)"
locks = "cmd:perm(mvattr) or perm(Builder)"
help_category = "Building"
def func(self):
@ -466,12 +466,12 @@ class CmdCreate(ObjManipCommand):
"""
key = "@create"
locks = "cmd:perm(create) or perm(Builders)"
locks = "cmd:perm(create) or perm(Builder)"
help_category = "Building"
# lockstring of newly created objects, for easy overloading.
# Will be formatted with the {id} of the creating object.
new_obj_lockstring = "control:id({id}) or perm(Wizards);delete:id({id}) or perm(Wizards)"
new_obj_lockstring = "control:id({id}) or perm(Admin);delete:id({id}) or perm(Admin)"
def func(self):
"""
@ -535,10 +535,10 @@ def _desc_quit(caller):
class CmdDesc(COMMAND_DEFAULT_CLASS):
"""
describe an object
describe an object or the current room.
Usage:
@desc [<obj> =] <description>
@setdesc [<obj> =] <description>
Switches:
edit - Open up a line editor for more advanced editing.
@ -546,9 +546,9 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
Sets the "desc" attribute on an object. If an object is not given,
describe the current room.
"""
key = "@desc"
key = "@setdesc"
aliases = "@describe"
locks = "cmd:perm(desc) or perm(Builders)"
locks = "cmd:perm(desc) or perm(Builder)"
help_category = "Building"
def edit_handler(self):
@ -607,7 +607,7 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
switches:
override - The @destroy command will usually avoid accidentally
destroying player objects. This switch overrides this safety.
destroying account objects. This switch overrides this safety.
examples:
@destroy house, roof, door, 44-78
@destroy 5-10, flower, 45
@ -618,7 +618,7 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
key = "@destroy"
aliases = ["@delete", "@del"]
locks = "cmd:perm(destroy) or perm(Builders)"
locks = "cmd:perm(destroy) or perm(Builder)"
help_category = "Building"
def func(self):
@ -640,8 +640,8 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
objname = obj.name
if not (obj.access(caller, "control") or obj.access(caller, 'delete')):
return "\nYou don't have permission to delete %s." % objname
if obj.player and not 'override' in self.switches:
return "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname
if obj.account and not 'override' in self.switches:
return "\nObject %s is controlled by an active account. Use /override to delete anyway." % objname
if obj.dbid == int(settings.DEFAULT_HOME.lstrip("#")):
return "\nYou are trying to delete |c%s|n, which is set as DEFAULT_HOME. " \
"Re-point settings.DEFAULT_HOME to another " \
@ -703,14 +703,14 @@ class CmdDig(ObjManipCommand):
would be 'north;no;n'.
"""
key = "@dig"
locks = "cmd:perm(dig) or perm(Builders)"
locks = "cmd:perm(dig) or perm(Builder)"
help_category = "Building"
# lockstring of newly created rooms, for easy overloading.
# Will be formatted with the {id} of the creating object.
new_room_lockstring = "control:id({id}) or perm(Wizards); " \
"delete:id({id}) or perm(Wizards); " \
"edit:id({id}) or perm(Wizards)"
new_room_lockstring = "control:id({id}) or perm(Admin); " \
"delete:id({id}) or perm(Admin); " \
"edit:id({id}) or perm(Admin)"
def func(self):
"Do the digging. Inherits variables from ObjManipCommand.parse()"
@ -847,7 +847,7 @@ class CmdTunnel(COMMAND_DEFAULT_CLASS):
key = "@tunnel"
aliases = ["@tun"]
locks = "cmd: perm(tunnel) or perm(Builders)"
locks = "cmd: perm(tunnel) or perm(Builder)"
help_category = "Building"
# store the direction, full name and its opposite
@ -920,7 +920,7 @@ class CmdLink(COMMAND_DEFAULT_CLASS):
"""
key = "@link"
locks = "cmd:perm(link) or perm(Builders)"
locks = "cmd:perm(link) or perm(Builder)"
help_category = "Building"
def func(self):
@ -998,7 +998,7 @@ class CmdUnLink(CmdLink):
# this is just a child of CmdLink
key = "@unlink"
locks = "cmd:perm(unlink) or perm(Builders)"
locks = "cmd:perm(unlink) or perm(Builder)"
help_key = "Building"
def func(self):
@ -1035,9 +1035,8 @@ class CmdSetHome(CmdLink):
If no location is given, just view the object's home location.
"""
key = "@home"
aliases = "@sethome"
locks = "cmd:perm(@home) or perm(Builders)"
key = "@sethome"
locks = "cmd:perm(@home) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1084,7 +1083,7 @@ class CmdListCmdSets(COMMAND_DEFAULT_CLASS):
"""
key = "@cmdsets"
aliases = "@listcmsets"
locks = "cmd:perm(listcmdsets) or perm(Builders)"
locks = "cmd:perm(listcmdsets) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1109,13 +1108,13 @@ class CmdName(ObjManipCommand):
@name obj = name;alias1;alias2
Rename an object to something new. Use *obj to
rename a player.
rename an account.
"""
key = "@name"
aliases = ["@rename"]
locks = "cmd:perm(rename) or perm(Builders)"
locks = "cmd:perm(rename) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1130,22 +1129,22 @@ class CmdName(ObjManipCommand):
if self.lhs_objs:
objname = self.lhs_objs[0]['name']
if objname.startswith("*"):
# player mode
obj = caller.player.search(objname.lstrip("*"))
# account mode
obj = caller.account.search(objname.lstrip("*"))
if obj:
if self.rhs_objs[0]['aliases']:
caller.msg("Players can't have aliases.")
caller.msg("Accounts can't have aliases.")
return
newname = self.rhs
if not newname:
caller.msg("No name defined!")
return
if not (obj.access(caller, "control") or obj.access(caller, "edit")):
caller.msg("You don't have right to edit this player %s." % obj)
caller.msg("You don't have right to edit this account %s." % obj)
return
obj.username = newname
obj.save()
caller.msg("Player's name changed to '%s'." % newname)
caller.msg("Account's name changed to '%s'." % newname)
return
# object search, also with *
obj = caller.search(objname)
@ -1191,7 +1190,7 @@ class CmdOpen(ObjManipCommand):
"""
key = "@open"
locks = "cmd:perm(open) or perm(Builders)"
locks = "cmd:perm(open) or perm(Builder)"
help_category = "Building"
# a custom member method to chug out exits and do checks
@ -1327,7 +1326,7 @@ def _convert_from_string(cmd, strobj):
be converted to a string and a warning will be given.
We need to convert like this since all data being sent over the
telnet connection by the Player is text - but we will want to
telnet connection by the Account is text - but we will want to
store it as the "real" python type so we can do convenient
comparisons later (e.g. obj.db.value = 2, if value is stored as a
string this will always fail).
@ -1382,13 +1381,13 @@ def _convert_from_string(cmd, strobj):
class CmdSetAttribute(ObjManipCommand):
"""
set attribute on an object or player
set attribute on an object or account
Usage:
@set <obj>/<attr> = <value>
@set <obj>/<attr> =
@set <obj>/<attr>
@set *<player>/attr = <value>
@set *<account>/attr = <value>
Switch:
edit: Open the line editor (string values only)
@ -1413,14 +1412,14 @@ class CmdSetAttribute(ObjManipCommand):
"""
key = "@set"
locks = "cmd:perm(set) or perm(Builders)"
locks = "cmd:perm(set) or perm(Builder)"
help_category = "Building"
def check_obj(self, obj):
"""
This may be overridden by subclasses in case restrictions need to be
placed on whether certain objects can have attributes set by certain
players.
accounts.
This function is expected to display its own error message.
@ -1506,7 +1505,7 @@ class CmdSetAttribute(ObjManipCommand):
attrs = self.lhs_objattr[0]['attrs']
if objname.startswith('*'):
obj = caller.search_player(objname.lstrip('*'))
obj = caller.search_account(objname.lstrip('*'))
else:
obj = caller.search(objname)
if not obj:
@ -1595,7 +1594,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
key = "@typeclass"
aliases = ["@type", "@parent", "@swap", "@update"]
locks = "cmd:perm(typeclass) or perm(Builders)"
locks = "cmd:perm(typeclass) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1685,7 +1684,7 @@ class CmdWipe(ObjManipCommand):
matching the given attribute-wildcard search string.
"""
key = "@wipe"
locks = "cmd:perm(wipe) or perm(Builders)"
locks = "cmd:perm(wipe) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1743,18 +1742,18 @@ class CmdLock(ObjManipCommand):
Separator expressions need not be capitalized.
For example:
'get: id(25) or perm(Wizards)'
'get: id(25) or perm(Admin)'
The 'get' access_type is checked by the get command and will
an object locked with this string will only be possible to
pick up by Wizards or by object with id 25.
pick up by Admins or by object with id=25.
You can add several access_types after oneanother by separating
them by ';', i.e:
'get:id(25);delete:perm(Builders)'
'get:id(25);delete:perm(Builder)'
"""
key = "@lock"
aliases = ["@locks", "lock", "locks"]
locks = "cmd: perm(locks) or perm(Builders)"
aliases = ["@locks"]
locks = "cmd: perm(locks) or perm(Builder)"
help_category = "Building"
def func(self):
@ -1832,26 +1831,26 @@ class CmdExamine(ObjManipCommand):
Usage:
examine [<object>[/attrname]]
examine [*<player>[/attrname]]
examine [*<account>[/attrname]]
Switch:
player - examine a Player (same as adding *)
account - examine an Account (same as adding *)
object - examine an Object (useful when OOC)
The examine command shows detailed game info about an
object and optionally a specific attribute on it.
If object is not specified, the current location is examined.
Append a * before the search string to examine a player.
Append a * before the search string to examine an account.
"""
key = "@examine"
aliases = ["@ex","ex", "exam", "examine"]
locks = "cmd:perm(examine) or perm(Builders)"
aliases = ["@ex","exam"]
locks = "cmd:perm(examine) or perm(Builder)"
help_category = "Building"
arg_regex = r"(/\w+?(\s|$))|\s|$"
player_mode = False
account_mode = False
def list_attribute(self, crop, attr, value):
"""
@ -1911,15 +1910,15 @@ class CmdExamine(ObjManipCommand):
for sess in obj.sessions.all()))
if hasattr(obj, "email") and obj.email:
string += "\n|wEmail|n: |c%s|n" % obj.email
if hasattr(obj, "has_player") and obj.has_player:
string += "\n|wPlayer|n: |c%s|n" % obj.player.name
perms = obj.player.permissions.all()
if obj.player.is_superuser:
if hasattr(obj, "has_account") and obj.has_account:
string += "\n|wAccount|n: |c%s|n" % obj.account.name
perms = obj.account.permissions.all()
if obj.account.is_superuser:
perms = ["<Superuser>"]
elif not perms:
perms = ["<None>"]
string += "\n|wPlayer Perms|n: %s" % (", ".join(perms))
if obj.player.attributes.has("_quell"):
string += "\n|wAccount Perms|n: %s" % (", ".join(perms))
if obj.account.attributes.has("_quell"):
string += " |r(quelled)|n"
string += "\n|wTypeclass|n: %s (%s)" % (obj.typename,
obj.typeclass_path)
@ -1962,16 +1961,16 @@ class CmdExamine(ObjManipCommand):
# this gets all components of the currently merged set
all_cmdsets = [(cmdset.key, cmdset) for cmdset in avail_cmdset.merged_from]
# we always at least try to add player- and session sets since these are ignored
# we always at least try to add account- and session sets since these are ignored
# if we merge on the object level.
if hasattr(obj, "player") and obj.player:
all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.player.cmdset.all()])
if hasattr(obj, "account") and obj.account:
all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.account.cmdset.all()])
if obj.sessions.count():
# if there are more sessions than one on objects it's because of multisession mode 3.
# we only show the first session's cmdset here (it is -in principle- possible that
# different sessions have different cmdsets but for admins who want such madness
# it is better that they overload with their own CmdExamine to handle it).
all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.player.sessions.all()[0].cmdset.all()])
all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.account.sessions.all()[0].cmdset.all()])
else:
try:
# we have to protect this since many objects don't have sessions.
@ -2012,7 +2011,7 @@ class CmdExamine(ObjManipCommand):
for content in obj.contents:
if content.destination:
exits.append(content)
elif content.player:
elif content.account:
pobjs.append(content)
else:
things.append(content)
@ -2052,7 +2051,7 @@ class CmdExamine(ObjManipCommand):
self.msg(caller.at_look(obj))
return
# using callback for printing result whenever function returns.
get_and_merge_cmdsets(obj, self.session, self.player, obj, "object", self.raw_string).addCallback(get_cmdset_callback)
get_and_merge_cmdsets(obj, self.session, self.account, obj, "object", self.raw_string).addCallback(get_cmdset_callback)
else:
self.msg("You need to supply a target to examine.")
return
@ -2064,13 +2063,13 @@ class CmdExamine(ObjManipCommand):
obj_name = objdef['name']
obj_attrs = objdef['attrs']
self.player_mode = utils.inherits_from(caller, "evennia.players.players.DefaultPlayer") or \
"player" in self.switches or obj_name.startswith('*')
if self.player_mode:
self.account_mode = utils.inherits_from(caller, "evennia.accounts.accounts.DefaultAccount") or \
"account" in self.switches or obj_name.startswith('*')
if self.account_mode:
try:
obj = caller.search_player(obj_name.lstrip('*'))
obj = caller.search_account(obj_name.lstrip('*'))
except AttributeError:
# this means we are calling examine from a player object
# this means we are calling examine from an account object
obj = caller.search(obj_name.lstrip('*'), search_object = 'object' in self.switches)
else:
obj = caller.search(obj_name)
@ -2090,12 +2089,12 @@ class CmdExamine(ObjManipCommand):
else:
if obj.sessions.count():
mergemode = "session"
elif self.player_mode:
mergemode = "player"
elif self.account_mode:
mergemode = "account"
else:
mergemode = "object"
# using callback to print results whenever function returns.
get_and_merge_cmdsets(obj, self.session, self.player, obj, mergemode, self.raw_string).addCallback(get_cmdset_callback)
get_and_merge_cmdsets(obj, self.session, self.account, obj, mergemode, self.raw_string).addCallback(get_cmdset_callback)
class CmdFind(COMMAND_DEFAULT_CLASS):
@ -2103,7 +2102,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
search the database for objects
Usage:
@find[/switches] <name or dbref or *player> [= dbrefmin[-dbrefmax]]
@find[/switches] <name or dbref or *account> [= dbrefmin[-dbrefmax]]
Switches:
room - only look for rooms (location=None)
@ -2112,15 +2111,15 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
exact- only exact matches are returned.
Searches the database for an object of a particular name or exact #dbref.
Use *playername to search for a player. The switches allows for
Use *accountname to search for an account. The switches allows for
limiting object matches to certain game entities. Dbrefmin and dbrefmax
limits matches to within the given dbrefs range, or above/below if only
one is given.
"""
key = "@find"
aliases = "find, @search, search, @locate, locate"
locks = "cmd:perm(find) or perm(Builders)"
aliases = "@search, @locate"
locks = "cmd:perm(find) or perm(Builder)"
help_category = "Building"
def func(self):
@ -2149,22 +2148,22 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
high = max(low, high)
is_dbref = utils.dbref(searchstring)
is_player = searchstring.startswith("*")
is_account = searchstring.startswith("*")
restrictions = ""
if self.switches:
restrictions = ", %s" % (",".join(self.switches))
if is_dbref or is_player:
if is_dbref or is_account:
if is_dbref:
# a dbref search
result = caller.search(searchstring, global_search=True, quiet=True)
string = "|wExact dbref match|n(#%i-#%i%s):" % (low, high, restrictions)
else:
# a player search
# an account search
searchstring = searchstring.lstrip("*")
result = caller.search_player(searchstring, quiet=True)
result = caller.search_account(searchstring, quiet=True)
string = "|wMatch|n(#%i-#%i%s):" % (low, high, restrictions)
if "room" in switches:
@ -2182,7 +2181,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS):
result=result[0]
string += "\n|g %s - %s|n" % (result.get_display_name(caller), result.path)
else:
# Not a player/dbref search but a wider search; build a queryset.
# Not an account/dbref search but a wider search; build a queryset.
# Searchs for key and aliases
if "exact" in switches:
keyquery = Q(db_key__iexact=searchstring, id__gte=low, id__lte=high)
@ -2251,7 +2250,7 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
is teleported to the target location. """
key = "@tel"
aliases = "@teleport"
locks = "cmd:perm(teleport) or perm(Builders)"
locks = "cmd:perm(teleport) or perm(Builder)"
help_category = "Building"
def func(self):
@ -2275,10 +2274,10 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
if not obj_to_teleport:
caller.msg("Did not find object to teleport.")
return
if obj_to_teleport.has_player:
if obj_to_teleport.has_account:
caller.msg("Cannot teleport a puppeted object "
"(%s, puppeted by %s) to a None-location." % (
obj_to_teleport.key, obj_to_teleport.player))
obj_to_teleport.key, obj_to_teleport.account))
return
caller.msg("Teleported %s -> None-location." % obj_to_teleport)
if obj_to_teleport.location and not tel_quietly:
@ -2352,7 +2351,7 @@ class CmdScript(COMMAND_DEFAULT_CLASS):
key = "@script"
aliases = "@addscript"
locks = "cmd:perm(script) or perm(Builders)"
locks = "cmd:perm(script) or perm(Builder)"
help_category = "Building"
def func(self):
@ -2453,7 +2452,7 @@ class CmdTag(COMMAND_DEFAULT_CLASS):
key = "@tag"
aliases = ["@tags"]
locks = "cmd:perm(tag) or perm(Builders)"
locks = "cmd:perm(tag) or perm(Builder)"
help_category = "Building"
arg_regex = r"(/\w+?(\s|$))|\s|$"
@ -2593,8 +2592,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
"""
key = "@spawn"
aliases = ["spawn"]
locks = "cmd:perm(spawn) or perm(Builders)"
locks = "cmd:perm(spawn) or perm(Builder)"
help_category = "Building"
def func(self):
@ -2635,8 +2633,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
return
elif isinstance(prototype, dict):
# we got the prototype on the command line. We must make sure to not allow
# the 'exec' key unless we are immortals or higher.
if "exec" in prototype and not self.caller.check_permstring("Immortals"):
# the 'exec' key unless we are developers or higher.
if "exec" in prototype and not self.caller.check_permstring("Developer"):
self.caller.msg("Spawn aborted: You don't have access to use the 'exec' prototype key.")
return
else:

View file

@ -1,8 +1,8 @@
"""
This is the cmdset for Player (OOC) commands. These are
stored on the Player object and should thus be able to handle getting
a Player object as caller rather than a Character.
This is the cmdset for Account (OOC) commands. These are
stored on the Account object and should thus be able to handle getting
an Account object as caller rather than a Character.
Note - in order for session-rerouting (in MULTISESSION_MODE=2) to
function, all commands in this cmdset should use the self.msg()
@ -11,33 +11,33 @@ command method rather than caller.msg().
from evennia.commands.cmdset import CmdSet
from evennia.commands.default import help, comms, admin, system
from evennia.commands.default import building, player
from evennia.commands.default import building, account
class PlayerCmdSet(CmdSet):
class AccountCmdSet(CmdSet):
"""
Implements the player command set.
Implements the account command set.
"""
key = "DefaultPlayer"
key = "DefaultAccount"
priority = -10
def at_cmdset_creation(self):
"Populates the cmdset"
# Player-specific commands
self.add(player.CmdOOCLook())
self.add(player.CmdIC())
self.add(player.CmdOOC())
self.add(player.CmdCharCreate())
self.add(player.CmdCharDelete())
#self.add(player.CmdSessions())
self.add(player.CmdWho())
self.add(player.CmdOption())
self.add(player.CmdQuit())
self.add(player.CmdPassword())
self.add(player.CmdColorTest())
self.add(player.CmdQuell())
# Account-specific commands
self.add(account.CmdOOCLook())
self.add(account.CmdIC())
self.add(account.CmdOOC())
self.add(account.CmdCharCreate())
self.add(account.CmdCharDelete())
#self.add(account.CmdSessions())
self.add(account.CmdWho())
self.add(account.CmdOption())
self.add(account.CmdQuit())
self.add(account.CmdPassword())
self.add(account.CmdColorTest())
self.add(account.CmdQuell())
# testing
self.add(building.CmdExamine())
@ -52,7 +52,7 @@ class PlayerCmdSet(CmdSet):
self.add(system.CmdPy())
# Admin commands
self.add(admin.CmdDelPlayer())
self.add(admin.CmdDelAccount())
self.add(admin.CmdNewPassword())
# Comm commands

View file

@ -1,8 +1,8 @@
"""
This module ties together all the commands default Character objects have
available (i.e. IC commands). Note that some commands, such as
communication-commands are instead put on the player level, in the
Player cmdset. Player commands remain available also to Characters.
communication-commands are instead put on the account level, in the
Account cmdset. Account commands remain available also to Characters.
"""
from evennia.commands.cmdset import CmdSet
from evennia.commands.default import general, help, admin, system
@ -25,7 +25,7 @@ class CharacterCmdSet(CmdSet):
self.add(general.CmdInventory())
self.add(general.CmdPose())
self.add(general.CmdNick())
self.add(general.CmdDesc())
self.add(general.CmdSetDesc())
self.add(general.CmdGet())
self.add(general.CmdDrop())
self.add(general.CmdGive())
@ -41,7 +41,7 @@ class CharacterCmdSet(CmdSet):
self.add(system.CmdPy())
self.add(system.CmdScripts())
self.add(system.CmdObjects())
self.add(system.CmdPlayers())
self.add(system.CmdAccounts())
self.add(system.CmdService())
self.add(system.CmdAbout())
self.add(system.CmdTime())

View file

@ -2,7 +2,7 @@
This module stores session-level commands.
"""
from evennia.commands.cmdset import CmdSet
from evennia.commands.default import player
from evennia.commands.default import account
class SessionCmdSet(CmdSet):
"""
@ -13,4 +13,4 @@ class SessionCmdSet(CmdSet):
def at_cmdset_creation(self):
"Populate the cmdset"
self.add(player.CmdSessions())
self.add(account.CmdSessions())

View file

@ -2,16 +2,16 @@
Comsystem command module.
Comm commands are OOC commands and intended to be made available to
the Player at all times (they go into the PlayerCmdSet). So we
make sure to homogenize self.caller to always be the player object
the Account at all times (they go into the AccountCmdSet). So we
make sure to homogenize self.caller to always be the account object
for easy handling.
"""
from past.builtins import cmp
from django.conf import settings
from evennia.comms.models import ChannelDB, Msg
from evennia.players.models import PlayerDB
from evennia.players import bots
from evennia.accounts.models import AccountDB
from evennia.accounts import bots
from evennia.comms.channelhandler import CHANNELHANDLER
from evennia.locks.lockhandler import LockException
from evennia.utils import create, utils, evtable
@ -69,14 +69,14 @@ class CmdAddCom(COMMAND_DEFAULT_CLASS):
locks = "cmd:not pperm(channel_banned)"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement the command"""
caller = self.caller
args = self.args
player = caller
account = caller
if not args:
self.msg("Usage: addcom [alias =] channelname.")
@ -96,21 +96,21 @@ class CmdAddCom(COMMAND_DEFAULT_CLASS):
return
# check permissions
if not channel.access(player, 'listen'):
if not channel.access(account, 'listen'):
self.msg("%s: You are not allowed to listen to this channel." % channel.key)
return
string = ""
if not channel.has_connection(player):
if not channel.has_connection(account):
# we want to connect as well.
if not channel.connect(player):
# if this would have returned True, the player is connected
if not channel.connect(account):
# if this would have returned True, the account is connected
self.msg("%s: You are not allowed to join this channel." % channel.key)
return
else:
string += "You now listen to the channel %s. " % channel.key
else:
if channel.unmute(player):
if channel.unmute(account):
string += "You unmute channel %s." % channel.key
else:
string += "You are already connected to channel %s." % channel.key
@ -145,13 +145,13 @@ class CmdDelCom(COMMAND_DEFAULT_CLASS):
locks = "cmd:not perm(channel_banned)"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implementing the command. """
caller = self.caller
player = caller
account = caller
if not self.args:
self.msg("Usage: delcom <alias or channel>")
@ -161,7 +161,7 @@ class CmdDelCom(COMMAND_DEFAULT_CLASS):
channel = find_channel(caller, ostring, silent=True, noaliases=True)
if channel:
# we have given a channel name - unsubscribe
if not channel.has_connection(player):
if not channel.has_connection(account):
self.msg("You are not listening to that channel.")
return
chkey = channel.key.lower()
@ -171,7 +171,7 @@ class CmdDelCom(COMMAND_DEFAULT_CLASS):
for nick in [nick for nick in make_iter(caller.nicks.get(category="channel", return_obj=True))
if nick and nick.pk and nick.value[3].lower() == chkey]:
nick.delete()
disconnect = channel.disconnect(player)
disconnect = channel.disconnect(account)
if disconnect:
wipednicks = " Eventual aliases were removed." if delnicks else ""
self.msg("You stop listening to channel '%s'.%s" % (channel.key, wipednicks))
@ -209,7 +209,7 @@ class CmdAllCom(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Runs the function"""
@ -268,12 +268,12 @@ class CmdChannels(COMMAND_DEFAULT_CLASS):
Use addcom/delcom to join and leave channels
"""
key = "@channels"
aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"]
aliases = ["@clist", "comlist", "chanlist", "channellist", "all channels"]
help_category = "Comms"
locks = "cmd: not pperm(channel_banned)"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement function"""
@ -345,7 +345,7 @@ class CmdCdestroy(COMMAND_DEFAULT_CLASS):
locks = "cmd: not pperm(channel_banned)"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Destroy objects cleanly."""
@ -372,15 +372,15 @@ class CmdCdestroy(COMMAND_DEFAULT_CLASS):
class CmdCBoot(COMMAND_DEFAULT_CLASS):
"""
kick a player from a channel you control
kick an account from a channel you control
Usage:
@cboot[/quiet] <channel> = <player> [:reason]
@cboot[/quiet] <channel> = <account> [:reason]
Switches:
quiet - don't notify the channel
Kicks a player or object from a channel you control.
Kicks an account or object from a channel you control.
"""
@ -389,13 +389,13 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""implement the function"""
if not self.args or not self.rhs:
string = "Usage: @cboot[/quiet] <channel> = <player> [:reason]"
string = "Usage: @cboot[/quiet] <channel> = <account> [:reason]"
self.msg(string)
return
@ -404,12 +404,12 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS):
return
reason = ""
if ":" in self.rhs:
playername, reason = self.rhs.rsplit(":", 1)
searchstring = playername.lstrip('*')
accountname, reason = self.rhs.rsplit(":", 1)
searchstring = accountname.lstrip('*')
else:
searchstring = self.rhs.lstrip('*')
player = self.caller.search(searchstring, player=True)
if not player:
account = self.caller.search(searchstring, account=True)
if not account:
return
if reason:
reason = " (reason: %s)" % reason
@ -417,20 +417,20 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS):
string = "You don't control this channel."
self.msg(string)
return
if player not in channel.db_subscriptions.all():
string = "Player %s is not connected to channel %s." % (player.key, channel.key)
if account not in channel.db_subscriptions.all():
string = "Account %s is not connected to channel %s." % (account.key, channel.key)
self.msg(string)
return
if "quiet" not in self.switches:
string = "%s boots %s from channel.%s" % (self.caller, player.key, reason)
string = "%s boots %s from channel.%s" % (self.caller, account.key, reason)
channel.msg(string)
# find all player's nicks linked to this channel and delete them
# find all account's nicks linked to this channel and delete them
for nick in [nick for nick in
player.character.nicks.get(category="channel") or []
account.character.nicks.get(category="channel") or []
if nick.value[3].lower() == channel.key]:
nick.delete()
# disconnect player
channel.disconnect(player)
# disconnect account
channel.disconnect(account)
CHANNELHANDLER.update()
@ -453,11 +453,11 @@ class CmdCemit(COMMAND_DEFAULT_CLASS):
key = "@cemit"
aliases = ["@cmsg"]
locks = "cmd: not pperm(channel_banned) and pperm(Players)"
locks = "cmd: not pperm(channel_banned) and pperm(Account)"
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement function"""
@ -496,7 +496,7 @@ class CmdCWho(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""implement function"""
@ -530,11 +530,11 @@ class CmdChannelCreate(COMMAND_DEFAULT_CLASS):
key = "@ccreate"
aliases = "channelcreate"
locks = "cmd:not pperm(channel_banned) and pperm(Players)"
locks = "cmd:not pperm(channel_banned) and pperm(Account)"
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement the command"""
@ -587,7 +587,7 @@ class CmdClock(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""run the function"""
@ -639,7 +639,7 @@ class CmdCdesc(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement command"""
@ -666,10 +666,10 @@ class CmdCdesc(COMMAND_DEFAULT_CLASS):
class CmdPage(COMMAND_DEFAULT_CLASS):
"""
send a private message to another player
send a private message to another account
Usage:
page[/switches] [<player>,<player>,... = <message>]
page[/switches] [<account>,<account>,... = <message>]
tell ''
page <number>
@ -687,12 +687,12 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
help_category = "Comms"
# this is used by the COMMAND_DEFAULT_CLASS parent
player_caller = True
account_caller = True
def func(self):
"""Implement function using the Msg methods"""
# Since player_caller is set above, this will be a Player.
# Since account_caller is set above, this will be an Account.
caller = self.caller
# get the messages we've sent (not to channels)
@ -718,7 +718,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
try:
number = int(self.args)
except ValueError:
self.msg("Usage: tell [<player> = msg]")
self.msg("Usage: tell [<account> = msg]")
return
if len(pages) > number:
@ -767,7 +767,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
self.msg("Noone found to page.")
return
header = "|wPlayer|n |c%s|n |wpages:|n" % caller.key
header = "|wAccount|n |c%s|n |wpages:|n" % caller.key
message = self.rhs
# if message begins with a :, we assume it is a 'page-pose'
@ -778,7 +778,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS):
create.create_message(caller, message,
receivers=recobjs)
# tell the players they got a message.
# tell the accounts they got a message.
received = []
rstrings = []
for pobj in recobjs:
@ -805,7 +805,7 @@ def _list_bots():
bots (str): A table of bots or an error message.
"""
ircbots = [bot for bot in PlayerDB.objects.filter(db_is_bot=True, username__startswith="ircbot-")]
ircbots = [bot for bot in AccountDB.objects.filter(db_is_bot=True, username__startswith="ircbot-")]
if ircbots:
from evennia.utils.evtable import EvTable
table = EvTable("|w#dbref|n", "|wbotname|n", "|wev-channel|n",
@ -836,7 +836,7 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS):
Example:
@irc2chan myircchan = irc.dalnet.net 6667 #mychannel evennia-bot
@irc2chan public = irc.freenode.net 6667 #evgaming #evbot:players.mybot.MyBot
@irc2chan public = irc.freenode.net 6667 #evgaming #evbot:accounts.mybot.MyBot
This creates an IRC bot that connects to a given IRC network and
channel. If a custom typeclass path is given, this will be used
@ -850,7 +850,7 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS):
"""
key = "@irc2chan"
locks = "cmd:serversetting(IRC_ENABLED) and pperm(Immortals)"
locks = "cmd:serversetting(IRC_ENABLED) and pperm(Developer)"
help_category = "Comms"
def func(self):
@ -868,11 +868,11 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS):
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
botname = "ircbot-%s" % self.lhs
matches = PlayerDB.objects.filter(db_is_bot=True, username=botname)
matches = AccountDB.objects.filter(db_is_bot=True, username=botname)
dbref = utils.dbref(self.lhs)
if not matches and dbref:
# try dbref match
matches = PlayerDB.objects.filter(db_is_bot=True, id=dbref)
matches = AccountDB.objects.filter(db_is_bot=True, id=dbref)
if matches:
matches[0].delete()
self.msg("IRC connection destroyed.")
@ -906,16 +906,16 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS):
irc_ssl = "ssl" in self.switches
# create a new bot
bot = PlayerDB.objects.filter(username__iexact=botname)
bot = AccountDB.objects.filter(username__iexact=botname)
if bot:
# re-use an existing bot
bot = bot[0]
if not bot.is_bot:
self.msg("Player '%s' already exists and is not a bot." % botname)
self.msg("Account '%s' already exists and is not a bot." % botname)
return
else:
try:
bot = create.create_player(botname, None, None, typeclass=botclass)
bot = create.create_account(botname, None, None, typeclass=botclass)
except Exception as err:
self.msg("|rError, could not create the bot:|n '%s'." % err)
return
@ -943,7 +943,7 @@ class CmdIRCStatus(COMMAND_DEFAULT_CLASS):
"""
key = "@ircstatus"
locks = "cmd:serversetting(IRC_ENABLED) and perm(ircstatus) or perm(Builders))"
locks = "cmd:serversetting(IRC_ENABLED) and perm(ircstatus) or perm(Builder))"
help_category = "Comms"
def func(self):
@ -963,7 +963,7 @@ class CmdIRCStatus(COMMAND_DEFAULT_CLASS):
return
matches = None
if utils.dbref(botname):
matches = PlayerDB.objects.filter(db_is_bot=True, id=utils.dbref(botname))
matches = AccountDB.objects.filter(db_is_bot=True, id=utils.dbref(botname))
if not matches:
self.msg("No matching IRC-bot found. Use @ircstatus without arguments to list active bots.")
return
@ -981,7 +981,7 @@ class CmdIRCStatus(COMMAND_DEFAULT_CLASS):
# an asynchronous call.
self.caller.msg("Requesting nicklist from %s (%s:%s)." % (channel, network, port))
ircbot.get_nicklist(self.caller)
elif self.caller.locks.check_lockstring(self.caller, "dummy:perm(ircstatus) or perm(Immortals)"):
elif self.caller.locks.check_lockstring(self.caller, "dummy:perm(ircstatus) or perm(Developer)"):
# reboot the client
self.caller.msg("Forcing a disconnect + reconnect of %s." % chtext)
ircbot.reconnect()
@ -1016,7 +1016,7 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
"""
key = "@rss2chan"
locks = "cmd:serversetting(RSS_ENABLED) and pperm(Immortals)"
locks = "cmd:serversetting(RSS_ENABLED) and pperm(Developer)"
help_category = "Comms"
def func(self):
@ -1038,7 +1038,7 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
if 'list' in self.switches:
# show all connections
rssbots = [bot for bot in PlayerDB.objects.filter(db_is_bot=True, username__startswith="rssbot-")]
rssbots = [bot for bot in AccountDB.objects.filter(db_is_bot=True, username__startswith="rssbot-")]
if rssbots:
from evennia.utils.evtable import EvTable
table = EvTable("|wdbid|n", "|wupdate rate|n", "|wev-channel",
@ -1052,10 +1052,10 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
botname = "rssbot-%s" % self.lhs
matches = PlayerDB.objects.filter(db_is_bot=True, db_key=botname)
matches = AccountDB.objects.filter(db_is_bot=True, db_key=botname)
if not matches:
# try dbref match
matches = PlayerDB.objects.filter(db_is_bot=True, id=self.args.lstrip("#"))
matches = AccountDB.objects.filter(db_is_bot=True, id=self.args.lstrip("#"))
if matches:
matches[0].delete()
self.msg("RSS connection destroyed.")
@ -1072,14 +1072,14 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
botname = "rssbot-%s" % url
# create a new bot
bot = PlayerDB.objects.filter(username__iexact=botname)
bot = AccountDB.objects.filter(username__iexact=botname)
if bot:
# re-use existing bot
bot = bot[0]
if not bot.is_bot:
self.msg("Player '%s' already exists and is not a bot." % botname)
self.msg("Account '%s' already exists and is not a bot." % botname)
return
else:
bot = create.create_player(botname, None, None, typeclass=bots.RSSBot)
bot = create.create_account(botname, None, None, typeclass=bots.RSSBot)
bot.start(ev_channel=channel, rss_url=url, rss_rate=10)
self.msg("RSS reporter created. Fetching RSS.")

View file

@ -9,7 +9,7 @@ COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
# limit symbol import for API
__all__ = ("CmdHome", "CmdLook", "CmdNick",
"CmdInventory", "CmdGet", "CmdDrop", "CmdGive",
"CmdInventory", "CmdSetDesc", "CmdGet", "CmdDrop", "CmdGive",
"CmdSay", "CmdWhisper", "CmdPose", "CmdAccess")
@ -24,7 +24,7 @@ class CmdHome(COMMAND_DEFAULT_CLASS):
"""
key = "home"
locks = "cmd:perm(home) or perm(Builders)"
locks = "cmd:perm(home) or perm(Builder)"
arg_regex = r"$"
def func(self):
@ -47,7 +47,7 @@ class CmdLook(COMMAND_DEFAULT_CLASS):
Usage:
look
look <obj>
look *<player>
look *<account>
Observes your location or objects in your vicinity.
"""
@ -86,7 +86,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
Switches:
inputline - replace on the inputline (default)
object - replace on object-lookup
player - replace on player-lookup
account - replace on account-lookup
delete - remove nick by name or by index given by /list
clearall - clear all nicks
list - show all defined aliases (also "nicks" works)
@ -113,7 +113,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
"""
key = "nick"
aliases = ["nickname", "nicks", "@nick", "@nicks", "alias"]
aliases = ["nickname", "nicks", "alias"]
locks = "cmd:all()"
def func(self):
@ -121,7 +121,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
caller = self.caller
switches = self.switches
nicktypes = [switch for switch in switches if switch in ("object", "player", "inputline")] or ["inputline"]
nicktypes = [switch for switch in switches if switch in ("object", "account", "inputline")] or ["inputline"]
nicklist = utils.make_iter(caller.nicks.get(return_obj=True) or [])
@ -358,18 +358,18 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
to_give.at_give(caller, target)
class CmdDesc(COMMAND_DEFAULT_CLASS):
class CmdSetDesc(COMMAND_DEFAULT_CLASS):
"""
describe yourself
Usage:
desc <description>
setdesc <description>
Add a description to yourself. This
will be visible to people when they
look at you.
"""
key = "desc"
key = "setdesc"
locks = "cmd:all()"
arg_regex = r"\s|$"
@ -417,7 +417,7 @@ class CmdSay(COMMAND_DEFAULT_CLASS):
return
# Call the at_after_say hook on the character
caller.at_after_say(speech)
caller.at_say(speech)
class CmdWhisper(COMMAND_DEFAULT_CLASS):
"""
@ -439,7 +439,7 @@ class CmdWhisper(COMMAND_DEFAULT_CLASS):
caller = self.caller
if not self.lhs or not self.rhs:
caller.msg("Usage: whisper <player> = <message>")
caller.msg("Usage: whisper <account> = <message>")
return
receiver = caller.search(self.lhs)
@ -453,14 +453,14 @@ class CmdWhisper(COMMAND_DEFAULT_CLASS):
speech = self.rhs
# Call a hook to change the speech before whispering
speech = caller.at_before_whisper(receiver, speech)
speech = caller.at_before_say(speech, whisper=True, receiver=receiver)
# If the speech is empty, abort the command
if not speech:
return
# Call the at_after_whisper hook for feedback
caller.at_after_whisper(receiver, speech)
caller.at_say(speech, receiver=receiver, whisper=True)
class CmdPose(COMMAND_DEFAULT_CLASS):
@ -529,15 +529,15 @@ class CmdAccess(COMMAND_DEFAULT_CLASS):
hierarchy_full = settings.PERMISSION_HIERARCHY
string = "\n|wPermission Hierarchy|n (climbing):\n %s" % ", ".join(hierarchy_full)
if self.caller.player.is_superuser:
if self.caller.account.is_superuser:
cperms = "<Superuser>"
pperms = "<Superuser>"
else:
cperms = ", ".join(caller.permissions.all())
pperms = ", ".join(caller.player.permissions.all())
pperms = ", ".join(caller.account.permissions.all())
string += "\n|wYour access|n:"
string += "\nCharacter |c%s|n: %s" % (caller.key, cperms)
if hasattr(caller, 'player'):
string += "\nPlayer |c%s|n: %s" % (caller.player.key, pperms)
if hasattr(caller, 'account'):
string += "\nAccount |c%s|n: %s" % (caller.account.key, pperms)
caller.msg(string)

View file

@ -66,7 +66,7 @@ class CmdHelp(Command):
if self.session.protocol_key in ("websocket", "ajax/comet"):
try:
options = self.player.db._saved_webclient_options
options = self.account.db._saved_webclient_options
if options and options["helppopup"]:
usemore = False
except KeyError:
@ -134,12 +134,12 @@ class CmdHelp(Command):
Helper method. If this return True, the given cmd
auto-help will be viewable in the help listing.
Override this to easily select what is shown to
the player. Note that only commands available
the account. Note that only commands available
in the caller's merged cmdset are available.
Args:
cmd (Command): Command class from the merged cmdset
caller (Character, Player or Session): The current caller
caller (Character, Account or Session): The current caller
executing the help command.
"""
@ -300,9 +300,8 @@ class CmdSetHelp(COMMAND_DEFAULT_CLASS):
is to let everyone read the help file.
"""
key = "@help"
aliases = "@sethelp"
locks = "cmd:perm(PlayerHelpers)"
key = "@sethelp"
locks = "cmd:perm(Helper)"
help_category = "Building"
def func(self):

View file

@ -1,13 +1,13 @@
"""
The command template for the default MUX-style command set. There
is also an Player/OOC version that makes sure caller is a Player object.
is also an Account/OOC version that makes sure caller is an Account object.
"""
from evennia.utils import utils
from evennia.commands.command import Command
# limit symbol import for API
__all__ = ("MuxCommand", "MuxPlayerCommand")
__all__ = ("MuxCommand", "MuxAccountCommand")
class MuxCommand(Command):
@ -128,17 +128,17 @@ class MuxCommand(Command):
self.rhs = rhs
self.rhslist = rhslist
# if the class has the player_caller property set on itself, we make
# sure that self.caller is always the player if possible. We also create
# if the class has the account_caller property set on itself, we make
# sure that self.caller is always the account if possible. We also create
# a special property "character" for the puppeted object, if any. This
# is convenient for commands defined on the Player only.
if hasattr(self, "player_caller") and self.player_caller:
# is convenient for commands defined on the Account only.
if hasattr(self, "account_caller") and self.account_caller:
if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"):
# caller is an Object/Character
self.character = self.caller
self.caller = self.caller.player
elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"):
# caller was already a Player
self.caller = self.caller.account
elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"):
# caller was already an Account
self.character = self.caller.get_puppet(self.session)
else:
self.character = None
@ -177,32 +177,32 @@ class MuxCommand(Command):
self.caller.msg(string)
class MuxPlayerCommand(MuxCommand):
class MuxAccountCommand(MuxCommand):
"""
This is an on-Player version of the MuxCommand. Since these commands sit
on Players rather than on Characters/Objects, we need to check
This is an on-Account version of the MuxCommand. Since these commands sit
on Accounts rather than on Characters/Objects, we need to check
this in the parser.
Player commands are available also when puppeting a Character, it's
Account commands are available also when puppeting a Character, it's
just that they are applied with a lower priority and are always
available, also when disconnected from a character (i.e. "ooc").
This class makes sure that caller is always a Player object, while
This class makes sure that caller is always an Account object, while
creating a new property "character" that is set only if a
character is actually attached to this Player and Session.
character is actually attached to this Account and Session.
"""
def parse(self):
"""
We run the parent parser as usual, then fix the result
"""
super(MuxPlayerCommand, self).parse()
super(MuxAccountCommand, self).parse()
if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"):
# caller is an Object/Character
self.character = self.caller
self.caller = self.caller.player
elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"):
# caller was already a Player
self.caller = self.caller.account
elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"):
# caller was already an Account
self.character = self.caller.get_puppet(self.session)
else:
self.character = None

View file

@ -17,7 +17,7 @@ from django.conf import settings
from evennia.server.sessionhandler import SESSIONS
from evennia.scripts.models import ScriptDB
from evennia.objects.models import ObjectDB
from evennia.players.models import PlayerDB
from evennia.accounts.models import AccountDB
from evennia.utils import logger, utils, gametime, create
from evennia.utils.eveditor import EvEditor
from evennia.utils.evtable import EvTable
@ -47,7 +47,7 @@ class CmdReload(COMMAND_DEFAULT_CLASS):
@reset to purge) and at_reload() hooks will be called.
"""
key = "@reload"
locks = "cmd:perm(reload) or perm(Immortals)"
locks = "cmd:perm(reload) or perm(Developer)"
help_category = "System"
def func(self):
@ -83,7 +83,7 @@ class CmdReset(COMMAND_DEFAULT_CLASS):
"""
key = "@reset"
aliases = ['@reboot']
locks = "cmd:perm(reload) or perm(Immortals)"
locks = "cmd:perm(reload) or perm(Developer)"
help_category = "System"
def func(self):
@ -105,7 +105,7 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
Gracefully shut down both Server and Portal.
"""
key = "@shutdown"
locks = "cmd:perm(shutdown) or perm(Immortals)"
locks = "cmd:perm(shutdown) or perm(Developer)"
help_category = "System"
def func(self):
@ -244,7 +244,7 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
"""
key = "@py"
aliases = ["!"]
locks = "cmd:perm(py) or perm(Immortals)"
locks = "cmd:perm(py) or perm(Developer)"
help_category = "System"
def func(self):
@ -327,7 +327,7 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
"""
key = "@scripts"
aliases = ["@globalscript", "@listscripts"]
locks = "cmd:perm(listscripts) or perm(Wizards)"
locks = "cmd:perm(listscripts) or perm(Admin)"
help_category = "System"
def func(self):
@ -409,7 +409,7 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
"""
key = "@objects"
aliases = ["@listobjects", "@listobjs", '@stats', '@db']
locks = "cmd:perm(listobjects) or perm(Builders)"
locks = "cmd:perm(listobjects) or perm(Builder)"
help_category = "System"
def func(self):
@ -455,24 +455,24 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
caller.msg(string)
class CmdPlayers(COMMAND_DEFAULT_CLASS):
class CmdAccounts(COMMAND_DEFAULT_CLASS):
"""
list all registered players
list all registered accounts
Usage:
@players [nr]
@accounts [nr]
Lists statistics about the Players registered with the game.
It will list the <nr> amount of latest registered players
Lists statistics about the Accounts registered with the game.
It will list the <nr> amount of latest registered accounts
If not given, <nr> defaults to 10.
"""
key = "@players"
aliases = ["@listplayers"]
locks = "cmd:perm(listplayers) or perm(Wizards)"
key = "@accounts"
aliases = ["@listaccounts"]
locks = "cmd:perm(listaccounts) or perm(Admin)"
help_category = "System"
def func(self):
"""List the players"""
"""List the accounts"""
caller = self.caller
if self.args and self.args.isdigit():
@ -480,21 +480,21 @@ class CmdPlayers(COMMAND_DEFAULT_CLASS):
else:
nlim = 10
nplayers = PlayerDB.objects.count()
naccounts = AccountDB.objects.count()
# typeclass table
dbtotals = PlayerDB.objects.object_totals()
dbtotals = AccountDB.objects.object_totals()
typetable = EvTable("|wtypeclass|n", "|wcount|n", "|w%%|n", border="cells", align="l")
for path, count in dbtotals.items():
typetable.add_row(path, count, "%.2f" % ((float(count) / nplayers) * 100))
typetable.add_row(path, count, "%.2f" % ((float(count) / naccounts) * 100))
# last N table
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
plyrs = AccountDB.objects.all().order_by("db_date_created")[max(0, naccounts - nlim):]
latesttable = EvTable("|wcreated|n", "|wdbref|n", "|wname|n", "|wtypeclass|n", border="cells", align="l")
for ply in plyrs:
latesttable.add_row(utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.path)
string = "\n|wPlayer typeclass distribution:|n\n%s" % typetable
string += "\n|wLast %s Players created:|n\n%s" % (min(nplayers, nlim), latesttable)
string = "\n|wAccount typeclass distribution:|n\n%s" % typetable
string += "\n|wLast %s Accounts created:|n\n%s" % (min(naccounts, nlim), latesttable)
caller.msg(string)
@ -520,7 +520,7 @@ class CmdService(COMMAND_DEFAULT_CLASS):
key = "@service"
aliases = ["@services"]
locks = "cmd:perm(service) or perm(Immortals)"
locks = "cmd:perm(service) or perm(Developer)"
help_category = "System"
def func(self):
@ -644,7 +644,7 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
"""
key = "@time"
aliases = "@uptime"
locks = "cmd:perm(time) or perm(Players)"
locks = "cmd:perm(time) or perm(Account)"
help_category = "System"
def func(self):
@ -702,7 +702,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
"""
key = "@server"
aliases = ["@serverload", "@serverprocess"]
locks = "cmd:perm(list) or perm(Immortals)"
locks = "cmd:perm(list) or perm(Developer)"
help_category = "System"
def func(self):
@ -823,7 +823,7 @@ class CmdTickers(COMMAND_DEFAULT_CLASS):
"""
key = "@tickers"
help_category = "System"
locks = "cmd:perm(tickers) or perm(Builders)"
locks = "cmd:perm(tickers) or perm(Builder)"
def func(self):
from evennia import TICKER_HANDLER

View file

@ -19,7 +19,7 @@ from mock import Mock
from evennia.commands.default.cmdset_character import CharacterCmdSet
from evennia.utils.test_resources import EvenniaTest
from evennia.commands.default import help, general, system, admin, player, building, batchprocess, comms
from evennia.commands.default import help, general, system, admin, account, building, batchprocess, comms
from evennia.commands.command import Command, InterruptCommand
from evennia.utils import ansi, utils
from evennia.server.sessionhandler import SESSIONS
@ -57,11 +57,13 @@ class CommandTest(EvenniaTest):
caller = caller if caller else self.char1
receiver = receiver if receiver else caller
cmdobj.caller = caller
cmdobj.cmdstring = cmdstring if cmdstring else cmdobj.key
cmdobj.cmdname = cmdstring if cmdstring else cmdobj.key
cmdobj.raw_cmdname = cmdobj.cmdname
cmdobj.cmdstring = cmdobj.cmdname # deprecated
cmdobj.args = args
cmdobj.cmdset = cmdset
cmdobj.session = SESSIONS.session_from_sessid(1)
cmdobj.player = self.player
cmdobj.account = self.account
cmdobj.raw_string = cmdobj.key + " " + args
cmdobj.obj = obj or (caller if caller else self.char1)
# test
@ -76,7 +78,7 @@ class CommandTest(EvenniaTest):
except InterruptCommand:
pass
finally:
# clean out prettytable sugar. We only operate on text-type
# clean out evtable sugar. We only operate on text-type
stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True))
for name, args, kwargs in receiver.msg.mock_calls]
# Get the first element of a tuple if msg received a tuple instead of a string
@ -117,10 +119,10 @@ class TestGeneral(CommandTest):
def test_nick(self):
self.call(general.CmdNick(), "testalias = testaliasedstring1", "Nick 'testalias' mapped to 'testaliasedstring1'.")
self.call(general.CmdNick(), "/player testalias = testaliasedstring2", "Nick 'testalias' mapped to 'testaliasedstring2'.")
self.call(general.CmdNick(), "/account testalias = testaliasedstring2", "Nick 'testalias' mapped to 'testaliasedstring2'.")
self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Nick 'testalias' mapped to 'testaliasedstring3'.")
self.assertEqual(u"testaliasedstring1", self.char1.nicks.get("testalias"))
self.assertEqual(u"testaliasedstring2", self.char1.nicks.get("testalias", category="player"))
self.assertEqual(u"testaliasedstring2", self.char1.nicks.get("testalias", category="account"))
self.assertEqual(u"testaliasedstring3", self.char1.nicks.get("testalias", category="object"))
def test_get_and_drop(self):
@ -170,54 +172,54 @@ class TestAdmin(CommandTest):
self.call(admin.CmdEmit(), "Char2 = Test", "Emitted to Char2:\nTest")
def test_perm(self):
self.call(admin.CmdPerm(), "Obj = Builders", "Permission 'Builders' given to Obj (the Object/Character).")
self.call(admin.CmdPerm(), "Char2 = Builders", "Permission 'Builders' given to Char2 (the Object/Character).")
self.call(admin.CmdPerm(), "Obj = Builder", "Permission 'Builder' given to Obj (the Object/Character).")
self.call(admin.CmdPerm(), "Char2 = Builder", "Permission 'Builder' given to Char2 (the Object/Character).")
def test_wall(self):
self.call(admin.CmdWall(), "Test", "Announcing to all connected players ...")
self.call(admin.CmdWall(), "Test", "Announcing to all connected accounts ...")
def test_ban(self):
self.call(admin.CmdBan(), "Char", "NameBan char was added.")
class TestPlayer(CommandTest):
class TestAccount(CommandTest):
def test_ooc_look(self):
if settings.MULTISESSION_MODE < 2:
self.call(player.CmdOOCLook(), "", "You are outofcharacter (OOC).", caller=self.player)
self.call(account.CmdOOCLook(), "", "You are outofcharacter (OOC).", caller=self.account)
if settings.MULTISESSION_MODE == 2:
self.call(player.CmdOOCLook(), "", "Account TestPlayer (you are OutofCharacter)", caller=self.player)
self.call(account.CmdOOCLook(), "", "Account TestAccount (you are OutofCharacter)", caller=self.account)
def test_ooc(self):
self.call(player.CmdOOC(), "", "You go OOC.", caller=self.player)
self.call(account.CmdOOC(), "", "You go OOC.", caller=self.account)
def test_ic(self):
self.player.unpuppet_object(self.session)
self.call(player.CmdIC(), "Char", "You become Char.", caller=self.player, receiver=self.char1)
self.account.unpuppet_object(self.session)
self.call(account.CmdIC(), "Char", "You become Char.", caller=self.account, receiver=self.char1)
def test_password(self):
self.call(player.CmdPassword(), "testpassword = testpassword", "Password changed.", caller=self.player)
self.call(account.CmdPassword(), "testpassword = testpassword", "Password changed.", caller=self.account)
def test_option(self):
self.call(player.CmdOption(), "", "Client settings", caller=self.player)
self.call(account.CmdOption(), "", "Client settings", caller=self.account)
def test_who(self):
self.call(player.CmdWho(), "", "Players:", caller=self.player)
self.call(account.CmdWho(), "", "Accounts:", caller=self.account)
def test_quit(self):
self.call(player.CmdQuit(), "", "Quitting. Hope to see you again, soon.", caller=self.player)
self.call(account.CmdQuit(), "", "Quitting. Hope to see you again, soon.", caller=self.account)
def test_sessions(self):
self.call(player.CmdSessions(), "", "Your current session(s):", caller=self.player)
self.call(account.CmdSessions(), "", "Your current session(s):", caller=self.account)
def test_color_test(self):
self.call(player.CmdColorTest(), "ansi", "ANSI colors:", caller=self.player)
self.call(account.CmdColorTest(), "ansi", "ANSI colors:", caller=self.account)
def test_char_create(self):
self.call(player.CmdCharCreate(), "Test1=Test char", "Created new character Test1. Use @ic Test1 to enter the game", caller=self.player)
self.call(account.CmdCharCreate(), "Test1=Test char", "Created new character Test1. Use @ic Test1 to enter the game", caller=self.account)
def test_quell(self):
self.call(player.CmdQuell(), "", "Quelling to current puppet's permissions (immortals).", caller=self.player)
self.call(account.CmdQuell(), "", "Quelling to current puppet's permissions (developer).", caller=self.account)
class TestBuilding(CommandTest):
@ -272,7 +274,7 @@ class TestBuilding(CommandTest):
"Obj changed typeclass from evennia.objects.objects.DefaultObject to evennia.objects.objects.DefaultExit.")
def test_lock(self):
self.call(building.CmdLock(), "Obj = test:perm(Immortals)", "Added lock 'test:perm(Immortals)' to Obj.")
self.call(building.CmdLock(), "Obj = test:perm(Developer)", "Added lock 'test:perm(Developer)' to Obj.")
def test_find(self):
self.call(building.CmdFind(), "Room2", "One Match")
@ -288,39 +290,39 @@ class TestComms(CommandTest):
def setUp(self):
super(CommandTest, self).setUp()
self.call(comms.CmdChannelCreate(), "testchan;test=Test Channel", "Created channel testchan and connected to it.", receiver=self.player)
self.call(comms.CmdChannelCreate(), "testchan;test=Test Channel", "Created channel testchan and connected to it.", receiver=self.account)
def test_toggle_com(self):
self.call(comms.CmdAddCom(), "tc = testchan", "You are already connected to channel testchan. You can now", receiver=self.player)
self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.", receiver=self.player)
self.call(comms.CmdAddCom(), "tc = testchan", "You are already connected to channel testchan. You can now", receiver=self.account)
self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.", receiver=self.account)
def test_channels(self):
self.call(comms.CmdChannels(), "" ,"Available channels (use comlist,addcom and delcom to manage", receiver=self.player)
self.call(comms.CmdChannels(), "" ,"Available channels (use comlist,addcom and delcom to manage", receiver=self.account)
def test_all_com(self):
self.call(comms.CmdAllCom(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.player)
self.call(comms.CmdAllCom(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.account)
def test_clock(self):
self.call(comms.CmdClock(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:", receiver=self.player)
self.call(comms.CmdClock(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:", receiver=self.account)
def test_cdesc(self):
self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.", receiver=self.player)
self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.", receiver=self.account)
def test_cemit(self):
self.call(comms.CmdCemit(), "testchan = Test Message", "[testchan] Test Message|Sent to channel testchan: Test Message", receiver=self.player)
self.call(comms.CmdCemit(), "testchan = Test Message", "[testchan] Test Message|Sent to channel testchan: Test Message", receiver=self.account)
def test_cwho(self):
self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestPlayer", receiver=self.player)
self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestAccount", receiver=self.account)
def test_page(self):
self.call(comms.CmdPage(), "TestPlayer2 = Test", "TestPlayer2 is offline. They will see your message if they list their pages later.|You paged TestPlayer2 with: 'Test'.", receiver=self.player)
self.call(comms.CmdPage(), "TestAccount2 = Test", "TestAccount2 is offline. They will see your message if they list their pages later.|You paged TestAccount2 with: 'Test'.", receiver=self.account)
def test_cboot(self):
# No one else connected to boot
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <player> [:reason]", receiver=self.player)
self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] <channel> = <account> [:reason]", receiver=self.account)
def test_cdestroy(self):
self.call(comms.CmdCdestroy(), "testchan" ,"[testchan] TestPlayer: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.", receiver=self.player)
self.call(comms.CmdCdestroy(), "testchan" ,"[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.", receiver=self.account)
class TestBatchProcess(CommandTest):

View file

@ -7,7 +7,7 @@ from collections import defaultdict
from random import getrandbits
from django.conf import settings
from django.contrib.auth import authenticate
from evennia.players.models import PlayerDB
from evennia.accounts.models import AccountDB
from evennia.objects.models import ObjectDB
from evennia.server.models import ServerConfig
from evennia.comms.models import ChannelDB
@ -25,7 +25,7 @@ MULTISESSION_MODE = settings.MULTISESSION_MODE
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
# Helper function to throttle failed connection attempts.
# This can easily be used to limit player creation too,
# This can easily be used to limit account creation too,
# (just supply a different storage dictionary), but this
# would also block dummyrunner, so it's not added as default.
@ -77,17 +77,17 @@ def _throttle(session, maxlim=None, timeout=None, storage=_LATEST_FAILED_LOGINS)
return False
def create_guest_player(session):
def create_guest_account(session):
"""
Creates a guest player/character for this session, if one is available.
Creates a guest account/character for this session, if one is available.
Args:
session (Session): the session which will use the guest player/character.
session (Session): the session which will use the guest account/character.
Returns:
GUEST_ENABLED (boolean), player (Player):
GUEST_ENABLED (boolean), account (Account):
the boolean is whether guest accounts are enabled at all.
the Player which was created from an available guest name.
the Account which was created from an available guest name.
"""
# check if guests are enabled.
if not settings.GUEST_ENABLED:
@ -105,25 +105,25 @@ def create_guest_player(session):
try:
# Find an available guest name.
playername = None
accountname = None
for name in settings.GUEST_LIST:
if not PlayerDB.objects.filter(username__iexact=playername).count():
playername = name
if not AccountDB.objects.filter(username__iexact=accountname).count():
accountname = name
break
if not playername:
if not accountname:
session.msg("All guest accounts are in use. Please try again later.")
return True, None
else:
# build a new player with the found guest playername
# build a new account with the found guest accountname
password = "%016x" % getrandbits(64)
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
ptypeclass = settings.BASE_GUEST_TYPECLASS
new_player = _create_player(session, playername, password, permissions, ptypeclass)
if new_player:
_create_character(session, new_player, typeclass, home, permissions)
return True, new_player
new_account = _create_account(session, accountname, password, permissions, ptypeclass)
if new_account:
_create_character(session, new_account, typeclass, home, permissions)
return True, new_account
except Exception:
# We are in the middle between logged in and -not, so we have
@ -134,17 +134,17 @@ def create_guest_player(session):
raise
def create_normal_player(session, name, password):
def create_normal_account(session, name, password):
"""
Creates a player with the given name and password.
Creates an account with the given name and password.
Args:
session (Session): the session which is requesting to create a player.
name (str): the name that the player wants to use for login.
password (str): the password desired by this player, for login.
session (Session): the session which is requesting to create an account.
name (str): the name that the account wants to use for login.
password (str): the password desired by this account, for login.
Returns:
player (Player): the player which was created from the name and password.
account (Account): the account which was created from the name and password.
"""
# check for too many login errors too quick.
if _throttle(session, maxlim=5, timeout=5*60):
@ -153,22 +153,22 @@ def create_normal_player(session, name, password):
return None
# Match account name and check password
player = authenticate(username=name, password=password)
account = authenticate(username=name, password=password)
if not player:
# No playername or password match
if not account:
# No accountname or password match
session.msg("Incorrect login information given.")
# this just updates the throttle
_throttle(session)
# calls player hook for a failed login if possible.
player = PlayerDB.objects.get_player_from_name(name)
if player:
player.at_failed_login(session)
# calls account hook for a failed login if possible.
account = AccountDB.objects.get_account_from_name(name)
if account:
account.at_failed_login(session)
return None
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == player.name.lower() for tup in bans)
if bans and (any(tup[0] == account.name.lower() for tup in bans)
or
any(tup[2].match(session.address) for tup in bans if tup[2])):
# this is a banned IP or name!
@ -178,7 +178,7 @@ def create_normal_player(session, name, password):
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return None
return player
return account
class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
@ -186,8 +186,8 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
connect to the game
Usage (at login screen):
connect playername password
connect "player name" "pass word"
connect accountname password
connect "account name" "pass word"
Use the create command to first create an account before logging in.
@ -204,7 +204,7 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
have a unique position in that their func() receives
a session object instead of a source_object like all
other types of logged-in commands (this is because
there is no object yet before the player has logged in)
there is no object yet before the account has logged in)
"""
session = self.caller
@ -222,9 +222,9 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
parts = parts[0].split(None, 1)
# Guest login
if len(parts) == 1 and parts[0].lower() == "guest":
enabled, new_player = create_guest_player(session)
if new_player:
session.sessionhandler.login(session, new_player)
enabled, new_account = create_guest_account(session)
if new_account:
session.sessionhandler.login(session, new_account)
if enabled:
return
@ -233,20 +233,20 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
return
name, password = parts
player = create_normal_player(session, name, password)
if player:
session.sessionhandler.login(session, player)
account = create_normal_account(session, name, password)
if account:
session.sessionhandler.login(session, account)
class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
"""
create a new player account
create a new account account
Usage (at login screen):
create <playername> <password>
create "player name" "pass word"
create <accountname> <password>
create "account name" "pass word"
This creates a new player account.
This creates a new account account.
If you have spaces in your name, enclose it in double quotes.
"""
@ -271,25 +271,25 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
"\nIf <name> or <password> contains spaces, enclose it in double quotes."
session.msg(string)
return
playername, password = parts
accountname, password = parts
# sanity checks
if not re.findall(r"^[\w. @+\-']+$", playername) or not (0 < len(playername) <= 30):
if not re.findall(r"^[\w. @+\-']+$", accountname) or not (0 < len(accountname) <= 30):
# this echoes the restrictions made by django's auth
# module (except not allowing spaces, for convenience of
# logging in).
string = "\n\r Playername can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_/' only."
string = "\n\r Accountname can max be 30 characters or fewer. Letters, spaces, digits and @/./+/-/_/' only."
session.msg(string)
return
# strip excessive spaces in playername
playername = re.sub(r"\s+", " ", playername).strip()
if PlayerDB.objects.filter(username__iexact=playername):
# player already exists (we also ignore capitalization here)
session.msg("Sorry, there is already a player with the name '%s'." % playername)
# strip excessive spaces in accountname
accountname = re.sub(r"\s+", " ", accountname).strip()
if AccountDB.objects.filter(username__iexact=accountname):
# account already exists (we also ignore capitalization here)
session.msg("Sorry, there is already an account with the name '%s'." % accountname)
return
# Reserve playernames found in GUEST_LIST
if settings.GUEST_LIST and playername.lower() in (guest.lower() for guest in settings.GUEST_LIST):
string = "\n\r That name is reserved. Please choose another Playername."
# Reserve accountnames found in GUEST_LIST
if settings.GUEST_LIST and accountname.lower() in (guest.lower() for guest in settings.GUEST_LIST):
string = "\n\r That name is reserved. Please choose another Accountname."
session.msg(string)
return
if not re.findall(r"^[\w. @+\-']+$", password) or not (3 < len(password)):
@ -301,7 +301,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
# Check IP and/or name bans
bans = ServerConfig.objects.conf("server_bans")
if bans and (any(tup[0] == playername.lower() for tup in bans)
if bans and (any(tup[0] == accountname.lower() for tup in bans)
or
any(tup[2].match(session.address) for tup in bans if tup[2])):
# this is a banned IP or name!
@ -311,22 +311,22 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return
# everything's ok. Create the new player account.
# everything's ok. Create the new account account.
try:
permissions = settings.PERMISSION_PLAYER_DEFAULT
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
new_player = _create_player(session, playername, password, permissions)
if new_player:
new_account = _create_account(session, accountname, password, permissions)
if new_account:
if MULTISESSION_MODE < 2:
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
_create_character(session, new_player, typeclass, default_home, permissions)
_create_character(session, new_account, typeclass, default_home, permissions)
# tell the caller everything went well.
string = "A new account '%s' was created. Welcome!"
if " " in playername:
if " " in accountname:
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
else:
string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (playername, playername))
session.msg(string % (accountname, accountname))
except Exception:
# We are in the middle between logged in and -not, so we have
@ -344,7 +344,7 @@ class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS):
quit
We maintain a different version of the quit command
here for unconnected players for the sake of simplicity. The logged in
here for unconnected accounts for the sake of simplicity. The logged in
version is a bit more complicated.
"""
key = "quit"
@ -516,50 +516,50 @@ class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
self.session.sessionhandler.session_portal_sync(self.session)
def _create_player(session, playername, password, permissions, typeclass=None, email=None):
def _create_account(session, accountname, password, permissions, typeclass=None, email=None):
"""
Helper function, creates a player of the specified typeclass.
Helper function, creates an account of the specified typeclass.
"""
try:
new_player = create.create_player(playername, email, password, permissions=permissions, typeclass=typeclass)
new_account = create.create_account(accountname, email, password, permissions=permissions, typeclass=typeclass)
except Exception as e:
session.msg("There was an error creating the Player:\n%s\n If this problem persists, contact an admin." % 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 player is
# 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_player.db.FIRST_LOGIN = True
new_account.db.FIRST_LOGIN = True
# join the new player to the public channel
# 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_player):
string = "New player '%s' could not connect to public channel!" % new_player.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_player
return new_account
def _create_character(session, new_player, typeclass, home, permissions):
def _create_character(session, new_account, typeclass, home, permissions):
"""
Helper function, creates a character based on a player's name.
Helper function, creates a character based on an account's name.
This is meant for Guest and MULTISESSION_MODE < 2 situations.
"""
try:
new_character = create.create_object(typeclass, key=new_player.key, home=home, permissions=permissions)
new_character = create.create_object(typeclass, key=new_account.key, home=home, permissions=permissions)
# set playable character list
new_player.db._playable_characters.append(new_character)
new_account.db._playable_characters.append(new_character)
# allow only the character itself and the player to puppet this character (and Immortals).
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, new_player.id))
# 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 Player."
new_character.db.desc = "This is an Account."
# We need to set this to have @ic auto-connect to this character
new_player.db._last_puppet = new_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

@ -266,15 +266,15 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest):
deferred.addCallback(_callback)
return deferred
def test_from_player(self):
from evennia.commands.default.cmdset_player import PlayerCmdSet
def test_from_account(self):
from evennia.commands.default.cmdset_account import AccountCmdSet
a = self.cmdset_a
a.no_channels = True
self.set_cmdsets(self.player, a)
deferred = cmdhandler.get_and_merge_cmdsets(self.player, None, self.player, None, "player", "")
self.set_cmdsets(self.account, a)
deferred = cmdhandler.get_and_merge_cmdsets(self.account, None, self.account, None, "account", "")
# get_and_merge_cmdsets converts to lower-case internally.
def _callback(cmdset):
pcmdset = PlayerCmdSet()
pcmdset = AccountCmdSet()
pcmdset.at_cmdset_creation()
pcmds = [cmd.key for cmd in pcmdset.commands] + ["a", "b", "c", "d"]
self.assertTrue(all(cmd.key in pcmds for cmd in cmdset.commands))
@ -305,18 +305,18 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest):
def test_autocmdsets(self):
import evennia
from evennia.commands.default.cmdset_player import PlayerCmdSet
from evennia.commands.default.cmdset_account import AccountCmdSet
from evennia.comms.channelhandler import CHANNEL_HANDLER
testchannel = evennia.create_channel("channeltest", locks="listen:all();send:all()")
CHANNEL_HANDLER.add(testchannel)
CHANNEL_HANDLER.update()
self.assertTrue(testchannel.connect(self.player))
self.assertTrue(testchannel.has_connection(self.player))
self.assertTrue(testchannel.connect(self.account))
self.assertTrue(testchannel.has_connection(self.account))
a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d
self.set_cmdsets(self.player, a, b, c, d)
deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, self.player, self.char1, "session", "")
self.set_cmdsets(self.account, a, b, c, d)
deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, self.account, self.char1, "session", "")
def _callback(cmdset):
pcmdset = PlayerCmdSet()
pcmdset = AccountCmdSet()
pcmdset.at_cmdset_creation()
pcmds = [cmd.key for cmd in pcmdset.commands] + ["a", "b", "c", "d"] + ["out"]
self.assertTrue(all(cmd.key or hasattr(cmd, "is_channel") in pcmds for cmd in cmdset.commands))