Merge conflicts against master, including cmdhandler support for direct cmdobject input together with prefix-ignore mechanism from devel.

This commit is contained in:
Griatch 2017-04-01 16:08:23 +02:00
commit a648433db8
69 changed files with 2617 additions and 1771 deletions

View file

@ -124,7 +124,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})."
def _msg_err(receiver, stringtuple):
@ -235,7 +235,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
for lobj in local_objlist:
try:
# call hook in case we need to do dynamic changing to cmdset
_GA(lobj, "at_cmdset_get")()
_GA(lobj, "at_cmdset_get")(caller=caller)
except Exception:
logger.log_trace()
# the call-type lock is checked here, it makes sure a player
@ -391,7 +391,8 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype, raw_string):
@inlineCallbacks
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", session=None, **kwargs):
def cmdhandler(called_by, raw_string, _testing=False, callertype="session", session=None,
cmdobj=None, cmdobj_key=None, **kwargs):
"""
This is the main mechanism that handles any string sent to the engine.
@ -413,6 +414,15 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
precendence for same-name and same-prio commands.
session (Session, optional): Relevant if callertype is "player" - 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)
`cmdobj_key` as its input command name. No cmdset lookup will be performed but
all other options apply as normal. This allows for running a specific Command
within the command system mechanism.
cmdobj_key (string, optional): Used together with `cmdobj` keyword to specify
which cmdname should be assigned when calling the specified Command instance. This
is made available as `self.cmdstring` when the Command runs.
If not given, the command will be assumed to be called as `cmdobj.key`.
Kwargs:
kwargs (any): other keyword arguments will be assigned as named variables on the
@ -428,7 +438,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
"""
@inlineCallbacks
def _run_command(cmd, cmdname, raw_cmdname, args):
def _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, player):
"""
Helper function: This initializes and runs the Command
instance once the parser has identified it as either a normal
@ -437,10 +447,13 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
Args:
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).
args (str): extra text entered after the identified command
cmdset (CmdSet): Command sert the command belongs to (if any)..
session (Session): Session of caller (if any).
player (Player): Player of caller (if any).
Returns:
deferred (Deferred): this will fire with the return of the
@ -482,7 +495,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=unformatted_raw_string,
raw_cmdname=raw_cmdname,
cmdclass=cmd.__class__)
raise RuntimeError(err)
@ -543,78 +556,89 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
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
cmdset = yield get_and_merge_cmdsets(caller, session, player, 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:
# Empty input. Test for system command instead.
syscmd = yield cmdset.get(CMD_NOINPUT)
sysarg = ""
raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string and match to available cmdset.
# This also checks for permissions, so all commands in match
# are commands the caller is allowed to call.
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)
else:
# no explicit cmdobject given, figure it out
cmdset = yield get_and_merge_cmdsets(caller, session, player, 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:
# Empty input. Test for system command instead.
syscmd = yield cmdset.get(CMD_NOINPUT)
sysarg = ""
raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string and match to available cmdset.
# This also checks for permissions, so all commands in match
# are commands the caller is allowed to call.
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)
# Deal with matches
# Deal with matches
if len(matches) > 1:
# We have a multiple-match
syscmd = yield cmdset.get(CMD_MULTIMATCH)
sysarg = _("There were multiple matches.")
if syscmd:
# use custom CMD_MULTIMATCH
syscmd.matches = matches
else:
# fall back to default error handling
sysarg = yield _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=matches[0][0])
raise ExecSystemCommand(syscmd, sysarg)
cmdname, args, cmd = "", "", None
if len(matches) == 1:
# We have a unique command match. But it may still be invalid.
match = matches[0]
cmdname, args, cmd, raw_cmdname = match[0], match[1], match[2], match[5]
if not matches:
# No commands match our entered command
syscmd = yield cmdset.get(CMD_NOMATCH)
if syscmd:
# use custom CMD_NOMATCH command
sysarg = raw_string
else:
# fallback to default error text
sysarg = _("Command '%s' is not available.") % raw_string
suggestions = string_suggestions(raw_string,
cmdset.get_all_cmd_keys_and_aliases(caller),
cutoff=0.7, maxnum=3)
if suggestions:
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
if len(matches) > 1:
# We have a multiple-match
syscmd = yield cmdset.get(CMD_MULTIMATCH)
sysarg = _("There were multiple matches.")
if syscmd:
# use custom CMD_MULTIMATCH
syscmd.matches = matches
else:
sysarg += _(" Type \"help\" for help.")
raise ExecSystemCommand(syscmd, sysarg)
# fall back to default error handling
sysarg = yield _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=matches[0][0])
raise ExecSystemCommand(syscmd, sysarg)
# Check if this is a Channel-cmd match.
if hasattr(cmd, 'is_channel') and cmd.is_channel:
# even if a user-defined syscmd is not defined, the
# found cmd is already a system command in its own right.
syscmd = yield cmdset.get(CMD_CHANNEL)
if syscmd:
# replace system command with custom version
cmd = syscmd
cmd.session = session
sysarg = "%s:%s" % (cmdname, args)
raise ExecSystemCommand(cmd, sysarg)
cmdname, args, cmd = "", "", None
if len(matches) == 1:
# We have a unique command match. But it may still be invalid.
match = matches[0]
cmdname, args, cmd, raw_cmdname = match[0], match[1], match[2], match[5]
if not matches:
# No commands match our entered command
syscmd = yield cmdset.get(CMD_NOMATCH)
if syscmd:
# use custom CMD_NOMATCH command
sysarg = raw_string
else:
# fallback to default error text
sysarg = _("Command '%s' is not available.") % raw_string
suggestions = string_suggestions(raw_string,
cmdset.get_all_cmd_keys_and_aliases(caller),
cutoff=0.7, maxnum=3)
if suggestions:
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
else:
sysarg += _(" Type \"help\" for help.")
raise ExecSystemCommand(syscmd, sysarg)
# Check if this is a Channel-cmd match.
if hasattr(cmd, 'is_channel') and cmd.is_channel:
# even if a user-defined syscmd is not defined, the
# found cmd is already a system command in its own right.
syscmd = yield cmdset.get(CMD_CHANNEL)
if syscmd:
# replace system command with custom version
cmd = syscmd
cmd.session = session
sysarg = "%s:%s" % (cmdname, args)
raise ExecSystemCommand(cmd, sysarg)
# A normal command.
ret = yield _run_command(cmd, cmdname, raw_cmdname, args)
ret = yield _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, player)
returnValue(ret)
except ErrorReported as exc:
@ -629,7 +653,8 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
sysarg = exc.sysarg
if syscmd:
ret = yield _run_command(syscmd, syscmd.key, syscmd, sysarg)
ret = yield _run_command(syscmd, syscmd.key, sysarg,
unformatted_raw_string, cmdset, session, player)
returnValue(ret)
elif sysarg:
# return system arg

View file

@ -35,7 +35,7 @@ def _init_command(cls, **kwargs):
if cls.aliases and not is_iter(cls.aliases):
try:
cls.aliases = [str(alias).strip().lower()
for alias in cls.aliases.split(',')]
for alias in cls.aliases.split(',')]
except Exception:
cls.aliases = []
cls.aliases = list(set(alias for alias in cls.aliases
@ -57,7 +57,7 @@ def _init_command(cls, **kwargs):
if not hasattr(cls, 'locks'):
# default if one forgets to define completely
cls.locks = "cmd:all()"
if not "cmd:" in cls.locks:
if "cmd:" not in cls.locks:
cls.locks = "cmd:all();" + cls.locks
for lockstring in cls.locks.split(';'):
if lockstring and not ':' in lockstring:
@ -197,7 +197,6 @@ class Command(with_metaclass(CommandMeta, object)):
try:
# first assume input is a command (the most common case)
return self._matchset.intersection(cmd._matchset)
#return cmd.key in self._matchset
except AttributeError:
# probably got a string
return cmd in self._matchset
@ -211,9 +210,8 @@ class Command(with_metaclass(CommandMeta, object)):
"""
try:
return self._matchset.isdisjoint(cmd._matchset)
#return not cmd.key in self._matcheset
except AttributeError:
return not cmd in self._matchset
return cmd not in self._matchset
def __contains__(self, query):
"""
@ -308,7 +306,7 @@ class Command(with_metaclass(CommandMeta, object)):
def msg(self, text=None, to_obj=None, from_obj=None,
session=None, **kwargs):
"""
This is a shortcut instad of calling msg() directly on an
This is a shortcut instead of calling msg() directly on an
object - it will detect if caller is an Object or a Player and
also appends self.session automatically if self.msg_all_sessions is False.
@ -398,17 +396,18 @@ class Command(with_metaclass(CommandMeta, object)):
"""
# a simple test command to show the available properties
string = "-" * 50
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "\n|w%s|n - Command variables from evennia:\n" % self.key
string += "-" * 50
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
string += "cmd locks (self.locks): {w%s{n\n" % self.locks
string += "help category (self.help_category): {w%s{n\n" % self.help_category.capitalize()
string += "object calling (self.caller): {w%s{n\n" % self.caller
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
string += "\nname of cmd (self.key): |w%s|n\n" % self.key
string += "cmd aliases (self.aliases): |w%s|n\n" % self.aliases
string += "cmd locks (self.locks): |w%s|n\n" % self.locks
string += "help category (self.help_category): |w%s|n\n" % self.help_category.capitalize()
string += "object calling (self.caller): |w%s|n\n" % self.caller
string += "object storing cmdset (self.obj): |w%s|n\n" % self.obj
string += "command string given (self.cmdstring): |w%s|n\n" % self.cmdstring
# show cmdset.key instead of cmdset to shorten output
string += fill("current cmdset (self.cmdset): {w%s{n\n" % (self.cmdset.key if self.cmdset.key else self.cmdset.__class__))
string += fill("current cmdset (self.cmdset): |w%s|n\n" %
(self.cmdset.key if self.cmdset.key else self.cmdset.__class__))
self.caller.msg(string)

View file

@ -1,21 +0,0 @@
#
# This is Evennia's default connection screen. It is imported
# and run from server/conf/connection_screens.py.
#
from django.conf import settings
from evennia.utils import utils
DEFAULT_SCREEN = \
"""{b=============================================================={n
Welcome to {g%s{n, version %s!
If you have an existing account, connect to it by typing:
{wconnect <username> <password>{n
If you need to create an account, type (without the <>'s):
{wcreate <username> <password>{n
If you have spaces in your username, enclose it in double quotes.
Enter {whelp{n for more info. {wlook{n will re-show this screen.
{b=============================================================={n""" \
% (settings.SERVERNAME, utils.get_evennia_version())

View file

@ -9,7 +9,7 @@ import re
from django.conf import settings
from evennia.server.sessionhandler import SESSIONS
from evennia.server.models import ServerConfig
from evennia.utils import prettytable, search, class_from_module
from evennia.utils import evtable, search, class_from_module
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -40,7 +40,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implementing the function"
"""Implementing the function"""
caller = self.caller
args = self.args
@ -86,7 +86,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
# Carry out the booting of the sessions in the boot list.
feedback = None
if not 'quiet' in self.switches:
if 'quiet' not in self.switches:
feedback = "You have been disconnected by %s.\n" % caller.name
if reason:
feedback += "\nReason given: %s" % reason
@ -108,13 +108,12 @@ def list_bans(banlist):
if not banlist:
return "No active bans were found."
table = prettytable.PrettyTable(["{wid", "{wname/ip", "{wdate", "{wreason"])
table = evtable.EvTable("|wid", "|wname/ip", "|wdate", "|wreason")
for inum, ban in enumerate(banlist):
table.add_row([str(inum + 1),
ban[0] and ban[0] or ban[1],
ban[3], ban[4]])
string = "{wActive bans:{n\n%s" % table
return string
table.add_row(str(inum + 1),
ban[0] and ban[0] or ban[1],
ban[3], ban[4])
return "|wActive bans:|n\n%s" % table
class CmdBan(COMMAND_DEFAULT_CLASS):
@ -174,7 +173,7 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
if not self.args or (self.switches
and not any(switch in ('ip', 'name')
for switch in self.switches)):
for switch in self.switches)):
self.caller.msg(list_bans(banlist))
return
@ -202,7 +201,7 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
# save updated banlist
banlist.append(bantup)
ServerConfig.objects.conf('server_bans', banlist)
self.caller.msg("%s-Ban {w%s{n was added." % (typ, ban))
self.caller.msg("%s-Ban |w%s|n was added." % (typ, ban))
class CmdUnban(COMMAND_DEFAULT_CLASS):
@ -223,7 +222,7 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implement unbanning"
"""Implement unbanning"""
banlist = ServerConfig.objects.conf('server_bans')
@ -240,14 +239,14 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
if not banlist:
self.caller.msg("There are no bans to clear.")
elif not (0 < num < len(banlist) + 1):
self.caller.msg("Ban id {w%s{x was not found." % self.args)
self.caller.msg("Ban id |w%s|x was not found." % self.args)
else:
# all is ok, clear ban
ban = banlist[num - 1]
del banlist[num - 1]
ServerConfig.objects.conf('server_bans', banlist)
self.caller.msg("Cleared ban %s: %s" %
(num, " ".join([s for s in ban[:2]])))
(num, " ".join([s for s in ban[:2]])))
class CmdDelPlayer(COMMAND_DEFAULT_CLASS):
@ -270,7 +269,7 @@ class CmdDelPlayer(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implements the command."
"""Implements the command."""
caller = self.caller
args = self.args
@ -346,7 +345,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implement the command"
"""Implement the command"""
caller = self.caller
args = self.args
@ -382,7 +381,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
obj = caller.search(objname, global_search=True)
if not obj:
return
if rooms_only and not obj.location is None:
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:
@ -414,7 +413,7 @@ class CmdNewPassword(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implement the function."
"""Implement the function."""
caller = self.caller
@ -455,7 +454,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implement function"
"""Implement function"""
caller = self.caller
switches = self.switches
@ -481,36 +480,37 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
caller.msg("You are not allowed to examine this object.")
return
string = "Permissions on {w%s{n: " % obj.key
string = "Permissions on |w%s|n: " % obj.key
if not obj.permissions.all():
string += "<None>"
else:
string += ", ".join(obj.permissions.all())
if (hasattr(obj, 'player') and
hasattr(obj.player, 'is_superuser') and
obj.player.is_superuser):
obj.player.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
if not obj.access(caller, 'control'):
caller.msg("You are not allowed to edit this object's permissions.")
locktype = "edit" if playermode 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"))
return
cstring = ""
tstring = ""
caller_result = []
target_result = []
if 'del' in switches:
# delete the given permission(s) from object.
for perm in self.rhslist:
obj.permissions.remove(perm)
if obj.permissions.get(perm):
cstring += "\nPermissions %s could not be removed from %s." % (perm, obj.name)
caller_result.append("\nPermissions %s could not be removed from %s." % (perm, obj.name))
else:
cstring += "\nPermission %s removed from %s (if they existed)." % (perm, obj.name)
tstring += "\n%s revokes the permission(s) %s from you." % (caller.name, perm)
caller_result.append("\nPermission %s removed from %s (if they existed)." % (perm, obj.name))
target_result.append("\n%s revokes the permission(s) %s from you." % (caller.name, perm))
else:
# add a new permission
permissions = obj.permissions.all()
@ -525,15 +525,16 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
return
if perm in permissions:
cstring += "\nPermission '%s' is already defined on %s." % (rhs, obj.name)
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"
cstring += "\nPermission '%s' given to %s (%s)." % (rhs, obj.name, plystring)
tstring += "\n%s gives you (%s, %s) the permission '%s'." % (caller.name, obj.name, plystring, rhs)
caller.msg(cstring.strip())
if tstring:
obj.msg(tstring.strip())
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))
caller.msg("".join(caller_result).strip())
if target_result:
obj.msg("".join(target_result).strip())
class CmdWall(COMMAND_DEFAULT_CLASS):
@ -550,7 +551,7 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
help_category = "Admin"
def func(self):
"Implements command"
"""Implements command"""
if not self.args:
self.caller.msg("Usage: @wall <message>")
return

View file

@ -29,15 +29,13 @@ from evennia.utils import logger, utils
_RE_COMMENT = re.compile(r"^#.*?$", re.MULTILINE + re.DOTALL)
_RE_CODE_START = re.compile(r"^# batchcode code:", re.MULTILINE)
_COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
#from evennia.commands.default.muxcommand import _COMMAND_DEFAULT_CLASS
# limit symbols for API inclusion
__all__ = ("CmdBatchCommands", "CmdBatchCode")
_HEADER_WIDTH = 70
_UTF8_ERROR = \
"""
{rDecode error in '%s'.{n
_UTF8_ERROR = """
|rDecode error in '%s'.|n
This file contains non-ascii character(s). This is common if you
wrote some input in a language that has more letters and special
@ -83,9 +81,9 @@ print "leaving run ..."
"""
#------------------------------------------------------------
# -------------------------------------------------------------
# Helper functions
#------------------------------------------------------------
# -------------------------------------------------------------
def format_header(caller, entry):
"""
@ -100,7 +98,7 @@ def format_header(caller, entry):
header = utils.crop(entry, width=width)
ptr = caller.ndb.batch_stackptr + 1
stacklen = len(caller.ndb.batch_stack)
header = "{w%02i/%02i{G: %s{n" % (ptr, stacklen, header)
header = "|w%02i/%02i|G: %s|n" % (ptr, stacklen, header)
# add extra space to the side for padding.
header = "%s%s" % (header, " " * (width - len(header)))
header = header.replace('\n', '\\n')
@ -114,7 +112,7 @@ def format_code(entry):
"""
code = ""
for line in entry.split('\n'):
code += "\n{G>>>{n %s" % line
code += "\n|G>>>|n %s" % line
return code.strip()
@ -164,9 +162,9 @@ def step_pointer(caller, step=1):
stack = caller.ndb.batch_stack
nstack = len(stack)
if ptr + step <= 0:
caller.msg("{RBeginning of batch file.")
caller.msg("|RBeginning of batch file.")
if ptr + step >= nstack:
caller.msg("{REnd of batch file.")
caller.msg("|REnd of batch file.")
caller.ndb.batch_stackptr = max(0, min(nstack - 1, ptr + step))
@ -186,10 +184,10 @@ def show_curr(caller, showall=False):
string = format_header(caller, entry)
codeall = entry.strip()
string += "{G(hh for help)"
string += "|G(hh for help)"
if showall:
for line in codeall.split('\n'):
string += "\n{G|{n %s" % line
string += "\n|G||n %s" % line
caller.msg(string)
@ -217,9 +215,9 @@ def purge_processor(caller):
caller.scripts.validate() # this will purge interactive mode
#------------------------------------------------------------
# -------------------------------------------------------------
# main access commands
#------------------------------------------------------------
# -------------------------------------------------------------
class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
@ -243,7 +241,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
help_category = "Building"
def func(self):
"Starts the processor."
"""Starts the processor."""
caller = self.caller
@ -253,7 +251,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
return
python_path = self.args
#parse indata file
# parse indata file
try:
commands = BATCHCMD.parse_file(python_path)
@ -289,7 +287,8 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
caller.msg("\nBatch-command processor - Interactive mode for %s ..." % python_path)
show_curr(caller)
else:
caller.msg("Running Batch-command processor - Automatic mode for %s (this might take some time) ..." % python_path)
caller.msg("Running Batch-command processor - Automatic mode for %s (this might take some time) ..."
% python_path)
procpool = False
if "PythonProcPool" in utils.server_services():
@ -301,11 +300,11 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
if procpool:
# run in parallel process
def callback(r):
caller.msg(" {GBatchfile '%s' applied." % python_path)
caller.msg(" |GBatchfile '%s' applied." % python_path)
purge_processor(caller)
def errback(e):
caller.msg(" {RError from processor: '%s'" % e)
caller.msg(" |RError from processor: '%s'" % e)
purge_processor(caller)
utils.run_async(_PROCPOOL_BATCHCMD_SOURCE,
@ -315,7 +314,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
at_err=errback)
else:
# run in-process (might block)
for inum in range(len(commands)):
for _ in range(len(commands)):
# loop through the batch file
if not batch_cmd_exec(caller):
return
@ -323,7 +322,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
# clean out the safety cmdset and clean out all other
# temporary attrs.
string = " Batchfile '%s' applied." % python_path
caller.msg("{G%s" % string)
caller.msg("|G%s" % string)
purge_processor(caller)
@ -352,7 +351,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
help_category = "Building"
def func(self):
"Starts the processor."
"""Starts the processor."""
caller = self.caller
@ -363,7 +362,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
python_path = self.args
debug = 'debug' in self.switches
#parse indata file
# parse indata file
try:
codes = BATCHCODE.parse_file(python_path)
except UnicodeDecodeError as err:
@ -410,11 +409,11 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
if procpool:
# run in parallel process
def callback(r):
caller.msg(" {GBatchfile '%s' applied." % python_path)
caller.msg(" |GBatchfile '%s' applied." % python_path)
purge_processor(caller)
def errback(e):
caller.msg(" {RError from processor: '%s'" % e)
caller.msg(" |RError from processor: '%s'" % e)
purge_processor(caller)
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE,
codes=codes,
@ -423,7 +422,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
at_err=errback)
else:
# un in-process (will block)
for inum in range(len(codes)):
for _ in range(len(codes)):
# loop through the batch file
if not batch_code_exec(caller):
return
@ -431,14 +430,14 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
# clean out the safety cmdset and clean out all other
# temporary attrs.
string = " Batchfile '%s' applied." % python_path
caller.msg("{G%s" % string)
caller.msg("|G%s" % string)
purge_processor(caller)
#------------------------------------------------------------
# -------------------------------------------------------------
# State-commands for the interactive batch processor modes
# (these are the same for both processors)
#------------------------------------------------------------
# -------------------------------------------------------------
class CmdStateAbort(_COMMAND_DEFAULT_CLASS):
"""
@ -453,7 +452,7 @@ class CmdStateAbort(_COMMAND_DEFAULT_CLASS):
locks = "cmd:perm(batchcommands)"
def func(self):
"Exit back to default."
"""Exit back to default."""
purge_processor(self.caller)
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
@ -472,6 +471,7 @@ class CmdStateLL(_COMMAND_DEFAULT_CLASS):
def func(self):
show_curr(self.caller, showall=True)
class CmdStatePP(_COMMAND_DEFAULT_CLASS):
"""
pp
@ -644,7 +644,7 @@ class CmdStateSS(_COMMAND_DEFAULT_CLASS):
else:
step = 1
for istep in range(step):
for _ in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
@ -673,7 +673,7 @@ class CmdStateSL(_COMMAND_DEFAULT_CLASS):
else:
step = 1
for istep in range(step):
for _ in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
@ -699,7 +699,7 @@ class CmdStateCC(_COMMAND_DEFAULT_CLASS):
ptr = caller.ndb.batch_stackptr
step = nstack - ptr
for istep in range(step):
for _ in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
@ -775,7 +775,7 @@ class CmdStateQQ(_COMMAND_DEFAULT_CLASS):
class CmdStateHH(_COMMAND_DEFAULT_CLASS):
"Help command"
"""Help command"""
key = "hh"
help_category = "BatchProcess"
@ -810,12 +810,12 @@ class CmdStateHH(_COMMAND_DEFAULT_CLASS):
self.caller.msg(string)
#------------------------------------------------------------
# -------------------------------------------------------------
#
# Defining the cmdsets for the interactive batchprocessor
# mode (same for both processors)
#
#------------------------------------------------------------
# -------------------------------------------------------------
class BatchSafeCmdSet(CmdSet):
"""
@ -827,7 +827,7 @@ class BatchSafeCmdSet(CmdSet):
priority = 150 # override other cmdsets.
def at_cmdset_creation(self):
"Init the cmdset"
"""Init the cmdset"""
self.add(CmdStateAbort())
@ -839,7 +839,7 @@ class BatchInteractiveCmdSet(CmdSet):
priority = 104
def at_cmdset_creation(self):
"init the cmdset"
"""init the cmdset"""
self.add(CmdStateAbort())
self.add(CmdStateLL())
self.add(CmdStatePP())

View file

@ -591,9 +591,11 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
if not obj:
return
desc = self.args
obj.db.desc = desc
caller.msg("The description was set on %s." % obj.get_display_name(caller))
if obj.access(caller, "edit"):
obj.db.desc = desc
caller.msg("The description was set on %s." % obj.get_display_name(caller))
else:
caller.msg("You don't have permission to edit the description of %s." % obj.key)
class CmdDestroy(COMMAND_DEFAULT_CLASS):
@ -1138,7 +1140,7 @@ class CmdName(ObjManipCommand):
caller.msg("No name defined!")
return
if not (obj.access(caller, "control") or obj.access(caller, "edit")):
caller.mgs("You don't have right to edit this player %s." % obj)
caller.msg("You don't have right to edit this player %s." % obj)
return
obj.username = newname
obj.save()

View file

@ -30,6 +30,7 @@ class CharacterCmdSet(CmdSet):
self.add(general.CmdDrop())
self.add(general.CmdGive())
self.add(general.CmdSay())
self.add(general.CmdWhisper())
self.add(general.CmdAccess())
# The help system

View file

@ -60,16 +60,17 @@ class CmdLook(COMMAND_DEFAULT_CLASS):
"""
Handle the looking.
"""
caller = self.caller
if not self.args:
target = self.caller.location
target = caller.location
if not target:
self.caller.msg("You have no location to look at!")
caller.msg("You have no location to look at!")
return
else:
target = self.caller.search(self.args)
target = caller.search(self.args, use_dbref=caller.check_permstring("Builders"))
if not target:
return
self.msg(self.caller.at_look(target))
self.msg(caller.at_look(target))
class CmdNick(COMMAND_DEFAULT_CLASS):
@ -175,10 +176,9 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
errstring += "Not a valid nick index."
else:
errstring += "Nick not found."
if "delete" in switches or "del" in switches:
# clear the nick
if caller.nicks.has(old_nickstring, category=nicktype):
if old_nickstring and caller.nicks.has(old_nickstring, category=nicktype):
caller.nicks.remove(old_nickstring, category=nicktype)
string += "\nNick removed: '|w%s|n' -> |w%s|n." % (old_nickstring, old_replstring)
else:
@ -354,6 +354,8 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
caller.msg("You give %s to %s." % (to_give.key, target.key))
to_give.move_to(target, quiet=True)
target.msg("%s gives you %s." % (caller.key, to_give.key))
# Call the object script's at_give() method.
to_give.at_give(caller, target)
class CmdSetDesc(COMMAND_DEFAULT_CLASS):
@ -415,7 +417,50 @@ class CmdSay(COMMAND_DEFAULT_CLASS):
# Build the string to emit to neighbors.
emit_string = '%s says, "%s|n"' % (caller.name, speech)
caller.location.msg_contents(emit_string, exclude=caller, from_obj=caller)
caller.location.msg_contents(text=(emit_string, {"type": "say"}),
exclude=caller, from_obj=caller)
class CmdWhisper(COMMAND_DEFAULT_CLASS):
"""
Speak privately as your character to another
Usage:
whisper <player> = <message>
Talk privately to those in your current location, without
others being informed.
"""
key = "whisper"
locks = "cmd:all()"
def func(self):
"""Run the whisper command"""
caller = self.caller
if not self.lhs or not self.rhs:
caller.msg("Usage: whisper <player> = <message>")
return
receiver = caller.search(self.lhs)
if not receiver:
return
if caller == receiver:
caller.msg("You can't whisper to yourself.")
return
speech = self.rhs
# Feedback for the object doing the talking.
caller.msg('You whisper to %s, "%s|n"' % (receiver.key, speech))
# Build the string to emit to receiver.
emit_string = '%s whispers, "%s|n"' % (caller.name, speech)
receiver.msg(text=(emit_string, {"type": "whisper"}), from_obj=caller)
class CmdPose(COMMAND_DEFAULT_CLASS):
@ -458,7 +503,8 @@ class CmdPose(COMMAND_DEFAULT_CLASS):
self.caller.msg(msg)
else:
msg = "%s%s" % (self.caller.name, self.args)
self.caller.location.msg_contents(msg, from_obj=self.caller)
self.caller.location.msg_contents(text=(msg, {"type": "pose"}),
from_obj=self.caller)
class CmdAccess(COMMAND_DEFAULT_CLASS):

View file

@ -60,7 +60,7 @@ class CmdHelp(Command):
if self.session.protocol_key in ("websocket", "ajax/comet"):
try:
options = self.caller.player.db._saved_webclient_options
options = self.player.db._saved_webclient_options
if options and options["helppopup"]:
usemore = False
except KeyError:

View file

@ -9,6 +9,7 @@ from evennia.commands.command import Command
# limit symbol import for API
__all__ = ("MuxCommand", "MuxPlayerCommand")
class MuxCommand(Command):
"""
This sets up the basis for a MUX command. The idea
@ -98,7 +99,7 @@ class MuxCommand(Command):
# split out switches
switches = []
if args and len(args) > 1 and args[0] == "/":
if args and len(args) > 1 and raw[0] == "/":
# we have a switch, or a set of switches. These end with a space.
switches = args[1:].split(None, 1)
if len(switches) > 1:
@ -150,33 +151,32 @@ class MuxCommand(Command):
"""
# a simple test command to show the available properties
string = "-" * 50
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "\n|w%s|n - Command variables from evennia:\n" % self.key
string += "-" * 50
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
string += "cmd locks (self.locks): {w%s{n\n" % self.locks
string += "help category (self.help_category): {w%s{n\n" % self.help_category
string += "object calling (self.caller): {w%s{n\n" % self.caller
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
string += "\nname of cmd (self.key): |w%s|n\n" % self.key
string += "cmd aliases (self.aliases): |w%s|n\n" % self.aliases
string += "cmd locks (self.locks): |w%s|n\n" % self.locks
string += "help category (self.help_category): |w%s|n\n" % self.help_category
string += "object calling (self.caller): |w%s|n\n" % self.caller
string += "object storing cmdset (self.obj): |w%s|n\n" % self.obj
string += "command string given (self.cmdstring): |w%s|n\n" % self.cmdstring
# show cmdset.key instead of cmdset to shorten output
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
string += utils.fill("current cmdset (self.cmdset): |w%s|n\n" % self.cmdset)
string += "\n" + "-" * 50
string += "\nVariables from MuxCommand baseclass\n"
string += "\nVariables from MuxCommand baseclass\n"
string += "-" * 50
string += "\nraw argument (self.raw): {w%s{n \n" % self.raw
string += "cmd args (self.args): {w%s{n\n" % self.args
string += "cmd switches (self.switches): {w%s{n\n" % self.switches
string += "space-separated arg list (self.arglist): {w%s{n\n" % self.arglist
string += "lhs, left-hand side of '=' (self.lhs): {w%s{n\n" % self.lhs
string += "lhs, comma separated (self.lhslist): {w%s{n\n" % self.lhslist
string += "rhs, right-hand side of '=' (self.rhs): {w%s{n\n" % self.rhs
string += "rhs, comma separated (self.rhslist): {w%s{n\n" % self.rhslist
string += "\nraw argument (self.raw): |w%s|n \n" % self.raw
string += "cmd args (self.args): |w%s|n\n" % self.args
string += "cmd switches (self.switches): |w%s|n\n" % self.switches
string += "space-separated arg list (self.arglist): |w%s|n\n" % self.arglist
string += "lhs, left-hand side of '=' (self.lhs): |w%s|n\n" % self.lhs
string += "lhs, comma separated (self.lhslist): |w%s|n\n" % self.lhslist
string += "rhs, right-hand side of '=' (self.rhs): |w%s|n\n" % self.rhs
string += "rhs, comma separated (self.rhslist): |w%s|n\n" % self.rhslist
string += "-" * 50
self.caller.msg(string)
class MuxPlayerCommand(MuxCommand):
"""
This is an on-Player version of the MuxCommand. Since these commands sit

View file

@ -13,7 +13,7 @@ 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, Player 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.
@ -23,7 +23,7 @@ from builtins import range
import time
from django.conf import settings
from evennia.server.sessionhandler import SESSIONS
from evennia.utils import utils, create, search, prettytable, evtable
from evennia.utils import utils, create, search, evtable
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -44,7 +44,7 @@ class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
"""
def parse(self):
"Custom parsing"
"""Custom parsing"""
super(MuxPlayerLookCommand, self).parse()
@ -62,7 +62,7 @@ class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
# store playable property
if self.args:
self.playable = dict((utils.to_str(char.key.lower()), char)
for char in playable).get(self.args.lower(), None)
for char in playable).get(self.args.lower(), None)
else:
self.playable = playable
@ -83,10 +83,10 @@ class CmdOOCLook(MuxPlayerLookCommand):
Look in the ooc state.
"""
#This is an OOC version of the look command. Since a
#Player 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.
# This is an OOC version of the look command. Since a
# Player 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.
key = "look"
aliases = ["l", "ls"]
@ -97,11 +97,11 @@ class CmdOOCLook(MuxPlayerLookCommand):
player_caller = True
def func(self):
"implement the ooc look command"
"""implement the ooc look command"""
if _MULTISESSION_MODE < 2:
# only one character allowed
self.msg("You are out-of-character (OOC).\nUse {w@ic{n to get back into the game.")
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
@ -128,7 +128,7 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
player_caller = True
def func(self):
"create the new character"
"""create the new character"""
player = self.player
if not self.args:
self.msg("Usage: @charcreate <charname> [= description]")
@ -139,8 +139,8 @@ 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):
(player.db._playable_characters and
len(player.db._playable_characters) >= charmax):
self.msg("You may only create a maximum of %i characters." % charmax)
return
from evennia.objects.models import ObjectDB
@ -150,15 +150,13 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
# check if this Character already exists. Note that we are only
# searching the base character typeclass here, not any child
# classes.
self.msg("{rA character named '{w%s{r' already exists.{n" % key)
self.msg("|rA character named '|w%s|r' already exists.|n" % key)
return
# 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
new_character = create.create_object(typeclass, key=key,
location=start_location,
home=default_home,
@ -171,7 +169,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
new_character.db.desc = desc
elif not new_character.db.desc:
new_character.db.desc = "This is a Player."
self.msg("Created new character %s. Use {w@ic %s{n to enter the game as this character." % (new_character.key, new_character.key))
self.msg("Created new character %s. Use |w@ic %s|n to enter the game as this character."
% (new_character.key, new_character.key))
class CmdCharDelete(COMMAND_DEFAULT_CLASS):
@ -188,7 +187,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
help_category = "General"
def func(self):
"delete the character"
"""delete the character"""
player = self.player
if not self.args:
@ -196,23 +195,23 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
return
# use the playable_characters list to search
match = [char for char in utils.make_iter(player.db._playable_characters) if char.key.lower() == self.args.lower()]
match = [char for char in utils.make_iter(player.db._playable_characters)
if char.key.lower() == self.args.lower()]
if not match:
self.msg("You have no such character to delete.")
return
elif len(match) > 1:
self.msg("Aborting - there are two characters with the same name. Ask an admin to delete the right one.")
return
else: # one match
else: # one match
from evennia.utils.evmenu import get_input
def _callback(caller, prompt, result):
def _callback(caller, callback_prompt, result):
if result.lower() == "yes":
# only take action
delobj = caller.ndb._char_to_delete
key = delobj.key
caller.db._playable_characters = [char for char
in caller.db._playable_characters if char != delobj]
caller.db._playable_characters = [pc for pc in caller.db._playable_characters if pc != delobj]
delobj.delete()
self.msg("Character '%s' was permanently deleted." % key)
else:
@ -272,7 +271,8 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
self.msg("That is not a valid character choice.")
return
if len(new_character) > 1:
self.msg("Multiple targets with the same name:\n %s" % ", ".join("%s(#%s)" % (obj.key, obj.id) for obj in new_character))
self.msg("Multiple targets with the same name:\n %s"
% ", ".join("%s(#%s)" % (obj.key, obj.id) for obj in new_character))
return
else:
new_character = new_character[0]
@ -280,7 +280,7 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
player.puppet_object(session, new_character)
player.db._last_puppet = new_character
except RuntimeError as exc:
self.msg("{rYou cannot become {C%s{n: %s" % (new_character.name, exc))
self.msg("|rYou cannot become |C%s|n: %s" % (new_character.name, exc))
# note that this is inheriting from MuxPlayerLookCommand,
@ -306,7 +306,7 @@ class CmdOOC(MuxPlayerLookCommand):
player_caller = True
def func(self):
"Implement function"
"""Implement function"""
player = self.player
session = self.session
@ -322,17 +322,18 @@ class CmdOOC(MuxPlayerLookCommand):
# disconnect
try:
player.unpuppet_object(session)
self.msg("\n{GYou go OOC.{n\n")
self.msg("\n|GYou go OOC.|n\n")
if _MULTISESSION_MODE < 2:
# only one character allowed
self.msg("You are out-of-character (OOC).\nUse {w@ic{n to get back into the game.")
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))
except RuntimeError as exc:
self.msg("{rCould not unpuppet from {c%s{n: %s" % (old_char, exc))
self.msg("|rCould not unpuppet from |c%s|n: %s" % (old_char, exc))
class CmdSessions(COMMAND_DEFAULT_CLASS):
"""
@ -352,23 +353,21 @@ class CmdSessions(COMMAND_DEFAULT_CLASS):
player_caller = True
def func(self):
"Implement function"
"""Implement function"""
player = self.player
sessions = player.sessions.all()
table = prettytable.PrettyTable(["{wsessid",
"{wprotocol",
"{whost",
"{wpuppet/character",
"{wlocation"])
table = evtable.EvTable("|wsessid",
"|wprotocol",
"|whost",
"|wpuppet/character",
"|wlocation")
for sess in sorted(sessions, key=lambda x: x.sessid):
char = player.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",
char and str(char.location) or "N/A"])
string = "{wYour current session(s):{n\n%s" % table
self.msg(string)
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",
char and str(char.location) or "N/A")
self.msg("|wYour current session(s):|n\n%s" % table)
class CmdWho(COMMAND_DEFAULT_CLASS):
@ -408,45 +407,45 @@ class CmdWho(COMMAND_DEFAULT_CLASS):
nplayers = (SESSIONS.player_count())
if show_session_data:
# privileged info
table = prettytable.PrettyTable(["{wPlayer Name",
"{wOn for",
"{wIdle",
"{wPuppeting",
"{wRoom",
"{wCmds",
"{wProtocol",
"{wHost"])
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()
puppet = session.get_puppet()
location = puppet.location.key if puppet and puppet.location else "None"
table.add_row([utils.crop(player.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),
utils.crop(location, width=25),
session.cmd_total,
session.protocol_key,
isinstance(session.address, tuple) and session.address[0] or session.address])
else:
# unprivileged
table = prettytable.PrettyTable(["{wPlayer name", "{wOn for", "{wIdle"])
table = evtable.EvTable("|wPlayer Name",
"|wOn for",
"|wIdle",
"|wPuppeting",
"|wRoom",
"|wCmds",
"|wProtocol",
"|wHost")
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),
utils.time_format(delta_conn, 0),
utils.time_format(delta_cmd, 1)])
isone = nplayers == 1
string = "{wPlayers:{n\n%s\n%s unique account%s logged in." % (table, "One" if isone else nplayers, "" if isone else "s")
self.msg(string)
puppet = session.get_puppet()
location = puppet.location.key if puppet and puppet.location else "None"
table.add_row(utils.crop(player.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),
utils.crop(location, width=25),
session.cmd_total,
session.protocol_key,
isinstance(session.address, tuple) and session.address[0] or session.address)
else:
# unprivileged
table = evtable.EvTable("|wPlayer 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),
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"))
class CmdOption(COMMAND_DEFAULT_CLASS):
@ -489,13 +488,13 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
if "save" in self.switches:
# save all options
self.caller.db._saved_protocol_flags = flags
self.msg("{gSaved all options. Use @option/clear to remove.{n")
self.msg("|gSaved all options. Use @option/clear to remove.|n")
if "clear" in self.switches:
# clear all saves
self.caller.db._saved_protocol_flags = {}
self.msg("{gCleared all saved options.")
self.msg("|gCleared all saved options.")
options = dict(flags) # make a copy of the flag dict
options = dict(flags) # make a copy of the flag dict
saved_options = dict(self.caller.attributes.get("_saved_protocol_flags", default={}))
if "SCREENWIDTH" in options:
@ -503,7 +502,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
options["SCREENWIDTH"] = options["SCREENWIDTH"][0]
else:
options["SCREENWIDTH"] = " \n".join("%s : %s" % (screenid, size)
for screenid, size in options["SCREENWIDTH"].iteritems())
for screenid, size in options["SCREENWIDTH"].iteritems())
if "SCREENHEIGHT" in options:
if len(options["SCREENHEIGHT"]) == 1:
options["SCREENHEIGHT"] = options["SCREENHEIGHT"][0]
@ -521,8 +520,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
changed = "|y*|n" if key in saved_options and flags[key] != saved_options[key] else ""
row.append("%s%s" % (saved, changed))
table.add_row(*row)
self.msg("{wClient settings (%s):|n\n%s|n" % (self.session.protocol_key, table))
self.msg("|wClient settings (%s):|n\n%s|n" % (self.session.protocol_key, table))
return
@ -532,30 +530,30 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
# Try to assign new values
def validate_encoding(val):
def validate_encoding(new_encoding):
# helper: change encoding
try:
utils.to_str(utils.to_unicode("test-string"), encoding=val)
utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
except LookupError:
raise RuntimeError("The encoding '|w%s|n' is invalid. " % val)
raise RuntimeError("The encoding '|w%s|n' is invalid. " % new_encoding)
return val
def validate_size(val):
return {0: int(val)}
def validate_size(new_size):
return {0: int(new_size)}
def validate_bool(val):
return True if val.lower() in ("true", "on", "1") else False
def validate_bool(new_bool):
return True if new_bool.lower() in ("true", "on", "1") else False
def update(name, val, validator):
def update(new_name, new_val, validator):
# helper: update property and report errors
try:
old_val = flags[name]
new_val = validator(val)
flags[name] = new_val
self.msg("Option |w%s|n was changed from '|w%s|n' to '|w%s|n'." % (name, old_val, new_val))
return {name: new_val}
old_val = flags.get(new_name, False)
new_val = validator(new_val)
flags[new_name] = new_val
self.msg("Option |w%s|n was changed from '|w%s|n' to '|w%s|n'." % (new_name, old_val, new_val))
return {new_name: new_val}
except Exception, err:
self.msg("|rCould not set option |w%s|r:|n %s" % (name, err))
self.msg("|rCould not set option |w%s|r:|n %s" % (new_name, err))
return False
validators = {"ANSI": validate_bool,
@ -590,16 +588,15 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
saved_options.update(optiondict)
self.player.attributes.add("_saved_protocol_flags", saved_options)
for key in optiondict:
self.msg("{gSaved option %s.{n" % key)
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.msg("{gCleared saved %s." % key)
self.msg("|gCleared saved %s." % key)
self.session.update_flags(**optiondict)
class CmdPassword(COMMAND_DEFAULT_CLASS):
"""
change your password
@ -616,14 +613,14 @@ class CmdPassword(COMMAND_DEFAULT_CLASS):
player_caller = True
def func(self):
"hook function."
"""hook function."""
player = self.player
if not self.rhs:
self.msg("Usage: @password <oldpass> = <newpass>")
return
oldpass = self.lhslist[0] # this is already stripped by parse()
newpass = self.rhslist[0] # ''
oldpass = self.lhslist[0] # Both of these are
newpass = self.rhslist[0] # already stripped by parse()
if not player.check_password(oldpass):
self.msg("The specified old password isn't correct.")
elif len(newpass) < 3:
@ -654,11 +651,11 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
player_caller = True
def func(self):
"hook function"
"""hook function"""
player = self.player
if 'all' in self.switches:
player.msg("{RQuitting{n all sessions. Hope to see you soon again.", session=self.session)
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)
else:
@ -673,7 +670,6 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
player.disconnect_session_from_player(self.session)
class CmdColorTest(COMMAND_DEFAULT_CLASS):
"""
testing which colors your client support
@ -695,23 +691,23 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
player_caller = True
def table_format(self, table):
"""
Helper method to format the ansi/xterm256 tables.
Takes a table of columns [[val,val,...],[val,val,...],...]
"""
if not table:
return [[]]
"""
Helper method to format the ansi/xterm256 tables.
Takes a table of columns [[val,val,...],[val,val,...],...]
"""
if not table:
return [[]]
extra_space = 1
max_widths = [max([len(str(val)) for val in col]) for col in table]
ftable = []
for irow in range(len(table[0])):
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " * extra_space
for icol, col in enumerate(table)])
return ftable
extra_space = 1
max_widths = [max([len(str(val)) for val in col]) for col in table]
ftable = []
for irow in range(len(table[0])):
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " *
extra_space for icol, col in enumerate(table)])
return ftable
def func(self):
"Show color tables"
"""Show color tables"""
if self.args.startswith("a"):
# show ansi 16-color table
@ -721,9 +717,11 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
# 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 i in range(len(col1)-len(col2))])
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])
string = "ANSI colors:"
for row in table:
@ -742,9 +740,8 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
# foreground table
table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib)))
# background table
table[6+ir].append("|%i%i%i|[%i%i%i%s|n" % (5 - ir, 5 - ig, 5 - ib,
ir, ig, ib,
"||[%i%i%i" % (ir, ig, ib)))
table[6+ir].append("|%i%i%i|[%i%i%i%s|n"
% (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib)))
table = self.table_format(table)
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
string += "\n" + "\n".join("".join(row) for row in table)
@ -753,15 +750,15 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
for igray in range(6):
letter = chr(97 + (ibatch*6 + igray))
inverse = chr(122 - (ibatch*6 + igray))
table[0 + igray].append("|=%s%s |n" % (letter, "||=%s" % (letter)))
table[6 + igray].append("|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % (letter)))
table[0 + igray].append("|=%s%s |n" % (letter, "||=%s" % letter))
table[6 + igray].append("|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter))
for igray in range(6):
# the last row (y, z) has empty columns
if igray < 2:
letter = chr(121 + igray)
inverse = chr(98 - igray)
fg = "|=%s%s |n" % (letter, "||=%s" % (letter))
bg = "|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % (letter))
fg = "|=%s%s |n" % (letter, "||=%s" % letter)
bg = "|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter)
else:
fg, bg = " ", " "
table[0 + igray].append(fg)
@ -800,7 +797,7 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
player_caller = True
def _recache_locks(self, player):
"Helper method to reset the lockhandler on an already puppeted object"
"""Helper method to reset the lockhandler on an already puppeted object"""
if self.session:
char = self.session.puppet
if char:
@ -811,25 +808,26 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
player.locks.reset()
def func(self):
"Perform the command"
"""Perform the command"""
player = self.player
permstr = player.is_superuser and " (superuser)" or " (%s)" % (", ".join(player.permissions.all()))
permstr = player.is_superuser and " (superuser)" or "(%s)" % (", ".join(player.permissions.all()))
if self.cmdstring == '@unquell':
if not player.attributes.get('_quell'):
self.msg("Already using normal Player permissions%s." % permstr)
self.msg("Already using normal Player permissions %s." % permstr)
else:
player.attributes.remove('_quell')
self.msg("Player permissions%s restored." % permstr)
self.msg("Player permissions %s restored." % permstr)
else:
if player.attributes.get('_quell'):
self.msg("Already quelling Player%s permissions." % permstr)
self.msg("Already quelling Player %s permissions." % permstr)
return
player.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, the lowest of the two will be used.)" % permstr
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," \
" the lowest of the two will be used.)" % permstr
cpermstr += "\nUse @unquell to return to normal permission usage."
self.msg(cpermstr)
else:

View file

@ -109,7 +109,7 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Define function"
"""Define function"""
# Only allow shutdown if caller has session
if not self.caller.sessions.get():
return
@ -126,6 +126,7 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
def _py_load(caller):
return ""
def _py_code(caller, buf):
"""
Execute the buffer.
@ -139,10 +140,12 @@ def _py_code(caller, buf):
show_input=False)
return True
def _py_quit(caller):
del caller.db._py_measure_time
caller.msg("Exited the code editor.")
def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
show_input=True):
"""
@ -174,9 +177,9 @@ def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
if show_input:
try:
caller.msg(">>> %s" % pycode, session=session,
options={"raw":True})
options={"raw": True})
except TypeError:
caller.msg(">>> %s" % pycode, options={"raw":True})
caller.msg(">>> %s" % pycode, options={"raw": True})
try:
try:
@ -204,9 +207,10 @@ def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
ret = "\n".join("%s" % line for line in errlist if line)
try:
caller.msg(ret, session=session, options={"raw":True})
caller.msg(ret, session=session, options={"raw": True})
except TypeError:
caller.msg(ret, options={"raw":True})
caller.msg(ret, options={"raw": True})
class CmdPy(COMMAND_DEFAULT_CLASS):
"""
@ -234,8 +238,8 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
You can explore The evennia API from inside the game by calling
evennia.help(), evennia.managers.help() etc.
{rNote: In the wrong hands this command is a severe security risk.
It should only be accessible by trusted server admins/superusers.{n
|rNote: In the wrong hands this command is a severe security risk.
It should only be accessible by trusted server admins/superusers.|n
"""
key = "@py"
@ -244,7 +248,7 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"hook function"
"""hook function"""
caller = self.caller
pycode = self.args
@ -266,13 +270,14 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
# helper function. Kept outside so it can be imported and run
# by other commands.
def format_script_list(scripts):
"Takes a list of scripts and formats the output."
"""Takes a list of scripts and formats the output."""
if not scripts:
return "<No scripts>"
table = EvTable("{wdbref{n", "{wobj{n", "{wkey{n", "{wintval{n", "{wnext{n",
"{wrept{n", "{wdb", "{wtypeclass{n", "{wdesc{n",
table = EvTable("|wdbref|n", "|wobj|n", "|wkey|n", "|wintval|n", "|wnext|n",
"|wrept|n", "|wdb", "|wtypeclass|n", "|wdesc|n",
align='r', border="tablecols")
for script in scripts:
nextrep = script.time_until_next_repeat()
@ -326,12 +331,11 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"implement method"
"""implement method"""
caller = self.caller
args = self.args
string = ""
if args:
if "start" in self.switches:
# global script-start mode
@ -374,9 +378,9 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
else:
string = "Stopping script '%s'." % scripts[0].key
scripts[0].stop()
#import pdb
#pdb.set_trace()
ScriptDB.objects.validate() #just to be sure all is synced
# import pdb # DEBUG
# pdb.set_trace() # DEBUG
ScriptDB.objects.validate() # just to be sure all is synced
else:
# multiple matches.
string = "Multiple script matches. Please refine your search:\n"
@ -409,26 +413,21 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Implement the command"
"""Implement the command"""
caller = self.caller
if self.args and self.args.isdigit():
nlim = int(self.args)
else:
nlim = 10
nlim = int(self.args) if self.args and self.args.isdigit() else 10
nobjs = ObjectDB.objects.count()
base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS
nchars = ObjectDB.objects.filter(db_typeclass_path=base_char_typeclass).count()
nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(db_typeclass_path=base_char_typeclass).count()
nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(
db_typeclass_path=base_char_typeclass).count()
nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count()
nother = nobjs - nchars - nrooms - nexits
nobjs = nobjs or 1 # fix zero-div error with empty database
nobjs = nobjs or 1 # fix zero-div error with empty database
# total object sum table
totaltable = EvTable("{wtype{n", "{wcomment{n", "{wcount{n", "{w%%{n", border="table", align="l")
totaltable = EvTable("|wtype|n", "|wcomment|n", "|wcount|n", "|w%%|n", border="table", align="l")
totaltable.align = 'l'
totaltable.add_row("Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars) / nobjs) * 100))
totaltable.add_row("Rooms", "(location=None)", nrooms, "%.2f" % ((float(nrooms) / nobjs) * 100))
@ -436,7 +435,7 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
totaltable.add_row("Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100))
# typeclass table
typetable = EvTable("{wtypeclass{n", "{wcount{n", "{w%%{n", border="table", align="l")
typetable = EvTable("|wtypeclass|n", "|wcount|n", "|w%%|n", border="table", align="l")
typetable.align = 'l'
dbtotals = ObjectDB.objects.object_totals()
for path, count in dbtotals.items():
@ -444,15 +443,15 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
# last N table
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim):]
latesttable = EvTable("{wcreated{n", "{wdbref{n", "{wname{n", "{wtypeclass{n", align="l", border="table")
latesttable = EvTable("|wcreated|n", "|wdbref|n", "|wname|n", "|wtypeclass|n", align="l", border="table")
latesttable.align = 'l'
for obj in objs:
latesttable.add_row(utils.datetime_format(obj.date_created),
obj.dbref, obj.key, obj.path)
string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (nobjs, totaltable)
string += "\n{wObject typeclass distribution:{n\n%s" % typetable
string += "\n{wLast %s Objects created:{n\n%s" % (min(nobjs, nlim), latesttable)
string = "\n|wObject subtype totals (out of %i Objects):|n\n%s" % (nobjs, totaltable)
string += "\n|wObject typeclass distribution:|n\n%s" % typetable
string += "\n|wLast %s Objects created:|n\n%s" % (min(nobjs, nlim), latesttable)
caller.msg(string)
@ -473,7 +472,7 @@ class CmdPlayers(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"List the players"
"""List the players"""
caller = self.caller
if self.args and self.args.isdigit():
@ -485,17 +484,17 @@ class CmdPlayers(COMMAND_DEFAULT_CLASS):
# typeclass table
dbtotals = PlayerDB.objects.object_totals()
typetable = EvTable("{wtypeclass{n", "{wcount{n", "{w%%{n", border="cells", align="l")
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))
# last N table
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
latesttable = EvTable("{wcreated{n", "{wdbref{n", "{wname{n", "{wtypeclass{n", border="cells", align="l")
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|wPlayer typeclass distribution:|n\n%s" % typetable
string += "\n|wLast %s Players created:|n\n%s" % (min(nplayers, nlim), latesttable)
caller.msg(string)
@ -525,7 +524,7 @@ class CmdService(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Implement command"
"""Implement command"""
caller = self.caller
switches = self.switches
@ -540,9 +539,9 @@ class CmdService(COMMAND_DEFAULT_CLASS):
if not switches or switches[0] == "list":
# Just display the list of installed services and their
# status, then exit.
table = EvTable("{wService{n (use @services/start|stop|delete)", "{wstatus", align="l")
table = EvTable("|wService|n (use @services/start|stop|delete)", "|wstatus", align="l")
for service in service_collection.services:
table.add_row(service.name, service.running and "{gRunning" or "{rNot Running")
table.add_row(service.name, service.running and "|gRunning" or "|rNot Running")
caller.msg(unicode(table))
return
@ -584,7 +583,7 @@ class CmdService(COMMAND_DEFAULT_CLASS):
return
if switches[0] == "start":
#Starts a service.
# Attempt to start a service.
if service.running:
caller.msg('That service is already running.')
return
@ -608,23 +607,23 @@ class CmdAbout(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Show the version"
"""Display information about server or target"""
string = """
{cEvennia{n %s{n
|cEvennia|n %s|n
MUD/MUX/MU* development system
{wLicence{n BSD 3-Clause Licence
{wWeb{n http://www.evennia.com
{wIrc{n #evennia on FreeNode
{wForum{n http://www.evennia.com/discussions
{wMaintainer{n (2010-) Griatch (griatch AT gmail DOT com)
{wMaintainer{n (2006-10) Greg Taylor
|wLicence|n https://opensource.org/licenses/BSD-3-Clause
|wWeb|n http://www.evennia.com
|wIrc|n #evennia on FreeNode
|wForum|n http://www.evennia.com/discussions
|wMaintainer|n (2010-) Griatch (griatch AT gmail DOT com)
|wMaintainer|n (2006-10) Greg Taylor
{wOS{n %s
{wPython{n %s
{wTwisted{n %s
{wDjango{n %s
|wOS|n %s
|wPython|n %s
|wTwisted|n %s
|wDjango|n %s
""" % (utils.get_evennia_version(),
os.name,
sys.version.split()[0],
@ -649,8 +648,8 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Show server time data in a table."
table1 = EvTable("|wServer time","", align="l", width=78)
"""Show server time data in a table."""
table1 = EvTable("|wServer time", "", align="l", width=78)
table1.add_row("Current uptime", utils.time_format(gametime.uptime(), 3))
table1.add_row("Total runtime", utils.time_format(gametime.runtime(), 2))
table1.add_row("First start", datetime.datetime.fromtimestamp(gametime.server_epoch()))
@ -682,20 +681,20 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
Some Important statistics in the table:
{wServer load{n is an average of processor usage. It's usually
|wServer load|n is an average of processor usage. It's usually
between 0 (no usage) and 1 (100% usage), but may also be
temporarily higher if your computer has multiple CPU cores.
The {wResident/Virtual memory{n displays the total memory used by
The |wResident/Virtual memory|n displays the total memory used by
the server process.
Evennia {wcaches{n all retrieved database entities when they are
Evennia |wcaches|n all retrieved database entities when they are
loaded by use of the idmapper functionality. This allows Evennia
to maintain the same instances of an entity and allowing
non-persistent storage schemes. The total amount of cached objects
are displayed plus a breakdown of database object types.
The {wflushmem{n switch allows to flush the object cache. Please
The |wflushmem|n switch allows to flush the object cache. Please
note that due to how Python's memory management works, releasing
caches may not show you a lower Residual/Virtual memory footprint,
the released memory will instead be re-used by the program.
@ -707,7 +706,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
help_category = "System"
def func(self):
"Show list."
"""Show list."""
global _IDMAPPER
if not _IDMAPPER:
@ -741,21 +740,21 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
if has_psutil:
loadavg = psutil.cpu_percent()
_mem = psutil.virtual_memory()
rmem = _mem.used / (1000.0 * 1000)
rmem = _mem.used / (1000.0 * 1000)
pmem = _mem.percent
if "mem" in self.switches:
string = "Total computer memory usage: {w%g{n MB (%g%%)"
string = "Total computer memory usage: |w%g|n MB (%g%%)"
self.caller.msg(string % (rmem, pmem))
return
# Display table
loadtable = EvTable("property", "statistic", align="l")
loadtable.add_row("Total CPU load", "%g %%" % loadavg)
loadtable.add_row("Total computer memory usage","%g MB (%g%%)" % (rmem, pmem))
loadtable.add_row("Total computer memory usage", "%g MB (%g%%)" % (rmem, pmem))
loadtable.add_row("Process ID", "%g" % pid),
else:
loadtable = "Not available on Windows without 'psutil' library " \
"(install with {wpip install psutil{n)."
"(install with |wpip install psutil|n)."
else:
# Linux / BSD (OSX) - proper pid-based statistics
@ -767,46 +766,49 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
loadavg = os.getloadavg()[0]
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1000.0 # resident memory
vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1000.0 # virtual memory
pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # percent of resident memory to total
pmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "%mem")).read()) # % of resident memory to total
rusage = _RESOURCE.getrusage(_RESOURCE.RUSAGE_SELF)
if "mem" in self.switches:
string = "Memory usage: RMEM: {w%g{n MB (%g%%), " \
" VMEM (res+swap+cache): {w%g{n MB."
string = "Memory usage: RMEM: |w%g|n MB (%g%%), VMEM (res+swap+cache): |w%g|n MB."
self.caller.msg(string % (rmem, pmem, vmem))
return
loadtable = EvTable("property", "statistic", align="l")
loadtable.add_row("Server load (1 min)", "%g" % loadavg)
loadtable.add_row("Process ID", "%g" % pid),
loadtable.add_row("Memory usage","%g MB (%g%%)" % (rmem, pmem))
loadtable.add_row("Memory usage", "%g MB (%g%%)" % (rmem, pmem))
loadtable.add_row("Virtual address space", "")
loadtable.add_row("{x(resident+swap+caching){n", "%g MB" % vmem)
loadtable.add_row("CPU time used (total)", "%s (%gs)" % (utils.time_format(rusage.ru_utime), rusage.ru_utime))
loadtable.add_row("CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime))
loadtable.add_row("Page faults", "%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap))
loadtable.add_row("|x(resident+swap+caching)|n", "%g MB" % vmem)
loadtable.add_row("CPU time used (total)", "%s (%gs)"
% (utils.time_format(rusage.ru_utime), rusage.ru_utime))
loadtable.add_row("CPU time used (user)", "%s (%gs)"
% (utils.time_format(rusage.ru_stime), rusage.ru_stime))
loadtable.add_row("Page faults", "%g hard, %g soft, %g swapouts"
% (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap))
loadtable.add_row("Disk I/O", "%g reads, %g writes" % (rusage.ru_inblock, rusage.ru_oublock))
loadtable.add_row("Network I/O", "%g in, %g out" % (rusage.ru_msgrcv, rusage.ru_msgsnd))
loadtable.add_row("Context switching", "%g vol, %g forced, %g signals" % (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals))
loadtable.add_row("Context switching", "%g vol, %g forced, %g signals"
% (rusage.ru_nvcsw, rusage.ru_nivcsw, rusage.ru_nsignals))
# os-generic
string = "{wServer CPU and Memory load:{n\n%s" % loadtable
string = "|wServer CPU and Memory load:|n\n%s" % loadtable
# object cache count (note that sys.getsiseof is not called so this works for pypy too.
total_num, cachedict = _IDMAPPER.cache_size()
sorted_cache = sorted([(key, num) for key, num in cachedict.items() if num > 0],
key=lambda tup: tup[1], reverse=True)
key=lambda tup: tup[1], reverse=True)
memtable = EvTable("entity name", "number", "idmapper %", align="l")
for tup in sorted_cache:
memtable.add_row(tup[0], "%i" % tup[1], "%.2f" % (float(tup[1]) / total_num * 100))
string += "\n{w Entity idmapper cache:{n %i items\n%s" % (total_num, memtable)
string += "\n|w Entity idmapper cache:|n %i items\n%s" % (total_num, memtable)
# return to caller
self.caller.msg(string)
class CmdTickers(COMMAND_DEFAULT_CLASS):
"""
View running tickers
@ -832,13 +834,9 @@ class CmdTickers(COMMAND_DEFAULT_CLASS):
table = EvTable("interval (s)", "object", "path/methodname", "idstring", "db")
for sub in all_subs:
table.add_row(sub[3],
"%s%s" % (sub[0] or "[None]", sub[0] and " (#%s)" % (sub[0].id if hasattr(sub[0], "id") else "") or ""),
"%s%s" % (sub[0] or "[None]",
sub[0] and " (#%s)" % (sub[0].id if hasattr(sub[0], "id") else "") or ""),
sub[1] if sub[1] else sub[2],
sub[4] or "[Unset]",
"*" if sub[5] else "-")
self.caller.msg("|wActive tickers|n:\n" + unicode(table))

View file

@ -38,7 +38,7 @@ class CommandTest(EvenniaTest):
Tests a command
"""
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None, receiver=None, cmdstring=None):
def call(self, cmdobj, args, msg=None, cmdset=None, noansi=True, caller=None, receiver=None, cmdstring=None, obj=None):
"""
Test a command by assigning all the needed
properties to cmdobj and running
@ -48,6 +48,10 @@ class CommandTest(EvenniaTest):
cmdobj.at_post_cmd()
The msgreturn value is compared to eventual
output sent to caller.msg in the game
Returns:
msg (str): The received message that was sent to the caller.
"""
caller = caller if caller else self.char1
receiver = receiver if receiver else caller
@ -60,9 +64,10 @@ class CommandTest(EvenniaTest):
cmdobj.session = SESSIONS.session_from_sessid(1)
cmdobj.player = self.player
cmdobj.raw_string = cmdobj.key + " " + args
cmdobj.obj = caller if caller else self.char1
cmdobj.obj = obj or (caller if caller else self.char1)
# test
old_msg = receiver.msg
returned_msg = ""
try:
receiver.msg = Mock()
cmdobj.at_pre_cmd()
@ -74,18 +79,23 @@ class CommandTest(EvenniaTest):
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
stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg]
returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg)
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
if msg is not None:
returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg)
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()):
sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n"
sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n"
sep3 = "\n" + "="*78
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
raise AssertionError(retval)
else:
returned_msg = "\n".join(stored_msg)
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
finally:
receiver.msg = old_msg
return returned_msg
# ------------------------------------------------------------
# Individual module Tests
# ------------------------------------------------------------
@ -119,6 +129,9 @@ class TestGeneral(CommandTest):
def test_say(self):
self.call(general.CmdSay(), "Testing", "You say, \"Testing\"")
def test_whisper(self):
self.call(general.CmdWhisper(), "Obj = Testing", "You whisper to Obj, \"Testing\"")
def test_access(self):
self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):")

View file

@ -30,6 +30,8 @@ CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
# would also block dummyrunner, so it's not added as default.
_LATEST_FAILED_LOGINS = defaultdict(list)
def _throttle(session, maxlim=None, timeout=None, storage=_LATEST_FAILED_LOGINS):
"""
This will check the session's address against the
@ -95,8 +97,8 @@ def create_guest_player(session):
bans = ServerConfig.objects.conf("server_bans")
if bans and any(tup[2].match(session.address) for tup in bans if tup[2]):
# this is a banned IP!
string = "{rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.{x"
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return True, None
@ -118,14 +120,11 @@ def create_guest_player(session):
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
ptypeclass = settings.BASE_GUEST_TYPECLASS
new_player = _create_player(session, playername, password,
permissions, ptypeclass)
new_player = _create_player(session, playername, password, permissions, ptypeclass)
if new_player:
_create_character(session, new_player, typeclass,
home, permissions)
_create_character(session, new_player, typeclass, home, permissions)
return True, new_player
except Exception:
# We are in the middle between logged in and -not, so we have
# to handle tracebacks ourselves at this point. If we don't,
@ -150,7 +149,7 @@ def create_normal_player(session, name, password):
# check for too many login errors too quick.
if _throttle(session, maxlim=5, timeout=5*60):
# timeout is 5 minutes.
session.msg("{RYou made too many connection attempts. Try again in a few minutes.{n")
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
return None
# Match account name and check password
@ -167,15 +166,14 @@ def create_normal_player(session, name, password):
player.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] == player.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!
string = "{rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.{x"
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return None
@ -213,7 +211,7 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
# check for too many login errors too quick.
if _throttle(session, maxlim=5, timeout=5*60, storage=_LATEST_FAILED_LOGINS):
# timeout is 5 minutes.
session.msg("{RYou made too many connection attempts. Try again in a few minutes.{n")
session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n")
return
args = self.args
@ -237,12 +235,6 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
name, password = parts
player = create_normal_player(session, name, password)
if player:
# actually do the login. This will call all other hooks:
# session.at_login()
# player.at_init() # always called when object is loaded from disk
# player.at_first_login() # only once, for player-centric setup
# player.at_pre_login()
# player.at_post_login(session=session)
session.sessionhandler.login(session, player)
@ -264,7 +256,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
arg_regex = r"\s.*?|$"
def func(self):
"Do checks and create account"
"""Do checks and create account"""
session = self.caller
args = self.args.strip()
@ -309,12 +301,12 @@ 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] == playername.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!
string = "{rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.{x"
string = "|rYou have been banned and cannot continue from here." \
"\nIf you feel this ban is in error, please email an admin.|x"
session.msg(string)
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
return
@ -327,8 +319,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
if new_player:
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_player, typeclass, default_home, permissions)
# tell the caller everything went well.
string = "A new account '%s' was created. Welcome!"
if " " in playername:
@ -361,7 +352,7 @@ class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS):
locks = "cmd:all()"
def func(self):
"Simply close the connection."
"""Simply close the connection."""
session = self.caller
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
@ -383,7 +374,7 @@ class CmdUnconnectedLook(COMMAND_DEFAULT_CLASS):
locks = "cmd:all()"
def func(self):
"Show the connect screen."
"""Show the connect screen."""
connection_screen = utils.random_string_from_module(CONNECTION_SCREEN_MODULE)
if not connection_screen:
connection_screen = "No connection screen found. Please contact an admin."
@ -405,25 +396,25 @@ class CmdUnconnectedHelp(COMMAND_DEFAULT_CLASS):
locks = "cmd:all()"
def func(self):
"Shows help"
"""Shows help"""
string = \
"""
You are not yet logged into the game. Commands available at this point:
{wcreate{n - create a new account
{wconnect{n - connect with an existing account
{wlook{n - re-show the connection screen
{whelp{n - show this help
{wencoding{n - change the text encoding to match your client
{wscreenreader{n - make the server more suitable for use with screen readers
{wquit{n - abort the connection
|wcreate|n - create a new account
|wconnect|n - connect with an existing account
|wlook|n - re-show the connection screen
|whelp|n - show this help
|wencoding|n - change the text encoding to match your client
|wscreenreader|n - make the server more suitable for use with screen readers
|wquit|n - abort the connection
First create an account e.g. with {wcreate Anna c67jHL8p{n
(If you have spaces in your name, use double quotes: {wcreate "Anna the Barbarian" c67jHL8p{n
Next you can connect to the game: {wconnect Anna c67jHL8p{n
First create an account e.g. with |wcreate Anna c67jHL8p|n
(If you have spaces in your name, use double quotes: |wcreate "Anna the Barbarian" c67jHL8p|n
Next you can connect to the game: |wconnect Anna c67jHL8p|n
You can use the {wlook{n command if you want to see the connect screen again.
You can use the |wlook|n command if you want to see the connect screen again.
"""
self.caller.msg(string)
@ -479,10 +470,10 @@ class CmdUnconnectedEncoding(COMMAND_DEFAULT_CLASS):
pencoding = self.session.protocol_flags.get("ENCODING", None)
string = ""
if pencoding:
string += "Default encoding: {g%s{n (change with {w@encoding <encoding>{n)" % pencoding
string += "Default encoding: |g%s|n (change with |w@encoding <encoding>|n)" % pencoding
encodings = settings.ENCODINGS
if encodings:
string += "\nServer's alternative encodings (tested in this order):\n {g%s{n" % ", ".join(encodings)
string += "\nServer's alternative encodings (tested in this order):\n |g%s|n" % ", ".join(encodings)
if not string:
string = "No encodings found."
else:
@ -492,7 +483,8 @@ class CmdUnconnectedEncoding(COMMAND_DEFAULT_CLASS):
try:
utils.to_str(utils.to_unicode("test-string"), encoding=encoding)
except LookupError:
string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n" % (encoding, old_encoding)
string = "|rThe encoding '|w%s|r' is invalid. Keeping the previous encoding '|w%s|r'.|n"\
% (encoding, old_encoding)
else:
self.session.protocol_flags["ENCODING"] = encoding
string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (old_encoding, encoding)
@ -501,6 +493,7 @@ class CmdUnconnectedEncoding(COMMAND_DEFAULT_CLASS):
self.session.sessionhandler.session_portal_sync(self.session)
self.caller.msg(string.strip())
class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
"""
Activate screenreader mode.
@ -515,21 +508,20 @@ class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
aliases = "@screenreader"
def func(self):
"Flips screenreader setting."
"""Flips screenreader setting."""
new_setting = not self.session.protocol_flags.get("SCREENREADER", False)
self.session.protocol_flags["SCREENREADER"] = new_setting
string = "Screenreader mode turned {w%s{n." % ("on" if new_setting else "off")
self.session.protocol_flags["SCREENREADER"] = new_setting
string = "Screenreader mode turned |w%s|n." % ("on" if new_setting else "off")
self.caller.msg(string)
self.session.sessionhandler.session_portal_sync(self.session)
def _create_player(session, playername, password, permissions, typeclass=None):
def _create_player(session, playername, password, permissions, typeclass=None, email=None):
"""
Helper function, creates a player of the specified typeclass.
"""
try:
new_player = create.create_player(playername, None, password,
permissions=permissions, typeclass=typeclass)
new_player = create.create_player(playername, 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)
@ -543,7 +535,7 @@ def _create_player(session, playername, password, permissions, typeclass=None):
# join the new player to the public channel
pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"])
if not pchannel.connect(new_player):
if not pchannel or not pchannel.connect(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_err(string)
return new_player
@ -555,8 +547,7 @@ def _create_character(session, new_player, typeclass, home, permissions):
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_player.key, home=home, permissions=permissions)
# set playable character list
new_player.db._playable_characters.append(new_character)