Merge conflicts against master, including cmdhandler support for direct cmdobject input together with prefix-ignore mechanism from devel.
This commit is contained in:
commit
a648433db8
69 changed files with 2617 additions and 1771 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -1,4 +1,23 @@
|
||||||
# Evennia Changelog
|
# Evennia Changelog
|
||||||
|
## Feb 2017:
|
||||||
|
New devel branch created, to lead up to Evennia 0.7.
|
||||||
|
|
||||||
|
## Dec 2016:
|
||||||
|
Lots of bugfixes and considerable uptick in contributors. Unittest coverage
|
||||||
|
and PEP8 adoption and refactoring.
|
||||||
|
|
||||||
|
## May 2016:
|
||||||
|
Evennia 0.6 with completely reworked Out-of-band system, making
|
||||||
|
the message path completely flexible and built around input/outputfuncs.
|
||||||
|
A completely new webclient, split into the evennia.js library and a
|
||||||
|
gui library, making it easier to customize.
|
||||||
|
|
||||||
|
## Feb 2016:
|
||||||
|
Added the new EvMenu and EvMore utilities, updated EvEdit and cleaned up
|
||||||
|
a lot of the batchcommand functionality. Started work on new Devel branch.
|
||||||
|
|
||||||
|
## Sept 2015:
|
||||||
|
Evennia 0.5. Merged devel branch, full library format implemented.
|
||||||
|
|
||||||
## Feb 2015:
|
## Feb 2015:
|
||||||
Development currently in devel/ branch. Moved typeclasses to use
|
Development currently in devel/ branch. Moved typeclasses to use
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ likely file a bug report with the Evennia project.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
_ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \
|
_ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \
|
||||||
"reached for '{raw_string}' ({cmdclass})."
|
"reached for '{raw_cmdname}' ({cmdclass})."
|
||||||
|
|
||||||
|
|
||||||
def _msg_err(receiver, stringtuple):
|
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:
|
for lobj in local_objlist:
|
||||||
try:
|
try:
|
||||||
# call hook in case we need to do dynamic changing to cmdset
|
# 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:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
# the call-type lock is checked here, it makes sure a player
|
# 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
|
@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.
|
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.
|
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 "player" - the session will help
|
||||||
retrieve the correct cmdsets from puppeted objects.
|
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:
|
||||||
kwargs (any): other keyword arguments will be assigned as named variables on the
|
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
|
@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
|
Helper function: This initializes and runs the Command
|
||||||
instance once the parser has identified it as either a normal
|
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:
|
Args:
|
||||||
cmd (Command): Command object
|
cmd (Command): Command object
|
||||||
cmdname (str): Name of command
|
cmdname (str): Name of command
|
||||||
|
args (str): extra text entered after the identified command
|
||||||
raw_cmdname (str): Name of Command, unaffected by eventual
|
raw_cmdname (str): Name of Command, unaffected by eventual
|
||||||
prefix-stripping (if no prefix-stripping, this is the same
|
prefix-stripping (if no prefix-stripping, this is the same
|
||||||
as cmdname).
|
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:
|
Returns:
|
||||||
deferred (Deferred): this will fire with the return of the
|
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
|
_COMMAND_NESTING[called_by] += 1
|
||||||
if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
|
if _COMMAND_NESTING[called_by] > _COMMAND_RECURSION_LIMIT:
|
||||||
err = _ERROR_RECURSION_LIMIT.format(recursion_limit=_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__)
|
cmdclass=cmd.__class__)
|
||||||
raise RuntimeError(err)
|
raise RuntimeError(err)
|
||||||
|
|
||||||
|
|
@ -543,7 +556,18 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
|
|
||||||
try: # catch bugs in cmdhandler itself
|
try: # catch bugs in cmdhandler itself
|
||||||
try: # catch special-type commands
|
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
|
||||||
|
|
||||||
|
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, player, obj,
|
||||||
callertype, raw_string)
|
callertype, raw_string)
|
||||||
if not cmdset:
|
if not cmdset:
|
||||||
|
|
@ -614,7 +638,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
raise ExecSystemCommand(cmd, sysarg)
|
raise ExecSystemCommand(cmd, sysarg)
|
||||||
|
|
||||||
# A normal command.
|
# 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)
|
returnValue(ret)
|
||||||
|
|
||||||
except ErrorReported as exc:
|
except ErrorReported as exc:
|
||||||
|
|
@ -629,7 +653,8 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
sysarg = exc.sysarg
|
sysarg = exc.sysarg
|
||||||
|
|
||||||
if syscmd:
|
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)
|
returnValue(ret)
|
||||||
elif sysarg:
|
elif sysarg:
|
||||||
# return system arg
|
# return system arg
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ def _init_command(cls, **kwargs):
|
||||||
if not hasattr(cls, 'locks'):
|
if not hasattr(cls, 'locks'):
|
||||||
# default if one forgets to define completely
|
# default if one forgets to define completely
|
||||||
cls.locks = "cmd:all()"
|
cls.locks = "cmd:all()"
|
||||||
if not "cmd:" in cls.locks:
|
if "cmd:" not in cls.locks:
|
||||||
cls.locks = "cmd:all();" + cls.locks
|
cls.locks = "cmd:all();" + cls.locks
|
||||||
for lockstring in cls.locks.split(';'):
|
for lockstring in cls.locks.split(';'):
|
||||||
if lockstring and not ':' in lockstring:
|
if lockstring and not ':' in lockstring:
|
||||||
|
|
@ -197,7 +197,6 @@ class Command(with_metaclass(CommandMeta, object)):
|
||||||
try:
|
try:
|
||||||
# first assume input is a command (the most common case)
|
# first assume input is a command (the most common case)
|
||||||
return self._matchset.intersection(cmd._matchset)
|
return self._matchset.intersection(cmd._matchset)
|
||||||
#return cmd.key in self._matchset
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# probably got a string
|
# probably got a string
|
||||||
return cmd in self._matchset
|
return cmd in self._matchset
|
||||||
|
|
@ -211,9 +210,8 @@ class Command(with_metaclass(CommandMeta, object)):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._matchset.isdisjoint(cmd._matchset)
|
return self._matchset.isdisjoint(cmd._matchset)
|
||||||
#return not cmd.key in self._matcheset
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return not cmd in self._matchset
|
return cmd not in self._matchset
|
||||||
|
|
||||||
def __contains__(self, query):
|
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,
|
def msg(self, text=None, to_obj=None, from_obj=None,
|
||||||
session=None, **kwargs):
|
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
|
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.
|
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
|
# a simple test command to show the available properties
|
||||||
string = "-" * 50
|
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 += "-" * 50
|
||||||
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
|
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 aliases (self.aliases): |w%s|n\n" % self.aliases
|
||||||
string += "cmd locks (self.locks): {w%s{n\n" % self.locks
|
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 += "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 calling (self.caller): |w%s|n\n" % self.caller
|
||||||
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
|
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 += "command string given (self.cmdstring): |w%s|n\n" % self.cmdstring
|
||||||
# show cmdset.key instead of cmdset to shorten output
|
# 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)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
|
|
@ -9,7 +9,7 @@ import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.server.sessionhandler import SESSIONS
|
from evennia.server.sessionhandler import SESSIONS
|
||||||
from evennia.server.models import ServerConfig
|
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)
|
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implementing the function"
|
"""Implementing the function"""
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS):
|
||||||
# Carry out the booting of the sessions in the boot list.
|
# Carry out the booting of the sessions in the boot list.
|
||||||
|
|
||||||
feedback = None
|
feedback = None
|
||||||
if not 'quiet' in self.switches:
|
if 'quiet' not in self.switches:
|
||||||
feedback = "You have been disconnected by %s.\n" % caller.name
|
feedback = "You have been disconnected by %s.\n" % caller.name
|
||||||
if reason:
|
if reason:
|
||||||
feedback += "\nReason given: %s" % reason
|
feedback += "\nReason given: %s" % reason
|
||||||
|
|
@ -108,13 +108,12 @@ def list_bans(banlist):
|
||||||
if not banlist:
|
if not banlist:
|
||||||
return "No active bans were found."
|
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):
|
for inum, ban in enumerate(banlist):
|
||||||
table.add_row([str(inum + 1),
|
table.add_row(str(inum + 1),
|
||||||
ban[0] and ban[0] or ban[1],
|
ban[0] and ban[0] or ban[1],
|
||||||
ban[3], ban[4]])
|
ban[3], ban[4])
|
||||||
string = "{wActive bans:{n\n%s" % table
|
return "|wActive bans:|n\n%s" % table
|
||||||
return string
|
|
||||||
|
|
||||||
|
|
||||||
class CmdBan(COMMAND_DEFAULT_CLASS):
|
class CmdBan(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -202,7 +201,7 @@ class CmdBan(COMMAND_DEFAULT_CLASS):
|
||||||
# save updated banlist
|
# save updated banlist
|
||||||
banlist.append(bantup)
|
banlist.append(bantup)
|
||||||
ServerConfig.objects.conf('server_bans', banlist)
|
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):
|
class CmdUnban(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -223,7 +222,7 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement unbanning"
|
"""Implement unbanning"""
|
||||||
|
|
||||||
banlist = ServerConfig.objects.conf('server_bans')
|
banlist = ServerConfig.objects.conf('server_bans')
|
||||||
|
|
||||||
|
|
@ -240,7 +239,7 @@ class CmdUnban(COMMAND_DEFAULT_CLASS):
|
||||||
if not banlist:
|
if not banlist:
|
||||||
self.caller.msg("There are no bans to clear.")
|
self.caller.msg("There are no bans to clear.")
|
||||||
elif not (0 < num < len(banlist) + 1):
|
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:
|
else:
|
||||||
# all is ok, clear ban
|
# all is ok, clear ban
|
||||||
ban = banlist[num - 1]
|
ban = banlist[num - 1]
|
||||||
|
|
@ -270,7 +269,7 @@ class CmdDelPlayer(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements the command."
|
"""Implements the command."""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
@ -346,7 +345,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the command"
|
"""Implement the command"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
@ -382,7 +381,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS):
|
||||||
obj = caller.search(objname, global_search=True)
|
obj = caller.search(objname, global_search=True)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
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)
|
caller.msg("%s is not a room. Ignored." % objname)
|
||||||
continue
|
continue
|
||||||
if players_only and not obj.has_player:
|
if players_only and not obj.has_player:
|
||||||
|
|
@ -414,7 +413,7 @@ class CmdNewPassword(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the function."
|
"""Implement the function."""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
|
|
@ -455,7 +454,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement function"
|
"""Implement function"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
switches = self.switches
|
switches = self.switches
|
||||||
|
|
@ -481,7 +480,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
caller.msg("You are not allowed to examine this object.")
|
caller.msg("You are not allowed to examine this object.")
|
||||||
return
|
return
|
||||||
|
|
||||||
string = "Permissions on {w%s{n: " % obj.key
|
string = "Permissions on |w%s|n: " % obj.key
|
||||||
if not obj.permissions.all():
|
if not obj.permissions.all():
|
||||||
string += "<None>"
|
string += "<None>"
|
||||||
else:
|
else:
|
||||||
|
|
@ -495,22 +494,23 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
# we supplied an argument on the form obj = perm
|
# we supplied an argument on the form obj = perm
|
||||||
|
locktype = "edit" if playermode else "control"
|
||||||
if not obj.access(caller, 'control'):
|
if not obj.access(caller, locktype):
|
||||||
caller.msg("You are not allowed to edit this object's permissions.")
|
caller.msg("You are not allowed to edit this %s's permissions."
|
||||||
|
% ("player" if playermode else "object"))
|
||||||
return
|
return
|
||||||
|
|
||||||
cstring = ""
|
caller_result = []
|
||||||
tstring = ""
|
target_result = []
|
||||||
if 'del' in switches:
|
if 'del' in switches:
|
||||||
# delete the given permission(s) from object.
|
# delete the given permission(s) from object.
|
||||||
for perm in self.rhslist:
|
for perm in self.rhslist:
|
||||||
obj.permissions.remove(perm)
|
obj.permissions.remove(perm)
|
||||||
if obj.permissions.get(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:
|
else:
|
||||||
cstring += "\nPermission %s removed from %s (if they existed)." % (perm, obj.name)
|
caller_result.append("\nPermission %s removed from %s (if they existed)." % (perm, obj.name))
|
||||||
tstring += "\n%s revokes the permission(s) %s from you." % (caller.name, perm)
|
target_result.append("\n%s revokes the permission(s) %s from you." % (caller.name, perm))
|
||||||
else:
|
else:
|
||||||
# add a new permission
|
# add a new permission
|
||||||
permissions = obj.permissions.all()
|
permissions = obj.permissions.all()
|
||||||
|
|
@ -525,15 +525,16 @@ class CmdPerm(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
if perm in permissions:
|
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:
|
else:
|
||||||
obj.permissions.add(perm)
|
obj.permissions.add(perm)
|
||||||
plystring = "the Player" if playermode else "the Object/Character"
|
plystring = "the Player" if playermode else "the Object/Character"
|
||||||
cstring += "\nPermission '%s' given to %s (%s)." % (rhs, obj.name, plystring)
|
caller_result.append("\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)
|
target_result.append("\n%s gives you (%s, %s) the permission '%s'."
|
||||||
caller.msg(cstring.strip())
|
% (caller.name, obj.name, plystring, rhs))
|
||||||
if tstring:
|
caller.msg("".join(caller_result).strip())
|
||||||
obj.msg(tstring.strip())
|
if target_result:
|
||||||
|
obj.msg("".join(target_result).strip())
|
||||||
|
|
||||||
|
|
||||||
class CmdWall(COMMAND_DEFAULT_CLASS):
|
class CmdWall(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -550,7 +551,7 @@ class CmdWall(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Admin"
|
help_category = "Admin"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implements command"
|
"""Implements command"""
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.caller.msg("Usage: @wall <message>")
|
self.caller.msg("Usage: @wall <message>")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,13 @@ from evennia.utils import logger, utils
|
||||||
_RE_COMMENT = re.compile(r"^#.*?$", re.MULTILINE + re.DOTALL)
|
_RE_COMMENT = re.compile(r"^#.*?$", re.MULTILINE + re.DOTALL)
|
||||||
_RE_CODE_START = re.compile(r"^# batchcode code:", re.MULTILINE)
|
_RE_CODE_START = re.compile(r"^# batchcode code:", re.MULTILINE)
|
||||||
_COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
_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
|
# limit symbols for API inclusion
|
||||||
__all__ = ("CmdBatchCommands", "CmdBatchCode")
|
__all__ = ("CmdBatchCommands", "CmdBatchCode")
|
||||||
|
|
||||||
_HEADER_WIDTH = 70
|
_HEADER_WIDTH = 70
|
||||||
_UTF8_ERROR = \
|
_UTF8_ERROR = """
|
||||||
"""
|
|rDecode error in '%s'.|n
|
||||||
{rDecode error in '%s'.{n
|
|
||||||
|
|
||||||
This file contains non-ascii character(s). This is common if you
|
This file contains non-ascii character(s). This is common if you
|
||||||
wrote some input in a language that has more letters and special
|
wrote some input in a language that has more letters and special
|
||||||
|
|
@ -83,9 +81,9 @@ print "leaving run ..."
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Helper functions
|
# Helper functions
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
def format_header(caller, entry):
|
def format_header(caller, entry):
|
||||||
"""
|
"""
|
||||||
|
|
@ -100,7 +98,7 @@ def format_header(caller, entry):
|
||||||
header = utils.crop(entry, width=width)
|
header = utils.crop(entry, width=width)
|
||||||
ptr = caller.ndb.batch_stackptr + 1
|
ptr = caller.ndb.batch_stackptr + 1
|
||||||
stacklen = len(caller.ndb.batch_stack)
|
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.
|
# add extra space to the side for padding.
|
||||||
header = "%s%s" % (header, " " * (width - len(header)))
|
header = "%s%s" % (header, " " * (width - len(header)))
|
||||||
header = header.replace('\n', '\\n')
|
header = header.replace('\n', '\\n')
|
||||||
|
|
@ -114,7 +112,7 @@ def format_code(entry):
|
||||||
"""
|
"""
|
||||||
code = ""
|
code = ""
|
||||||
for line in entry.split('\n'):
|
for line in entry.split('\n'):
|
||||||
code += "\n{G>>>{n %s" % line
|
code += "\n|G>>>|n %s" % line
|
||||||
return code.strip()
|
return code.strip()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -164,9 +162,9 @@ def step_pointer(caller, step=1):
|
||||||
stack = caller.ndb.batch_stack
|
stack = caller.ndb.batch_stack
|
||||||
nstack = len(stack)
|
nstack = len(stack)
|
||||||
if ptr + step <= 0:
|
if ptr + step <= 0:
|
||||||
caller.msg("{RBeginning of batch file.")
|
caller.msg("|RBeginning of batch file.")
|
||||||
if ptr + step >= nstack:
|
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))
|
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)
|
string = format_header(caller, entry)
|
||||||
codeall = entry.strip()
|
codeall = entry.strip()
|
||||||
string += "{G(hh for help)"
|
string += "|G(hh for help)"
|
||||||
if showall:
|
if showall:
|
||||||
for line in codeall.split('\n'):
|
for line in codeall.split('\n'):
|
||||||
string += "\n{G|{n %s" % line
|
string += "\n|G||n %s" % line
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -217,9 +215,9 @@ def purge_processor(caller):
|
||||||
|
|
||||||
caller.scripts.validate() # this will purge interactive mode
|
caller.scripts.validate() # this will purge interactive mode
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# main access commands
|
# main access commands
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -243,7 +241,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Starts the processor."
|
"""Starts the processor."""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
|
|
@ -289,7 +287,8 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
caller.msg("\nBatch-command processor - Interactive mode for %s ..." % python_path)
|
caller.msg("\nBatch-command processor - Interactive mode for %s ..." % python_path)
|
||||||
show_curr(caller)
|
show_curr(caller)
|
||||||
else:
|
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
|
procpool = False
|
||||||
if "PythonProcPool" in utils.server_services():
|
if "PythonProcPool" in utils.server_services():
|
||||||
|
|
@ -301,11 +300,11 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
if procpool:
|
if procpool:
|
||||||
# run in parallel process
|
# run in parallel process
|
||||||
def callback(r):
|
def callback(r):
|
||||||
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
caller.msg(" |GBatchfile '%s' applied." % python_path)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
def errback(e):
|
def errback(e):
|
||||||
caller.msg(" {RError from processor: '%s'" % e)
|
caller.msg(" |RError from processor: '%s'" % e)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
utils.run_async(_PROCPOOL_BATCHCMD_SOURCE,
|
utils.run_async(_PROCPOOL_BATCHCMD_SOURCE,
|
||||||
|
|
@ -315,7 +314,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
at_err=errback)
|
at_err=errback)
|
||||||
else:
|
else:
|
||||||
# run in-process (might block)
|
# run in-process (might block)
|
||||||
for inum in range(len(commands)):
|
for _ in range(len(commands)):
|
||||||
# loop through the batch file
|
# loop through the batch file
|
||||||
if not batch_cmd_exec(caller):
|
if not batch_cmd_exec(caller):
|
||||||
return
|
return
|
||||||
|
|
@ -323,7 +322,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS):
|
||||||
# clean out the safety cmdset and clean out all other
|
# clean out the safety cmdset and clean out all other
|
||||||
# temporary attrs.
|
# temporary attrs.
|
||||||
string = " Batchfile '%s' applied." % python_path
|
string = " Batchfile '%s' applied." % python_path
|
||||||
caller.msg("{G%s" % string)
|
caller.msg("|G%s" % string)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -352,7 +351,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "Building"
|
help_category = "Building"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Starts the processor."
|
"""Starts the processor."""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
|
|
@ -410,11 +409,11 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
|
||||||
if procpool:
|
if procpool:
|
||||||
# run in parallel process
|
# run in parallel process
|
||||||
def callback(r):
|
def callback(r):
|
||||||
caller.msg(" {GBatchfile '%s' applied." % python_path)
|
caller.msg(" |GBatchfile '%s' applied." % python_path)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
def errback(e):
|
def errback(e):
|
||||||
caller.msg(" {RError from processor: '%s'" % e)
|
caller.msg(" |RError from processor: '%s'" % e)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE,
|
utils.run_async(_PROCPOOL_BATCHCODE_SOURCE,
|
||||||
codes=codes,
|
codes=codes,
|
||||||
|
|
@ -423,7 +422,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
|
||||||
at_err=errback)
|
at_err=errback)
|
||||||
else:
|
else:
|
||||||
# un in-process (will block)
|
# un in-process (will block)
|
||||||
for inum in range(len(codes)):
|
for _ in range(len(codes)):
|
||||||
# loop through the batch file
|
# loop through the batch file
|
||||||
if not batch_code_exec(caller):
|
if not batch_code_exec(caller):
|
||||||
return
|
return
|
||||||
|
|
@ -431,14 +430,14 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS):
|
||||||
# clean out the safety cmdset and clean out all other
|
# clean out the safety cmdset and clean out all other
|
||||||
# temporary attrs.
|
# temporary attrs.
|
||||||
string = " Batchfile '%s' applied." % python_path
|
string = " Batchfile '%s' applied." % python_path
|
||||||
caller.msg("{G%s" % string)
|
caller.msg("|G%s" % string)
|
||||||
purge_processor(caller)
|
purge_processor(caller)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# State-commands for the interactive batch processor modes
|
# State-commands for the interactive batch processor modes
|
||||||
# (these are the same for both processors)
|
# (these are the same for both processors)
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class CmdStateAbort(_COMMAND_DEFAULT_CLASS):
|
class CmdStateAbort(_COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
|
|
@ -453,7 +452,7 @@ class CmdStateAbort(_COMMAND_DEFAULT_CLASS):
|
||||||
locks = "cmd:perm(batchcommands)"
|
locks = "cmd:perm(batchcommands)"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Exit back to default."
|
"""Exit back to default."""
|
||||||
purge_processor(self.caller)
|
purge_processor(self.caller)
|
||||||
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
|
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):
|
def func(self):
|
||||||
show_curr(self.caller, showall=True)
|
show_curr(self.caller, showall=True)
|
||||||
|
|
||||||
|
|
||||||
class CmdStatePP(_COMMAND_DEFAULT_CLASS):
|
class CmdStatePP(_COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
pp
|
pp
|
||||||
|
|
@ -644,7 +644,7 @@ class CmdStateSS(_COMMAND_DEFAULT_CLASS):
|
||||||
else:
|
else:
|
||||||
step = 1
|
step = 1
|
||||||
|
|
||||||
for istep in range(step):
|
for _ in range(step):
|
||||||
if caller.ndb.batch_batchmode == "batch_code":
|
if caller.ndb.batch_batchmode == "batch_code":
|
||||||
batch_code_exec(caller)
|
batch_code_exec(caller)
|
||||||
else:
|
else:
|
||||||
|
|
@ -673,7 +673,7 @@ class CmdStateSL(_COMMAND_DEFAULT_CLASS):
|
||||||
else:
|
else:
|
||||||
step = 1
|
step = 1
|
||||||
|
|
||||||
for istep in range(step):
|
for _ in range(step):
|
||||||
if caller.ndb.batch_batchmode == "batch_code":
|
if caller.ndb.batch_batchmode == "batch_code":
|
||||||
batch_code_exec(caller)
|
batch_code_exec(caller)
|
||||||
else:
|
else:
|
||||||
|
|
@ -699,7 +699,7 @@ class CmdStateCC(_COMMAND_DEFAULT_CLASS):
|
||||||
ptr = caller.ndb.batch_stackptr
|
ptr = caller.ndb.batch_stackptr
|
||||||
step = nstack - ptr
|
step = nstack - ptr
|
||||||
|
|
||||||
for istep in range(step):
|
for _ in range(step):
|
||||||
if caller.ndb.batch_batchmode == "batch_code":
|
if caller.ndb.batch_batchmode == "batch_code":
|
||||||
batch_code_exec(caller)
|
batch_code_exec(caller)
|
||||||
else:
|
else:
|
||||||
|
|
@ -775,7 +775,7 @@ class CmdStateQQ(_COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
|
|
||||||
class CmdStateHH(_COMMAND_DEFAULT_CLASS):
|
class CmdStateHH(_COMMAND_DEFAULT_CLASS):
|
||||||
"Help command"
|
"""Help command"""
|
||||||
|
|
||||||
key = "hh"
|
key = "hh"
|
||||||
help_category = "BatchProcess"
|
help_category = "BatchProcess"
|
||||||
|
|
@ -810,12 +810,12 @@ class CmdStateHH(_COMMAND_DEFAULT_CLASS):
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Defining the cmdsets for the interactive batchprocessor
|
# Defining the cmdsets for the interactive batchprocessor
|
||||||
# mode (same for both processors)
|
# mode (same for both processors)
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class BatchSafeCmdSet(CmdSet):
|
class BatchSafeCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
|
|
@ -827,7 +827,7 @@ class BatchSafeCmdSet(CmdSet):
|
||||||
priority = 150 # override other cmdsets.
|
priority = 150 # override other cmdsets.
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"Init the cmdset"
|
"""Init the cmdset"""
|
||||||
self.add(CmdStateAbort())
|
self.add(CmdStateAbort())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -839,7 +839,7 @@ class BatchInteractiveCmdSet(CmdSet):
|
||||||
priority = 104
|
priority = 104
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"init the cmdset"
|
"""init the cmdset"""
|
||||||
self.add(CmdStateAbort())
|
self.add(CmdStateAbort())
|
||||||
self.add(CmdStateLL())
|
self.add(CmdStateLL())
|
||||||
self.add(CmdStatePP())
|
self.add(CmdStatePP())
|
||||||
|
|
|
||||||
|
|
@ -591,9 +591,11 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
desc = self.args
|
desc = self.args
|
||||||
|
if obj.access(caller, "edit"):
|
||||||
obj.db.desc = desc
|
obj.db.desc = desc
|
||||||
caller.msg("The description was set on %s." % obj.get_display_name(caller))
|
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):
|
class CmdDestroy(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -1138,7 +1140,7 @@ class CmdName(ObjManipCommand):
|
||||||
caller.msg("No name defined!")
|
caller.msg("No name defined!")
|
||||||
return
|
return
|
||||||
if not (obj.access(caller, "control") or obj.access(caller, "edit")):
|
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
|
return
|
||||||
obj.username = newname
|
obj.username = newname
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class CharacterCmdSet(CmdSet):
|
||||||
self.add(general.CmdDrop())
|
self.add(general.CmdDrop())
|
||||||
self.add(general.CmdGive())
|
self.add(general.CmdGive())
|
||||||
self.add(general.CmdSay())
|
self.add(general.CmdSay())
|
||||||
|
self.add(general.CmdWhisper())
|
||||||
self.add(general.CmdAccess())
|
self.add(general.CmdAccess())
|
||||||
|
|
||||||
# The help system
|
# The help system
|
||||||
|
|
|
||||||
|
|
@ -60,16 +60,17 @@ class CmdLook(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
Handle the looking.
|
Handle the looking.
|
||||||
"""
|
"""
|
||||||
|
caller = self.caller
|
||||||
if not self.args:
|
if not self.args:
|
||||||
target = self.caller.location
|
target = caller.location
|
||||||
if not target:
|
if not target:
|
||||||
self.caller.msg("You have no location to look at!")
|
caller.msg("You have no location to look at!")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
target = self.caller.search(self.args)
|
target = caller.search(self.args, use_dbref=caller.check_permstring("Builders"))
|
||||||
if not target:
|
if not target:
|
||||||
return
|
return
|
||||||
self.msg(self.caller.at_look(target))
|
self.msg(caller.at_look(target))
|
||||||
|
|
||||||
|
|
||||||
class CmdNick(COMMAND_DEFAULT_CLASS):
|
class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -175,10 +176,9 @@ class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||||
errstring += "Not a valid nick index."
|
errstring += "Not a valid nick index."
|
||||||
else:
|
else:
|
||||||
errstring += "Nick not found."
|
errstring += "Nick not found."
|
||||||
|
|
||||||
if "delete" in switches or "del" in switches:
|
if "delete" in switches or "del" in switches:
|
||||||
# clear the nick
|
# 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)
|
caller.nicks.remove(old_nickstring, category=nicktype)
|
||||||
string += "\nNick removed: '|w%s|n' -> |w%s|n." % (old_nickstring, old_replstring)
|
string += "\nNick removed: '|w%s|n' -> |w%s|n." % (old_nickstring, old_replstring)
|
||||||
else:
|
else:
|
||||||
|
|
@ -354,6 +354,8 @@ class CmdGive(COMMAND_DEFAULT_CLASS):
|
||||||
caller.msg("You give %s to %s." % (to_give.key, target.key))
|
caller.msg("You give %s to %s." % (to_give.key, target.key))
|
||||||
to_give.move_to(target, quiet=True)
|
to_give.move_to(target, quiet=True)
|
||||||
target.msg("%s gives you %s." % (caller.key, to_give.key))
|
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):
|
class CmdSetDesc(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -415,7 +417,50 @@ class CmdSay(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# Build the string to emit to neighbors.
|
# Build the string to emit to neighbors.
|
||||||
emit_string = '%s says, "%s|n"' % (caller.name, speech)
|
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):
|
class CmdPose(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -458,7 +503,8 @@ class CmdPose(COMMAND_DEFAULT_CLASS):
|
||||||
self.caller.msg(msg)
|
self.caller.msg(msg)
|
||||||
else:
|
else:
|
||||||
msg = "%s%s" % (self.caller.name, self.args)
|
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):
|
class CmdAccess(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class CmdHelp(Command):
|
||||||
|
|
||||||
if self.session.protocol_key in ("websocket", "ajax/comet"):
|
if self.session.protocol_key in ("websocket", "ajax/comet"):
|
||||||
try:
|
try:
|
||||||
options = self.caller.player.db._saved_webclient_options
|
options = self.player.db._saved_webclient_options
|
||||||
if options and options["helppopup"]:
|
if options and options["helppopup"]:
|
||||||
usemore = False
|
usemore = False
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from evennia.commands.command import Command
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("MuxCommand", "MuxPlayerCommand")
|
__all__ = ("MuxCommand", "MuxPlayerCommand")
|
||||||
|
|
||||||
|
|
||||||
class MuxCommand(Command):
|
class MuxCommand(Command):
|
||||||
"""
|
"""
|
||||||
This sets up the basis for a MUX command. The idea
|
This sets up the basis for a MUX command. The idea
|
||||||
|
|
@ -98,7 +99,7 @@ class MuxCommand(Command):
|
||||||
|
|
||||||
# split out switches
|
# split out switches
|
||||||
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.
|
# we have a switch, or a set of switches. These end with a space.
|
||||||
switches = args[1:].split(None, 1)
|
switches = args[1:].split(None, 1)
|
||||||
if len(switches) > 1:
|
if len(switches) > 1:
|
||||||
|
|
@ -150,33 +151,32 @@ class MuxCommand(Command):
|
||||||
"""
|
"""
|
||||||
# a simple test command to show the available properties
|
# a simple test command to show the available properties
|
||||||
string = "-" * 50
|
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 += "-" * 50
|
||||||
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
|
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 aliases (self.aliases): |w%s|n\n" % self.aliases
|
||||||
string += "cmd locks (self.locks): {w%s{n\n" % self.locks
|
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 += "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 calling (self.caller): |w%s|n\n" % self.caller
|
||||||
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
|
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 += "command string given (self.cmdstring): |w%s|n\n" % self.cmdstring
|
||||||
# show cmdset.key instead of cmdset to shorten output
|
# 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 += "\n" + "-" * 50
|
||||||
string += "\nVariables from MuxCommand baseclass\n"
|
string += "\nVariables from MuxCommand baseclass\n"
|
||||||
string += "-" * 50
|
string += "-" * 50
|
||||||
string += "\nraw argument (self.raw): {w%s{n \n" % self.raw
|
string += "\nraw argument (self.raw): |w%s|n \n" % self.raw
|
||||||
string += "cmd args (self.args): {w%s{n\n" % self.args
|
string += "cmd args (self.args): |w%s|n\n" % self.args
|
||||||
string += "cmd switches (self.switches): {w%s{n\n" % self.switches
|
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 += "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, 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 += "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, 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 += "rhs, comma separated (self.rhslist): |w%s|n\n" % self.rhslist
|
||||||
string += "-" * 50
|
string += "-" * 50
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class MuxPlayerCommand(MuxCommand):
|
class MuxPlayerCommand(MuxCommand):
|
||||||
"""
|
"""
|
||||||
This is an on-Player version of the MuxCommand. Since these commands sit
|
This is an on-Player version of the MuxCommand. Since these commands sit
|
||||||
|
|
|
||||||
|
|
@ -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
|
these commands are triggered with a connected character (such as the
|
||||||
case of the @ooc command), it is None if we are OOC.
|
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
|
self.msg() and similar methods to reroute returns to the correct
|
||||||
method. Otherwise all text will be returned to all connected sessions.
|
method. Otherwise all text will be returned to all connected sessions.
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ from builtins import range
|
||||||
import time
|
import time
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.server.sessionhandler import SESSIONS
|
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)
|
COMMAND_DEFAULT_CLASS = utils.class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ class MuxPlayerLookCommand(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
"Custom parsing"
|
"""Custom parsing"""
|
||||||
|
|
||||||
super(MuxPlayerLookCommand, self).parse()
|
super(MuxPlayerLookCommand, self).parse()
|
||||||
|
|
||||||
|
|
@ -97,11 +97,11 @@ class CmdOOCLook(MuxPlayerLookCommand):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"implement the ooc look command"
|
"""implement the ooc look command"""
|
||||||
|
|
||||||
if _MULTISESSION_MODE < 2:
|
if _MULTISESSION_MODE < 2:
|
||||||
# only one character allowed
|
# 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
|
return
|
||||||
|
|
||||||
# call on-player look helper method
|
# call on-player look helper method
|
||||||
|
|
@ -128,7 +128,7 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"create the new character"
|
"""create the new character"""
|
||||||
player = self.player
|
player = self.player
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.msg("Usage: @charcreate <charname> [= description]")
|
self.msg("Usage: @charcreate <charname> [= description]")
|
||||||
|
|
@ -150,15 +150,13 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||||
# check if this Character already exists. Note that we are only
|
# check if this Character already exists. Note that we are only
|
||||||
# searching the base character typeclass here, not any child
|
# searching the base character typeclass here, not any child
|
||||||
# classes.
|
# 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
|
return
|
||||||
|
|
||||||
# create the character
|
# create the character
|
||||||
start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
|
start_location = ObjectDB.objects.get_id(settings.START_LOCATION)
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||||
|
|
||||||
|
|
||||||
new_character = create.create_object(typeclass, key=key,
|
new_character = create.create_object(typeclass, key=key,
|
||||||
location=start_location,
|
location=start_location,
|
||||||
home=default_home,
|
home=default_home,
|
||||||
|
|
@ -171,7 +169,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS):
|
||||||
new_character.db.desc = desc
|
new_character.db.desc = desc
|
||||||
elif not new_character.db.desc:
|
elif not new_character.db.desc:
|
||||||
new_character.db.desc = "This is a Player."
|
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):
|
class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -188,7 +187,7 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"delete the character"
|
"""delete the character"""
|
||||||
player = self.player
|
player = self.player
|
||||||
|
|
||||||
if not self.args:
|
if not self.args:
|
||||||
|
|
@ -196,7 +195,8 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
# use the playable_characters list to search
|
# 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:
|
if not match:
|
||||||
self.msg("You have no such character to delete.")
|
self.msg("You have no such character to delete.")
|
||||||
return
|
return
|
||||||
|
|
@ -206,13 +206,12 @@ class CmdCharDelete(COMMAND_DEFAULT_CLASS):
|
||||||
else: # one match
|
else: # one match
|
||||||
from evennia.utils.evmenu import get_input
|
from evennia.utils.evmenu import get_input
|
||||||
|
|
||||||
def _callback(caller, prompt, result):
|
def _callback(caller, callback_prompt, result):
|
||||||
if result.lower() == "yes":
|
if result.lower() == "yes":
|
||||||
# only take action
|
# only take action
|
||||||
delobj = caller.ndb._char_to_delete
|
delobj = caller.ndb._char_to_delete
|
||||||
key = delobj.key
|
key = delobj.key
|
||||||
caller.db._playable_characters = [char for char
|
caller.db._playable_characters = [pc for pc in caller.db._playable_characters if pc != delobj]
|
||||||
in caller.db._playable_characters if char != delobj]
|
|
||||||
delobj.delete()
|
delobj.delete()
|
||||||
self.msg("Character '%s' was permanently deleted." % key)
|
self.msg("Character '%s' was permanently deleted." % key)
|
||||||
else:
|
else:
|
||||||
|
|
@ -272,7 +271,8 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
|
||||||
self.msg("That is not a valid character choice.")
|
self.msg("That is not a valid character choice.")
|
||||||
return
|
return
|
||||||
if len(new_character) > 1:
|
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
|
return
|
||||||
else:
|
else:
|
||||||
new_character = new_character[0]
|
new_character = new_character[0]
|
||||||
|
|
@ -280,7 +280,7 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
|
||||||
player.puppet_object(session, new_character)
|
player.puppet_object(session, new_character)
|
||||||
player.db._last_puppet = new_character
|
player.db._last_puppet = new_character
|
||||||
except RuntimeError as exc:
|
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,
|
# note that this is inheriting from MuxPlayerLookCommand,
|
||||||
|
|
@ -306,7 +306,7 @@ class CmdOOC(MuxPlayerLookCommand):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement function"
|
"""Implement function"""
|
||||||
|
|
||||||
player = self.player
|
player = self.player
|
||||||
session = self.session
|
session = self.session
|
||||||
|
|
@ -322,17 +322,18 @@ class CmdOOC(MuxPlayerLookCommand):
|
||||||
# disconnect
|
# disconnect
|
||||||
try:
|
try:
|
||||||
player.unpuppet_object(session)
|
player.unpuppet_object(session)
|
||||||
self.msg("\n{GYou go OOC.{n\n")
|
self.msg("\n|GYou go OOC.|n\n")
|
||||||
|
|
||||||
if _MULTISESSION_MODE < 2:
|
if _MULTISESSION_MODE < 2:
|
||||||
# only one character allowed
|
# 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
|
return
|
||||||
|
|
||||||
self.msg(player.at_look(target=self.playable, session=session))
|
self.msg(player.at_look(target=self.playable, session=session))
|
||||||
|
|
||||||
except RuntimeError as exc:
|
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):
|
class CmdSessions(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
|
|
@ -352,23 +353,21 @@ class CmdSessions(COMMAND_DEFAULT_CLASS):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement function"
|
"""Implement function"""
|
||||||
player = self.player
|
player = self.player
|
||||||
sessions = player.sessions.all()
|
sessions = player.sessions.all()
|
||||||
|
table = evtable.EvTable("|wsessid",
|
||||||
table = prettytable.PrettyTable(["{wsessid",
|
"|wprotocol",
|
||||||
"{wprotocol",
|
"|whost",
|
||||||
"{whost",
|
"|wpuppet/character",
|
||||||
"{wpuppet/character",
|
"|wlocation")
|
||||||
"{wlocation"])
|
|
||||||
for sess in sorted(sessions, key=lambda x: x.sessid):
|
for sess in sorted(sessions, key=lambda x: x.sessid):
|
||||||
char = player.get_puppet(sess)
|
char = player.get_puppet(sess)
|
||||||
table.add_row([str(sess.sessid), str(sess.protocol_key),
|
table.add_row(str(sess.sessid), str(sess.protocol_key),
|
||||||
type(sess.address) == tuple and sess.address[0] or sess.address,
|
type(sess.address) == tuple and sess.address[0] or sess.address,
|
||||||
char and str(char) or "None",
|
char and str(char) or "None",
|
||||||
char and str(char.location) or "N/A"])
|
char and str(char.location) or "N/A")
|
||||||
string = "{wYour current session(s):{n\n%s" % table
|
self.msg("|wYour current session(s):|n\n%s" % table)
|
||||||
self.msg(string)
|
|
||||||
|
|
||||||
|
|
||||||
class CmdWho(COMMAND_DEFAULT_CLASS):
|
class CmdWho(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -408,45 +407,45 @@ class CmdWho(COMMAND_DEFAULT_CLASS):
|
||||||
nplayers = (SESSIONS.player_count())
|
nplayers = (SESSIONS.player_count())
|
||||||
if show_session_data:
|
if show_session_data:
|
||||||
# privileged info
|
# privileged info
|
||||||
table = prettytable.PrettyTable(["{wPlayer Name",
|
table = evtable.EvTable("|wPlayer Name",
|
||||||
"{wOn for",
|
"|wOn for",
|
||||||
"{wIdle",
|
"|wIdle",
|
||||||
"{wPuppeting",
|
"|wPuppeting",
|
||||||
"{wRoom",
|
"|wRoom",
|
||||||
"{wCmds",
|
"|wCmds",
|
||||||
"{wProtocol",
|
"|wProtocol",
|
||||||
"{wHost"])
|
"|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"])
|
|
||||||
for session in session_list:
|
for session in session_list:
|
||||||
if not session.logged_in:
|
if not session.logged_in:
|
||||||
continue
|
continue
|
||||||
delta_cmd = time.time() - session.cmd_last_visible
|
delta_cmd = time.time() - session.cmd_last_visible
|
||||||
delta_conn = time.time() - session.conn_time
|
delta_conn = time.time() - session.conn_time
|
||||||
player = session.get_player()
|
player = session.get_player()
|
||||||
table.add_row([utils.crop(player.key, width=25),
|
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_conn, 0),
|
||||||
utils.time_format(delta_cmd, 1)])
|
utils.time_format(delta_cmd, 1),
|
||||||
|
utils.crop(puppet.key if puppet else "None", width=25),
|
||||||
isone = nplayers == 1
|
utils.crop(location, width=25),
|
||||||
string = "{wPlayers:{n\n%s\n%s unique account%s logged in." % (table, "One" if isone else nplayers, "" if isone else "s")
|
session.cmd_total,
|
||||||
self.msg(string)
|
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):
|
class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
@ -489,11 +488,11 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
if "save" in self.switches:
|
if "save" in self.switches:
|
||||||
# save all options
|
# save all options
|
||||||
self.caller.db._saved_protocol_flags = flags
|
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:
|
if "clear" in self.switches:
|
||||||
# clear all saves
|
# clear all saves
|
||||||
self.caller.db._saved_protocol_flags = {}
|
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={}))
|
saved_options = dict(self.caller.attributes.get("_saved_protocol_flags", default={}))
|
||||||
|
|
@ -521,8 +520,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
changed = "|y*|n" if key in saved_options and flags[key] != saved_options[key] else ""
|
changed = "|y*|n" if key in saved_options and flags[key] != saved_options[key] else ""
|
||||||
row.append("%s%s" % (saved, changed))
|
row.append("%s%s" % (saved, changed))
|
||||||
table.add_row(*row)
|
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
|
return
|
||||||
|
|
||||||
|
|
@ -532,30 +530,30 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# Try to assign new values
|
# Try to assign new values
|
||||||
|
|
||||||
def validate_encoding(val):
|
def validate_encoding(new_encoding):
|
||||||
# helper: change encoding
|
# helper: change encoding
|
||||||
try:
|
try:
|
||||||
utils.to_str(utils.to_unicode("test-string"), encoding=val)
|
utils.to_str(utils.to_unicode("test-string"), encoding=new_encoding)
|
||||||
except LookupError:
|
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
|
return val
|
||||||
|
|
||||||
def validate_size(val):
|
def validate_size(new_size):
|
||||||
return {0: int(val)}
|
return {0: int(new_size)}
|
||||||
|
|
||||||
def validate_bool(val):
|
def validate_bool(new_bool):
|
||||||
return True if val.lower() in ("true", "on", "1") else False
|
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
|
# helper: update property and report errors
|
||||||
try:
|
try:
|
||||||
old_val = flags[name]
|
old_val = flags.get(new_name, False)
|
||||||
new_val = validator(val)
|
new_val = validator(new_val)
|
||||||
flags[name] = new_val
|
flags[new_name] = new_val
|
||||||
self.msg("Option |w%s|n was changed from '|w%s|n' to '|w%s|n'." % (name, old_val, 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 {name: new_val}
|
return {new_name: new_val}
|
||||||
except Exception, err:
|
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
|
return False
|
||||||
|
|
||||||
validators = {"ANSI": validate_bool,
|
validators = {"ANSI": validate_bool,
|
||||||
|
|
@ -590,16 +588,15 @@ class CmdOption(COMMAND_DEFAULT_CLASS):
|
||||||
saved_options.update(optiondict)
|
saved_options.update(optiondict)
|
||||||
self.player.attributes.add("_saved_protocol_flags", saved_options)
|
self.player.attributes.add("_saved_protocol_flags", saved_options)
|
||||||
for key in optiondict:
|
for key in optiondict:
|
||||||
self.msg("{gSaved option %s.{n" % key)
|
self.msg("|gSaved option %s.|n" % key)
|
||||||
if "clear" in self.switches:
|
if "clear" in self.switches:
|
||||||
# clear this save
|
# clear this save
|
||||||
for key in optiondict:
|
for key in optiondict:
|
||||||
self.player.attributes.get("_saved_protocol_flags", {}).pop(key, None)
|
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)
|
self.session.update_flags(**optiondict)
|
||||||
|
|
||||||
|
|
||||||
class CmdPassword(COMMAND_DEFAULT_CLASS):
|
class CmdPassword(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
change your password
|
change your password
|
||||||
|
|
@ -616,14 +613,14 @@ class CmdPassword(COMMAND_DEFAULT_CLASS):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"hook function."
|
"""hook function."""
|
||||||
|
|
||||||
player = self.player
|
player = self.player
|
||||||
if not self.rhs:
|
if not self.rhs:
|
||||||
self.msg("Usage: @password <oldpass> = <newpass>")
|
self.msg("Usage: @password <oldpass> = <newpass>")
|
||||||
return
|
return
|
||||||
oldpass = self.lhslist[0] # this is already stripped by parse()
|
oldpass = self.lhslist[0] # Both of these are
|
||||||
newpass = self.rhslist[0] # ''
|
newpass = self.rhslist[0] # already stripped by parse()
|
||||||
if not player.check_password(oldpass):
|
if not player.check_password(oldpass):
|
||||||
self.msg("The specified old password isn't correct.")
|
self.msg("The specified old password isn't correct.")
|
||||||
elif len(newpass) < 3:
|
elif len(newpass) < 3:
|
||||||
|
|
@ -654,11 +651,11 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"hook function"
|
"""hook function"""
|
||||||
player = self.player
|
player = self.player
|
||||||
|
|
||||||
if 'all' in self.switches:
|
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():
|
for session in player.sessions.all():
|
||||||
player.disconnect_session_from_player(session)
|
player.disconnect_session_from_player(session)
|
||||||
else:
|
else:
|
||||||
|
|
@ -673,7 +670,6 @@ class CmdQuit(COMMAND_DEFAULT_CLASS):
|
||||||
player.disconnect_session_from_player(self.session)
|
player.disconnect_session_from_player(self.session)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
testing which colors your client support
|
testing which colors your client support
|
||||||
|
|
@ -706,12 +702,12 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||||
max_widths = [max([len(str(val)) for val in col]) for col in table]
|
max_widths = [max([len(str(val)) for val in col]) for col in table]
|
||||||
ftable = []
|
ftable = []
|
||||||
for irow in range(len(table[0])):
|
for irow in range(len(table[0])):
|
||||||
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " * extra_space
|
ftable.append([str(col[irow]).ljust(max_widths[icol]) + " " *
|
||||||
for icol, col in enumerate(table)])
|
extra_space for icol, col in enumerate(table)])
|
||||||
return ftable
|
return ftable
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Show color tables"
|
"""Show color tables"""
|
||||||
|
|
||||||
if self.args.startswith("a"):
|
if self.args.startswith("a"):
|
||||||
# show ansi 16-color table
|
# show ansi 16-color table
|
||||||
|
|
@ -721,9 +717,11 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||||
# show all ansi color-related codes
|
# show all ansi color-related codes
|
||||||
col1 = ["%s%s|n" % (code, code.replace("|", "||")) for code, _ in ap.ext_ansi_map[48:56]]
|
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]]
|
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:]]
|
col3 = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", ""))
|
||||||
col4 = ["%s%s|n" % (code.replace("\\",""), code.replace("|", "||").replace("\\", "")) for code, _ in ap.ansi_bright_bgs[-8:]]
|
for code, _ in ap.ext_ansi_map[-8:]]
|
||||||
col2.extend(["" for i in range(len(col1)-len(col2))])
|
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])
|
table = utils.format_table([col1, col2, col4, col3])
|
||||||
string = "ANSI colors:"
|
string = "ANSI colors:"
|
||||||
for row in table:
|
for row in table:
|
||||||
|
|
@ -742,9 +740,8 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||||
# foreground table
|
# foreground table
|
||||||
table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib)))
|
table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib)))
|
||||||
# background table
|
# background table
|
||||||
table[6+ir].append("|%i%i%i|[%i%i%i%s|n" % (5 - ir, 5 - ig, 5 - ib,
|
table[6+ir].append("|%i%i%i|[%i%i%i%s|n"
|
||||||
ir, ig, ib,
|
% (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib)))
|
||||||
"||[%i%i%i" % (ir, ig, ib)))
|
|
||||||
table = self.table_format(table)
|
table = self.table_format(table)
|
||||||
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
|
string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):"
|
||||||
string += "\n" + "\n".join("".join(row) for row in table)
|
string += "\n" + "\n".join("".join(row) for row in table)
|
||||||
|
|
@ -753,15 +750,15 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS):
|
||||||
for igray in range(6):
|
for igray in range(6):
|
||||||
letter = chr(97 + (ibatch*6 + igray))
|
letter = chr(97 + (ibatch*6 + igray))
|
||||||
inverse = chr(122 - (ibatch*6 + igray))
|
inverse = chr(122 - (ibatch*6 + igray))
|
||||||
table[0 + igray].append("|=%s%s |n" % (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)))
|
table[6 + igray].append("|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter))
|
||||||
for igray in range(6):
|
for igray in range(6):
|
||||||
# the last row (y, z) has empty columns
|
# the last row (y, z) has empty columns
|
||||||
if igray < 2:
|
if igray < 2:
|
||||||
letter = chr(121 + igray)
|
letter = chr(121 + igray)
|
||||||
inverse = chr(98 - igray)
|
inverse = chr(98 - igray)
|
||||||
fg = "|=%s%s |n" % (letter, "||=%s" % (letter))
|
fg = "|=%s%s |n" % (letter, "||=%s" % letter)
|
||||||
bg = "|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % (letter))
|
bg = "|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter)
|
||||||
else:
|
else:
|
||||||
fg, bg = " ", " "
|
fg, bg = " ", " "
|
||||||
table[0 + igray].append(fg)
|
table[0 + igray].append(fg)
|
||||||
|
|
@ -800,7 +797,7 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
|
||||||
player_caller = True
|
player_caller = True
|
||||||
|
|
||||||
def _recache_locks(self, player):
|
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:
|
if self.session:
|
||||||
char = self.session.puppet
|
char = self.session.puppet
|
||||||
if char:
|
if char:
|
||||||
|
|
@ -811,7 +808,7 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
|
||||||
player.locks.reset()
|
player.locks.reset()
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Perform the command"
|
"""Perform the command"""
|
||||||
player = self.player
|
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 self.cmdstring == '@unquell':
|
||||||
|
|
@ -829,7 +826,8 @@ class CmdQuell(COMMAND_DEFAULT_CLASS):
|
||||||
if puppet:
|
if puppet:
|
||||||
cpermstr = "(%s)" % ", ".join(puppet.permissions.all())
|
cpermstr = "(%s)" % ", ".join(puppet.permissions.all())
|
||||||
cpermstr = "Quelling to current puppet's permissions %s." % cpermstr
|
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 += "\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."
|
cpermstr += "\nUse @unquell to return to normal permission usage."
|
||||||
self.msg(cpermstr)
|
self.msg(cpermstr)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Define function"
|
"""Define function"""
|
||||||
# Only allow shutdown if caller has session
|
# Only allow shutdown if caller has session
|
||||||
if not self.caller.sessions.get():
|
if not self.caller.sessions.get():
|
||||||
return
|
return
|
||||||
|
|
@ -126,6 +126,7 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
|
||||||
def _py_load(caller):
|
def _py_load(caller):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _py_code(caller, buf):
|
def _py_code(caller, buf):
|
||||||
"""
|
"""
|
||||||
Execute the buffer.
|
Execute the buffer.
|
||||||
|
|
@ -139,10 +140,12 @@ def _py_code(caller, buf):
|
||||||
show_input=False)
|
show_input=False)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _py_quit(caller):
|
def _py_quit(caller):
|
||||||
del caller.db._py_measure_time
|
del caller.db._py_measure_time
|
||||||
caller.msg("Exited the code editor.")
|
caller.msg("Exited the code editor.")
|
||||||
|
|
||||||
|
|
||||||
def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
|
def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
|
||||||
show_input=True):
|
show_input=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -208,6 +211,7 @@ def _run_code_snippet(caller, pycode, mode="eval", measure_time=False,
|
||||||
except TypeError:
|
except TypeError:
|
||||||
caller.msg(ret, options={"raw": True})
|
caller.msg(ret, options={"raw": True})
|
||||||
|
|
||||||
|
|
||||||
class CmdPy(COMMAND_DEFAULT_CLASS):
|
class CmdPy(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
execute a snippet of python code
|
execute a snippet of python code
|
||||||
|
|
@ -234,8 +238,8 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
|
||||||
You can explore The evennia API from inside the game by calling
|
You can explore The evennia API from inside the game by calling
|
||||||
evennia.help(), evennia.managers.help() etc.
|
evennia.help(), evennia.managers.help() etc.
|
||||||
|
|
||||||
{rNote: In the wrong hands this command is a severe security risk.
|
|rNote: In the wrong hands this command is a severe security risk.
|
||||||
It should only be accessible by trusted server admins/superusers.{n
|
It should only be accessible by trusted server admins/superusers.|n
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "@py"
|
key = "@py"
|
||||||
|
|
@ -244,7 +248,7 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"hook function"
|
"""hook function"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
pycode = self.args
|
pycode = self.args
|
||||||
|
|
@ -266,13 +270,14 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
|
||||||
# helper function. Kept outside so it can be imported and run
|
# helper function. Kept outside so it can be imported and run
|
||||||
# by other commands.
|
# by other commands.
|
||||||
|
|
||||||
|
|
||||||
def format_script_list(scripts):
|
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:
|
if not scripts:
|
||||||
return "<No scripts>"
|
return "<No scripts>"
|
||||||
|
|
||||||
table = EvTable("{wdbref{n", "{wobj{n", "{wkey{n", "{wintval{n", "{wnext{n",
|
table = EvTable("|wdbref|n", "|wobj|n", "|wkey|n", "|wintval|n", "|wnext|n",
|
||||||
"{wrept{n", "{wdb", "{wtypeclass{n", "{wdesc{n",
|
"|wrept|n", "|wdb", "|wtypeclass|n", "|wdesc|n",
|
||||||
align='r', border="tablecols")
|
align='r', border="tablecols")
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
nextrep = script.time_until_next_repeat()
|
nextrep = script.time_until_next_repeat()
|
||||||
|
|
@ -326,12 +331,11 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"implement method"
|
"""implement method"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
||||||
string = ""
|
|
||||||
if args:
|
if args:
|
||||||
if "start" in self.switches:
|
if "start" in self.switches:
|
||||||
# global script-start mode
|
# global script-start mode
|
||||||
|
|
@ -374,8 +378,8 @@ class CmdScripts(COMMAND_DEFAULT_CLASS):
|
||||||
else:
|
else:
|
||||||
string = "Stopping script '%s'." % scripts[0].key
|
string = "Stopping script '%s'." % scripts[0].key
|
||||||
scripts[0].stop()
|
scripts[0].stop()
|
||||||
#import pdb
|
# import pdb # DEBUG
|
||||||
#pdb.set_trace()
|
# pdb.set_trace() # DEBUG
|
||||||
ScriptDB.objects.validate() # just to be sure all is synced
|
ScriptDB.objects.validate() # just to be sure all is synced
|
||||||
else:
|
else:
|
||||||
# multiple matches.
|
# multiple matches.
|
||||||
|
|
@ -409,26 +413,21 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the command"
|
"""Implement the command"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
nlim = int(self.args) if self.args and self.args.isdigit() else 10
|
||||||
if self.args and self.args.isdigit():
|
|
||||||
nlim = int(self.args)
|
|
||||||
else:
|
|
||||||
nlim = 10
|
|
||||||
|
|
||||||
nobjs = ObjectDB.objects.count()
|
nobjs = ObjectDB.objects.count()
|
||||||
base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
base_char_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||||
nchars = ObjectDB.objects.filter(db_typeclass_path=base_char_typeclass).count()
|
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()
|
nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count()
|
||||||
nother = nobjs - nchars - nrooms - nexits
|
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
|
# 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.align = 'l'
|
||||||
totaltable.add_row("Characters", "(BASE_CHARACTER_TYPECLASS)", nchars, "%.2f" % ((float(nchars) / nobjs) * 100))
|
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))
|
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))
|
totaltable.add_row("Other", "", nother, "%.2f" % ((float(nother) / nobjs) * 100))
|
||||||
|
|
||||||
# typeclass table
|
# 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'
|
typetable.align = 'l'
|
||||||
dbtotals = ObjectDB.objects.object_totals()
|
dbtotals = ObjectDB.objects.object_totals()
|
||||||
for path, count in dbtotals.items():
|
for path, count in dbtotals.items():
|
||||||
|
|
@ -444,15 +443,15 @@ class CmdObjects(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# last N table
|
# last N table
|
||||||
objs = ObjectDB.objects.all().order_by("db_date_created")[max(0, nobjs - nlim):]
|
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'
|
latesttable.align = 'l'
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
latesttable.add_row(utils.datetime_format(obj.date_created),
|
latesttable.add_row(utils.datetime_format(obj.date_created),
|
||||||
obj.dbref, obj.key, obj.path)
|
obj.dbref, obj.key, obj.path)
|
||||||
|
|
||||||
string = "\n{wObject subtype totals (out of %i Objects):{n\n%s" % (nobjs, totaltable)
|
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|wObject typeclass distribution:|n\n%s" % typetable
|
||||||
string += "\n{wLast %s Objects created:{n\n%s" % (min(nobjs, nlim), latesttable)
|
string += "\n|wLast %s Objects created:|n\n%s" % (min(nobjs, nlim), latesttable)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -473,7 +472,7 @@ class CmdPlayers(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"List the players"
|
"""List the players"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
if self.args and self.args.isdigit():
|
if self.args and self.args.isdigit():
|
||||||
|
|
@ -485,17 +484,17 @@ class CmdPlayers(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# typeclass table
|
# typeclass table
|
||||||
dbtotals = PlayerDB.objects.object_totals()
|
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():
|
for path, count in dbtotals.items():
|
||||||
typetable.add_row(path, count, "%.2f" % ((float(count) / nplayers) * 100))
|
typetable.add_row(path, count, "%.2f" % ((float(count) / nplayers) * 100))
|
||||||
# last N table
|
# last N table
|
||||||
plyrs = PlayerDB.objects.all().order_by("db_date_created")[max(0, nplayers - nlim):]
|
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:
|
for ply in plyrs:
|
||||||
latesttable.add_row(utils.datetime_format(ply.date_created), ply.dbref, ply.key, ply.path)
|
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|wPlayer typeclass distribution:|n\n%s" % typetable
|
||||||
string += "\n{wLast %s Players created:{n\n%s" % (min(nplayers, nlim), latesttable)
|
string += "\n|wLast %s Players created:|n\n%s" % (min(nplayers, nlim), latesttable)
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -525,7 +524,7 @@ class CmdService(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement command"
|
"""Implement command"""
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
switches = self.switches
|
switches = self.switches
|
||||||
|
|
@ -540,9 +539,9 @@ class CmdService(COMMAND_DEFAULT_CLASS):
|
||||||
if not switches or switches[0] == "list":
|
if not switches or switches[0] == "list":
|
||||||
# Just display the list of installed services and their
|
# Just display the list of installed services and their
|
||||||
# status, then exit.
|
# 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:
|
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))
|
caller.msg(unicode(table))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -584,7 +583,7 @@ class CmdService(COMMAND_DEFAULT_CLASS):
|
||||||
return
|
return
|
||||||
|
|
||||||
if switches[0] == "start":
|
if switches[0] == "start":
|
||||||
#Starts a service.
|
# Attempt to start a service.
|
||||||
if service.running:
|
if service.running:
|
||||||
caller.msg('That service is already running.')
|
caller.msg('That service is already running.')
|
||||||
return
|
return
|
||||||
|
|
@ -608,23 +607,23 @@ class CmdAbout(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Show the version"
|
"""Display information about server or target"""
|
||||||
|
|
||||||
string = """
|
string = """
|
||||||
{cEvennia{n %s{n
|
|cEvennia|n %s|n
|
||||||
MUD/MUX/MU* development system
|
MUD/MUX/MU* development system
|
||||||
|
|
||||||
{wLicence{n BSD 3-Clause Licence
|
|wLicence|n https://opensource.org/licenses/BSD-3-Clause
|
||||||
{wWeb{n http://www.evennia.com
|
|wWeb|n http://www.evennia.com
|
||||||
{wIrc{n #evennia on FreeNode
|
|wIrc|n #evennia on FreeNode
|
||||||
{wForum{n http://www.evennia.com/discussions
|
|wForum|n http://www.evennia.com/discussions
|
||||||
{wMaintainer{n (2010-) Griatch (griatch AT gmail DOT com)
|
|wMaintainer|n (2010-) Griatch (griatch AT gmail DOT com)
|
||||||
{wMaintainer{n (2006-10) Greg Taylor
|
|wMaintainer|n (2006-10) Greg Taylor
|
||||||
|
|
||||||
{wOS{n %s
|
|wOS|n %s
|
||||||
{wPython{n %s
|
|wPython|n %s
|
||||||
{wTwisted{n %s
|
|wTwisted|n %s
|
||||||
{wDjango{n %s
|
|wDjango|n %s
|
||||||
""" % (utils.get_evennia_version(),
|
""" % (utils.get_evennia_version(),
|
||||||
os.name,
|
os.name,
|
||||||
sys.version.split()[0],
|
sys.version.split()[0],
|
||||||
|
|
@ -649,7 +648,7 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Show server time data in a table."
|
"""Show server time data in a table."""
|
||||||
table1 = EvTable("|wServer time", "", align="l", width=78)
|
table1 = EvTable("|wServer time", "", align="l", width=78)
|
||||||
table1.add_row("Current uptime", utils.time_format(gametime.uptime(), 3))
|
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("Total runtime", utils.time_format(gametime.runtime(), 2))
|
||||||
|
|
@ -682,20 +681,20 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
Some Important statistics in the table:
|
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
|
between 0 (no usage) and 1 (100% usage), but may also be
|
||||||
temporarily higher if your computer has multiple CPU cores.
|
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.
|
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
|
loaded by use of the idmapper functionality. This allows Evennia
|
||||||
to maintain the same instances of an entity and allowing
|
to maintain the same instances of an entity and allowing
|
||||||
non-persistent storage schemes. The total amount of cached objects
|
non-persistent storage schemes. The total amount of cached objects
|
||||||
are displayed plus a breakdown of database object types.
|
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
|
note that due to how Python's memory management works, releasing
|
||||||
caches may not show you a lower Residual/Virtual memory footprint,
|
caches may not show you a lower Residual/Virtual memory footprint,
|
||||||
the released memory will instead be re-used by the program.
|
the released memory will instead be re-used by the program.
|
||||||
|
|
@ -707,7 +706,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
help_category = "System"
|
help_category = "System"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Show list."
|
"""Show list."""
|
||||||
|
|
||||||
global _IDMAPPER
|
global _IDMAPPER
|
||||||
if not _IDMAPPER:
|
if not _IDMAPPER:
|
||||||
|
|
@ -745,7 +744,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
pmem = _mem.percent
|
pmem = _mem.percent
|
||||||
|
|
||||||
if "mem" in self.switches:
|
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))
|
self.caller.msg(string % (rmem, pmem))
|
||||||
return
|
return
|
||||||
# Display table
|
# Display table
|
||||||
|
|
@ -755,7 +754,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
loadtable.add_row("Process ID", "%g" % pid),
|
loadtable.add_row("Process ID", "%g" % pid),
|
||||||
else:
|
else:
|
||||||
loadtable = "Not available on Windows without 'psutil' library " \
|
loadtable = "Not available on Windows without 'psutil' library " \
|
||||||
"(install with {wpip install psutil{n)."
|
"(install with |wpip install psutil|n)."
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Linux / BSD (OSX) - proper pid-based statistics
|
# Linux / BSD (OSX) - proper pid-based statistics
|
||||||
|
|
@ -767,12 +766,11 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
loadavg = os.getloadavg()[0]
|
loadavg = os.getloadavg()[0]
|
||||||
rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1000.0 # resident memory
|
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
|
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)
|
rusage = _RESOURCE.getrusage(_RESOURCE.RUSAGE_SELF)
|
||||||
|
|
||||||
if "mem" in self.switches:
|
if "mem" in self.switches:
|
||||||
string = "Memory usage: RMEM: {w%g{n MB (%g%%), " \
|
string = "Memory usage: RMEM: |w%g|n MB (%g%%), VMEM (res+swap+cache): |w%g|n MB."
|
||||||
" VMEM (res+swap+cache): {w%g{n MB."
|
|
||||||
self.caller.msg(string % (rmem, pmem, vmem))
|
self.caller.msg(string % (rmem, pmem, vmem))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -781,18 +779,21 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
loadtable.add_row("Process ID", "%g" % pid),
|
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("Virtual address space", "")
|
||||||
loadtable.add_row("{x(resident+swap+caching){n", "%g MB" % vmem)
|
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 (total)", "%s (%gs)"
|
||||||
loadtable.add_row("CPU time used (user)", "%s (%gs)" % (utils.time_format(rusage.ru_stime), rusage.ru_stime))
|
% (utils.time_format(rusage.ru_utime), rusage.ru_utime))
|
||||||
loadtable.add_row("Page faults", "%g hard, %g soft, %g swapouts" % (rusage.ru_majflt, rusage.ru_minflt, rusage.ru_nswap))
|
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("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("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
|
# 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.
|
# object cache count (note that sys.getsiseof is not called so this works for pypy too.
|
||||||
total_num, cachedict = _IDMAPPER.cache_size()
|
total_num, cachedict = _IDMAPPER.cache_size()
|
||||||
|
|
@ -802,11 +803,12 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS):
|
||||||
for tup in sorted_cache:
|
for tup in sorted_cache:
|
||||||
memtable.add_row(tup[0], "%i" % tup[1], "%.2f" % (float(tup[1]) / total_num * 100))
|
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
|
# return to caller
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
|
|
||||||
|
|
||||||
class CmdTickers(COMMAND_DEFAULT_CLASS):
|
class CmdTickers(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
View running tickers
|
View running tickers
|
||||||
|
|
@ -832,13 +834,9 @@ class CmdTickers(COMMAND_DEFAULT_CLASS):
|
||||||
table = EvTable("interval (s)", "object", "path/methodname", "idstring", "db")
|
table = EvTable("interval (s)", "object", "path/methodname", "idstring", "db")
|
||||||
for sub in all_subs:
|
for sub in all_subs:
|
||||||
table.add_row(sub[3],
|
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[1] if sub[1] else sub[2],
|
||||||
sub[4] or "[Unset]",
|
sub[4] or "[Unset]",
|
||||||
"*" if sub[5] else "-")
|
"*" if sub[5] else "-")
|
||||||
self.caller.msg("|wActive tickers|n:\n" + unicode(table))
|
self.caller.msg("|wActive tickers|n:\n" + unicode(table))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class CommandTest(EvenniaTest):
|
||||||
Tests a command
|
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
|
Test a command by assigning all the needed
|
||||||
properties to cmdobj and running
|
properties to cmdobj and running
|
||||||
|
|
@ -48,6 +48,10 @@ class CommandTest(EvenniaTest):
|
||||||
cmdobj.at_post_cmd()
|
cmdobj.at_post_cmd()
|
||||||
The msgreturn value is compared to eventual
|
The msgreturn value is compared to eventual
|
||||||
output sent to caller.msg in the game
|
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
|
caller = caller if caller else self.char1
|
||||||
receiver = receiver if receiver else caller
|
receiver = receiver if receiver else caller
|
||||||
|
|
@ -60,9 +64,10 @@ class CommandTest(EvenniaTest):
|
||||||
cmdobj.session = SESSIONS.session_from_sessid(1)
|
cmdobj.session = SESSIONS.session_from_sessid(1)
|
||||||
cmdobj.player = self.player
|
cmdobj.player = self.player
|
||||||
cmdobj.raw_string = cmdobj.key + " " + args
|
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
|
# test
|
||||||
old_msg = receiver.msg
|
old_msg = receiver.msg
|
||||||
|
returned_msg = ""
|
||||||
try:
|
try:
|
||||||
receiver.msg = Mock()
|
receiver.msg = Mock()
|
||||||
cmdobj.at_pre_cmd()
|
cmdobj.at_pre_cmd()
|
||||||
|
|
@ -74,18 +79,23 @@ class CommandTest(EvenniaTest):
|
||||||
for name, args, kwargs in receiver.msg.mock_calls]
|
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
|
# 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]
|
stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg]
|
||||||
|
if msg is not None:
|
||||||
returned_msg = "||".join(_RE.sub("", mess) for mess 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()
|
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
|
||||||
if msg is not None:
|
|
||||||
if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()):
|
if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()):
|
||||||
sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n"
|
sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n"
|
||||||
sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n"
|
sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n"
|
||||||
sep3 = "\n" + "="*78
|
sep3 = "\n" + "="*78
|
||||||
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
|
retval = sep1 + msg.strip() + sep2 + returned_msg + sep3
|
||||||
raise AssertionError(retval)
|
raise AssertionError(retval)
|
||||||
|
else:
|
||||||
|
returned_msg = "\n".join(stored_msg)
|
||||||
|
returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip()
|
||||||
finally:
|
finally:
|
||||||
receiver.msg = old_msg
|
receiver.msg = old_msg
|
||||||
|
|
||||||
|
return returned_msg
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Individual module Tests
|
# Individual module Tests
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
@ -119,6 +129,9 @@ class TestGeneral(CommandTest):
|
||||||
def test_say(self):
|
def test_say(self):
|
||||||
self.call(general.CmdSay(), "Testing", "You say, \"Testing\"")
|
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):
|
def test_access(self):
|
||||||
self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):")
|
self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||||
# would also block dummyrunner, so it's not added as default.
|
# would also block dummyrunner, so it's not added as default.
|
||||||
|
|
||||||
_LATEST_FAILED_LOGINS = defaultdict(list)
|
_LATEST_FAILED_LOGINS = defaultdict(list)
|
||||||
|
|
||||||
|
|
||||||
def _throttle(session, maxlim=None, timeout=None, storage=_LATEST_FAILED_LOGINS):
|
def _throttle(session, maxlim=None, timeout=None, storage=_LATEST_FAILED_LOGINS):
|
||||||
"""
|
"""
|
||||||
This will check the session's address against the
|
This will check the session's address against the
|
||||||
|
|
@ -95,8 +97,8 @@ def create_guest_player(session):
|
||||||
bans = ServerConfig.objects.conf("server_bans")
|
bans = ServerConfig.objects.conf("server_bans")
|
||||||
if bans and any(tup[2].match(session.address) for tup in bans if tup[2]):
|
if bans and any(tup[2].match(session.address) for tup in bans if tup[2]):
|
||||||
# this is a banned IP!
|
# this is a banned IP!
|
||||||
string = "{rYou have been banned and cannot continue from here." \
|
string = "|rYou have been banned and cannot continue from here." \
|
||||||
"\nIf you feel this ban is in error, please email an admin.{x"
|
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
@ -118,14 +120,11 @@ def create_guest_player(session):
|
||||||
permissions = settings.PERMISSION_GUEST_DEFAULT
|
permissions = settings.PERMISSION_GUEST_DEFAULT
|
||||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||||
ptypeclass = settings.BASE_GUEST_TYPECLASS
|
ptypeclass = settings.BASE_GUEST_TYPECLASS
|
||||||
new_player = _create_player(session, playername, password,
|
new_player = _create_player(session, playername, password, permissions, ptypeclass)
|
||||||
permissions, ptypeclass)
|
|
||||||
if new_player:
|
if new_player:
|
||||||
_create_character(session, new_player, typeclass,
|
_create_character(session, new_player, typeclass, home, permissions)
|
||||||
home, permissions)
|
|
||||||
return True, new_player
|
return True, new_player
|
||||||
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We are in the middle between logged in and -not, so we have
|
# We are in the middle between logged in and -not, so we have
|
||||||
# to handle tracebacks ourselves at this point. If we don't,
|
# 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.
|
# check for too many login errors too quick.
|
||||||
if _throttle(session, maxlim=5, timeout=5*60):
|
if _throttle(session, maxlim=5, timeout=5*60):
|
||||||
# timeout is 5 minutes.
|
# 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
|
return None
|
||||||
|
|
||||||
# Match account name and check password
|
# Match account name and check password
|
||||||
|
|
@ -167,15 +166,14 @@ def create_normal_player(session, name, password):
|
||||||
player.at_failed_login(session)
|
player.at_failed_login(session)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Check IP and/or name bans
|
# Check IP and/or name bans
|
||||||
bans = ServerConfig.objects.conf("server_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
|
or
|
||||||
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
||||||
# this is a banned IP or name!
|
# this is a banned IP or name!
|
||||||
string = "{rYou have been banned and cannot continue from here." \
|
string = "|rYou have been banned and cannot continue from here." \
|
||||||
"\nIf you feel this ban is in error, please email an admin.{x"
|
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
return None
|
return None
|
||||||
|
|
@ -213,7 +211,7 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
|
||||||
# check for too many login errors too quick.
|
# check for too many login errors too quick.
|
||||||
if _throttle(session, maxlim=5, timeout=5*60, storage=_LATEST_FAILED_LOGINS):
|
if _throttle(session, maxlim=5, timeout=5*60, storage=_LATEST_FAILED_LOGINS):
|
||||||
# timeout is 5 minutes.
|
# 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
|
return
|
||||||
|
|
||||||
args = self.args
|
args = self.args
|
||||||
|
|
@ -237,12 +235,6 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS):
|
||||||
name, password = parts
|
name, password = parts
|
||||||
player = create_normal_player(session, name, password)
|
player = create_normal_player(session, name, password)
|
||||||
if player:
|
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)
|
session.sessionhandler.login(session, player)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -264,7 +256,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
||||||
arg_regex = r"\s.*?|$"
|
arg_regex = r"\s.*?|$"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Do checks and create account"
|
"""Do checks and create account"""
|
||||||
|
|
||||||
session = self.caller
|
session = self.caller
|
||||||
args = self.args.strip()
|
args = self.args.strip()
|
||||||
|
|
@ -313,8 +305,8 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
||||||
or
|
or
|
||||||
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
any(tup[2].match(session.address) for tup in bans if tup[2])):
|
||||||
# this is a banned IP or name!
|
# this is a banned IP or name!
|
||||||
string = "{rYou have been banned and cannot continue from here." \
|
string = "|rYou have been banned and cannot continue from here." \
|
||||||
"\nIf you feel this ban is in error, please email an admin.{x"
|
"\nIf you feel this ban is in error, please email an admin.|x"
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
return
|
return
|
||||||
|
|
@ -327,8 +319,7 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS):
|
||||||
if new_player:
|
if new_player:
|
||||||
if MULTISESSION_MODE < 2:
|
if MULTISESSION_MODE < 2:
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||||
_create_character(session, new_player, typeclass,
|
_create_character(session, new_player, typeclass, default_home, permissions)
|
||||||
default_home, permissions)
|
|
||||||
# tell the caller everything went well.
|
# tell the caller everything went well.
|
||||||
string = "A new account '%s' was created. Welcome!"
|
string = "A new account '%s' was created. Welcome!"
|
||||||
if " " in playername:
|
if " " in playername:
|
||||||
|
|
@ -361,7 +352,7 @@ class CmdUnconnectedQuit(COMMAND_DEFAULT_CLASS):
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Simply close the connection."
|
"""Simply close the connection."""
|
||||||
session = self.caller
|
session = self.caller
|
||||||
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
|
|
||||||
|
|
@ -383,7 +374,7 @@ class CmdUnconnectedLook(COMMAND_DEFAULT_CLASS):
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Show the connect screen."
|
"""Show the connect screen."""
|
||||||
connection_screen = utils.random_string_from_module(CONNECTION_SCREEN_MODULE)
|
connection_screen = utils.random_string_from_module(CONNECTION_SCREEN_MODULE)
|
||||||
if not connection_screen:
|
if not connection_screen:
|
||||||
connection_screen = "No connection screen found. Please contact an admin."
|
connection_screen = "No connection screen found. Please contact an admin."
|
||||||
|
|
@ -405,25 +396,25 @@ class CmdUnconnectedHelp(COMMAND_DEFAULT_CLASS):
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Shows help"
|
"""Shows help"""
|
||||||
|
|
||||||
string = \
|
string = \
|
||||||
"""
|
"""
|
||||||
You are not yet logged into the game. Commands available at this point:
|
You are not yet logged into the game. Commands available at this point:
|
||||||
|
|
||||||
{wcreate{n - create a new account
|
|wcreate|n - create a new account
|
||||||
{wconnect{n - connect with an existing account
|
|wconnect|n - connect with an existing account
|
||||||
{wlook{n - re-show the connection screen
|
|wlook|n - re-show the connection screen
|
||||||
{whelp{n - show this help
|
|whelp|n - show this help
|
||||||
{wencoding{n - change the text encoding to match your client
|
|wencoding|n - change the text encoding to match your client
|
||||||
{wscreenreader{n - make the server more suitable for use with screen readers
|
|wscreenreader|n - make the server more suitable for use with screen readers
|
||||||
{wquit{n - abort the connection
|
|wquit|n - abort the connection
|
||||||
|
|
||||||
First create an account e.g. with {wcreate 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
|
(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
|
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)
|
self.caller.msg(string)
|
||||||
|
|
@ -479,10 +470,10 @@ class CmdUnconnectedEncoding(COMMAND_DEFAULT_CLASS):
|
||||||
pencoding = self.session.protocol_flags.get("ENCODING", None)
|
pencoding = self.session.protocol_flags.get("ENCODING", None)
|
||||||
string = ""
|
string = ""
|
||||||
if pencoding:
|
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
|
encodings = settings.ENCODINGS
|
||||||
if 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:
|
if not string:
|
||||||
string = "No encodings found."
|
string = "No encodings found."
|
||||||
else:
|
else:
|
||||||
|
|
@ -492,7 +483,8 @@ class CmdUnconnectedEncoding(COMMAND_DEFAULT_CLASS):
|
||||||
try:
|
try:
|
||||||
utils.to_str(utils.to_unicode("test-string"), encoding=encoding)
|
utils.to_str(utils.to_unicode("test-string"), encoding=encoding)
|
||||||
except LookupError:
|
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:
|
else:
|
||||||
self.session.protocol_flags["ENCODING"] = encoding
|
self.session.protocol_flags["ENCODING"] = encoding
|
||||||
string = "Your custom text encoding was changed from '|w%s|n' to '|w%s|n'." % (old_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.session.sessionhandler.session_portal_sync(self.session)
|
||||||
self.caller.msg(string.strip())
|
self.caller.msg(string.strip())
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
|
class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
Activate screenreader mode.
|
Activate screenreader mode.
|
||||||
|
|
@ -515,21 +508,20 @@ class CmdUnconnectedScreenreader(COMMAND_DEFAULT_CLASS):
|
||||||
aliases = "@screenreader"
|
aliases = "@screenreader"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Flips screenreader setting."
|
"""Flips screenreader setting."""
|
||||||
new_setting = not self.session.protocol_flags.get("SCREENREADER", False)
|
new_setting = not self.session.protocol_flags.get("SCREENREADER", False)
|
||||||
self.session.protocol_flags["SCREENREADER"] = new_setting
|
self.session.protocol_flags["SCREENREADER"] = new_setting
|
||||||
string = "Screenreader mode turned {w%s{n." % ("on" if new_setting else "off")
|
string = "Screenreader mode turned |w%s|n." % ("on" if new_setting else "off")
|
||||||
self.caller.msg(string)
|
self.caller.msg(string)
|
||||||
self.session.sessionhandler.session_portal_sync(self.session)
|
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.
|
Helper function, creates a player of the specified typeclass.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
new_player = create.create_player(playername, None, password,
|
new_player = create.create_player(playername, email, password, permissions=permissions, typeclass=typeclass)
|
||||||
permissions=permissions, typeclass=typeclass)
|
|
||||||
|
|
||||||
except Exception as e:
|
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 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
|
# join the new player to the public channel
|
||||||
pchannel = ChannelDB.objects.get_channel(settings.DEFAULT_CHANNELS[0]["key"])
|
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
|
string = "New player '%s' could not connect to public channel!" % new_player.key
|
||||||
logger.log_err(string)
|
logger.log_err(string)
|
||||||
return new_player
|
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.
|
This is meant for Guest and MULTISESSION_MODE < 2 situations.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
new_character = create.create_object(typeclass, key=new_player.key,
|
new_character = create.create_object(typeclass, key=new_player.key, home=home, permissions=permissions)
|
||||||
home=home, permissions=permissions)
|
|
||||||
# set playable character list
|
# set playable character list
|
||||||
new_player.db._playable_characters.append(new_character)
|
new_player.db._playable_characters.append(new_character)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
listening = [ob for ob in subs if ob.is_connected and ob not in self.mutelist]
|
listening = [ob for ob in subs if ob.is_connected and ob not in self.mutelist]
|
||||||
if subs:
|
if subs:
|
||||||
# display listening subscribers in bold
|
# display listening subscribers in bold
|
||||||
string = ", ".join([player.key if player not in listening else "{w%s{n" % player.key for player in subs])
|
string = ", ".join([player.key if player not in listening else "|w%s|n" % player.key for player in subs])
|
||||||
else:
|
else:
|
||||||
string = "<None>"
|
string = "<None>"
|
||||||
return string
|
return string
|
||||||
|
|
@ -126,7 +126,6 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def connect(self, subscriber):
|
def connect(self, subscriber):
|
||||||
"""
|
"""
|
||||||
Connect the user to this channel. This checks access.
|
Connect the user to this channel. This checks access.
|
||||||
|
|
@ -331,7 +330,6 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
"""
|
"""
|
||||||
self.msg(message, senders=senders, header=header, keep_log=False)
|
self.msg(message, senders=senders, header=header, keep_log=False)
|
||||||
|
|
||||||
|
|
||||||
# hooks
|
# hooks
|
||||||
|
|
||||||
def channel_prefix(self, msg=None, emit=False):
|
def channel_prefix(self, msg=None, emit=False):
|
||||||
|
|
@ -376,7 +374,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
message accordingly.
|
message accordingly.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msgob (Msg or TempMsg): The message to analyze for a pose.
|
msgobj (Msg or TempMsg): The message to analyze for a pose.
|
||||||
sender_string (str): The name of the sender/poser.
|
sender_string (str): The name of the sender/poser.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -426,7 +424,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
Hook method. Formats a message body for display.
|
Hook method. Formats a message body for display.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msgob (Msg or TempMsg): The message object to send.
|
msgobj (Msg or TempMsg): The message object to send.
|
||||||
emit (bool, optional): The message is agnostic of senders.
|
emit (bool, optional): The message is agnostic of senders.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -475,7 +473,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
value, leaving the channel will be aborted.
|
value, leaving the channel will be aborted.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
joiner (object): The joining object.
|
leaver (object): The leaving object.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
should_leave (bool): If `False`, channel parting is aborted.
|
should_leave (bool): If `False`, channel parting is aborted.
|
||||||
|
|
@ -488,7 +486,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
|
||||||
Hook method. Runs right after an object or player leaves a channel.
|
Hook method. Runs right after an object or player leaves a channel.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
joiner (object): The joining object.
|
leaver (object): The leaving object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -580,7 +580,7 @@ class CmdDecline(CmdTradeBase):
|
||||||
self.msg_other(caller, self.str_other
|
self.msg_other(caller, self.str_other
|
||||||
% "%s changes their mind, |Rdeclining|n the current offer." % caller.key)
|
% "%s changes their mind, |Rdeclining|n the current offer." % caller.key)
|
||||||
else:
|
else:
|
||||||
# no accept_ance to change
|
# no acceptance to change
|
||||||
caller.msg(self.str_caller % "You |Rdecline|n the current offer.")
|
caller.msg(self.str_caller % "You |Rdecline|n the current offer.")
|
||||||
self.msg_other(caller, self.str_other % "%s declines the current offer." % caller.key)
|
self.msg_other(caller, self.str_other % "%s declines the current offer." % caller.key)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,25 @@
|
||||||
|
|
||||||
Contribution - Griatch 2011
|
Contribution - Griatch 2011
|
||||||
|
|
||||||
[Note - with the advent of MULTISESSION_MODE=2, this is not really as
|
> Note - with the advent of MULTISESSION_MODE=2, this is not really as
|
||||||
necessary anymore - the ooclook and @charcreate commands in that mode
|
necessary anymore - the ooclook and @charcreate commands in that mode
|
||||||
replaces this module with better functionality.]
|
replaces this module with better functionality. This remains here for
|
||||||
|
inspiration.
|
||||||
|
|
||||||
This is a simple character creation commandset. A suggestion is to
|
This is a simple character creation commandset for the Player level.
|
||||||
test this together with menu_login, which doesn't create a Character
|
It shows some more info and gives the Player the option to create a
|
||||||
on its own. This shows some more info and gives the Player the option
|
character without any more customizations than their name (further
|
||||||
to create a character without any more customizations than their name
|
options are unique for each game anyway).
|
||||||
(further options are unique for each game anyway).
|
|
||||||
|
|
||||||
Since this extends the OOC cmdset, logging in from the menu will
|
In MULTISESSION_MODEs 0 and 1, you will automatically log into an
|
||||||
automatically drop the Player into this cmdset unless they logged off
|
existing Character. When using `@ooc` you will then end up in this
|
||||||
while puppeting a Character already before.
|
cmdset.
|
||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
|
|
||||||
Read the instructions in contrib/examples/cmdset.py in order to create
|
Import this module to `mygame/commands/default_cmdsets.py` and
|
||||||
a new default cmdset module for Evennia to use (copy the template up
|
add `chargen.OOCCMdSetCharGen` to the `PlayerCmdSet` class
|
||||||
one level, and change the settings file's relevant variables to point
|
(it says where to add it). Reload.
|
||||||
to the cmdsets inside). If you already have such a module you should
|
|
||||||
of course use that.
|
|
||||||
|
|
||||||
Next import this module in your custom cmdset module and add the
|
|
||||||
following line to the end of OOCCmdSet's at_cmdset_creation():
|
|
||||||
|
|
||||||
self.add(chargen.OOCCmdSetCharGen)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -179,7 +172,7 @@ class CmdOOCCharacterCreate(Command):
|
||||||
else:
|
else:
|
||||||
avail_chars = [new_character.id]
|
avail_chars = [new_character.id]
|
||||||
self.caller.db._character_dbrefs = avail_chars
|
self.caller.db._character_dbrefs = avail_chars
|
||||||
self.caller.msg("|gThe Character |c%s|g was successfully created!" % charname)
|
self.caller.msg("|gThe character |c%s|g was successfully created!" % charname)
|
||||||
|
|
||||||
|
|
||||||
class OOCCmdSetCharGen(default_cmds.PlayerCmdSet):
|
class OOCCmdSetCharGen(default_cmds.PlayerCmdSet):
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,23 @@ Usage:
|
||||||
|
|
||||||
Use as the normal gametime module, that is by importing and using the
|
Use as the normal gametime module, that is by importing and using the
|
||||||
helper functions in this module in your own code. The calendar can be
|
helper functions in this module in your own code. The calendar can be
|
||||||
specified in your settings file by adding and setting custom values
|
customized by adding the `TIME_UNITS` dictionary to your settings
|
||||||
for one or more of the variables `TIME_SECS_PER_MIN`,
|
file. This maps unit names to their length, expressed in the smallest
|
||||||
`TIME_MINS_PER_HOUR`, `TIME_DAYS_PER_WEEK`, `TIME_WEEKS_PER_MONTH` and
|
unit. Here's the default as an example:
|
||||||
`TIME_MONTHS_PER_YEAR`. These are all given in seconds and whereas
|
|
||||||
they are called "week", "month" etc these names could represent
|
TIME_UNITS = {
|
||||||
whatever fits your game. You can also set `TIME_UNITS` to a dict
|
"sec": 1,
|
||||||
mapping the name of a unit to its length in seconds (like `{"min":
|
"min": 60,
|
||||||
60, ...}. If not given, sane defaults will be used.
|
"hr": 60 * 60,
|
||||||
|
"hour": 60 * 60,
|
||||||
|
"day": 60 * 60 * 24,
|
||||||
|
"week": 60 * 60 * 24 * 7,
|
||||||
|
"month": 60 * 60 * 24 * 7 * 4,
|
||||||
|
"yr": 60 * 60 * 24 * 7 * 4 * 12,
|
||||||
|
"year": 60 * 60 * 24 * 7 * 4 * 12, }
|
||||||
|
|
||||||
|
When using a custom calendar, these time unit names are used as kwargs to
|
||||||
|
the converter functions in this module.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -28,33 +37,23 @@ mapping the name of a unit to its length in seconds (like `{"min":
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia import DefaultScript
|
from evennia import DefaultScript
|
||||||
from evennia.utils.create import create_script
|
from evennia.utils.create import create_script
|
||||||
from evennia.utils.gametime import gametime
|
from evennia.utils import gametime
|
||||||
# The game time speedup / slowdown relative real time
|
# The game time speedup / slowdown relative real time
|
||||||
TIMEFACTOR = settings.TIME_FACTOR
|
TIMEFACTOR = settings.TIME_FACTOR
|
||||||
|
|
||||||
# Game-time units, in game time seconds. These are supplied as a
|
# These are the unit names understood by the scheduler.
|
||||||
# convenient measure for determining the current in-game time, e.g.
|
# Each unit must be consistent and expressed in seconds.
|
||||||
# when defining in-game events. The words month, week and year can be
|
|
||||||
# used to mean whatever units of time are used in your game.
|
|
||||||
SEC = 1
|
|
||||||
MIN = getattr(settings, "TIME_SECS_PER_MIN", 60)
|
|
||||||
HOUR = getattr(settings, "TIME_MINS_PER_HOUR", 60) * MIN
|
|
||||||
DAY = getattr(settings, "TIME_HOURS_PER_DAY", 24) * HOUR
|
|
||||||
WEEK = getattr(settings, "TIME_DAYS_PER_WEEK", 7) * DAY
|
|
||||||
MONTH = getattr(settings, "TIME_WEEKS_PER_MONTH", 4) * WEEK
|
|
||||||
YEAR = getattr(settings, "TIME_MONTHS_PER_YEAR", 12) * MONTH
|
|
||||||
# these are the unit names understood by the scheduler.
|
|
||||||
UNITS = getattr(settings, "TIME_UNITS", {
|
UNITS = getattr(settings, "TIME_UNITS", {
|
||||||
"sec": SEC,
|
# default custom calendar
|
||||||
"min": MIN,
|
"sec": 1,
|
||||||
"hr": HOUR,
|
"min": 60,
|
||||||
"hour": HOUR,
|
"hr": 60 * 60,
|
||||||
"day": DAY,
|
"hour": 60 * 60,
|
||||||
"week": WEEK,
|
"day": 60 * 60 * 24,
|
||||||
"month": MONTH,
|
"week": 60 * 60 * 24 * 7,
|
||||||
"year": YEAR,
|
"month": 60 * 60 * 24 * 7 * 4,
|
||||||
"yr": YEAR,
|
"yr": 60 * 60 * 24 * 7 * 4 * 12,
|
||||||
})
|
"year": 60 * 60 * 24 * 7 * 4 * 12, })
|
||||||
|
|
||||||
|
|
||||||
def time_to_tuple(seconds, *divisors):
|
def time_to_tuple(seconds, *divisors):
|
||||||
|
|
@ -92,7 +91,8 @@ def gametime_to_realtime(format=False, **kwargs):
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
format (bool): Formatting the output.
|
format (bool): Formatting the output.
|
||||||
times (int): The various components of the time (must match UNITS).
|
days, month etc (int): These are the names of time units that must
|
||||||
|
match the `settings.TIME_UNITS` dict keys.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
time (float or tuple): The realtime difference or the same
|
time (float or tuple): The realtime difference or the same
|
||||||
|
|
@ -163,7 +163,7 @@ def custom_gametime(absolute=False):
|
||||||
week, day, hour, minute, second).
|
week, day, hour, minute, second).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
current = gametime(absolute=absolute)
|
current = gametime.gametime(absolute=absolute)
|
||||||
units = sorted(set(UNITS.values()), reverse=True)
|
units = sorted(set(UNITS.values()), reverse=True)
|
||||||
del units[-1]
|
del units[-1]
|
||||||
return time_to_tuple(current, *units)
|
return time_to_tuple(current, *units)
|
||||||
|
|
@ -186,7 +186,7 @@ def real_seconds_until(**kwargs):
|
||||||
The number of real seconds before the given game time is up.
|
The number of real seconds before the given game time is up.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
current = gametime(absolute=True)
|
current = gametime.gametime(absolute=True)
|
||||||
units = sorted(set(UNITS.values()), reverse=True)
|
units = sorted(set(UNITS.values()), reverse=True)
|
||||||
# Remove seconds from the tuple
|
# Remove seconds from the tuple
|
||||||
del units[-1]
|
del units[-1]
|
||||||
|
|
@ -232,25 +232,26 @@ def schedule(callback, repeat=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Call the callback when the game time is up.
|
Call the callback when the game time is up.
|
||||||
|
|
||||||
This function will setup a script that will be called when the
|
|
||||||
time corresponds to the game time. If the game is stopped for
|
|
||||||
more than a few seconds, the callback may be called with a slight
|
|
||||||
delay. If `repeat` is set to True, the callback will be called
|
|
||||||
again next time the game time matches the given time. The time
|
|
||||||
is given in units as keyword arguments. For instance:
|
|
||||||
>>> schedule(func, min=5, sec=0) # Will call next hour at :05.
|
|
||||||
>>> schedule(func, hour=2, min=30, sec=0) # Will call the next day at 02:30.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
callback (function): the callback function that will be called [1].
|
callback (function): The callback function that will be called. This
|
||||||
repeat (bool, optional): should the callback be called regularly?
|
must be a top-level function since the script will be persistent.
|
||||||
times (str: int): the time to call the callback.
|
repeat (bool, optional): Should the callback be called regularly?
|
||||||
|
day, month, etc (str: int): The time units to call the callback; should
|
||||||
[1] The callback must be a top-level function, since the script will
|
match the keys of TIME_UNITS.
|
||||||
be persistent.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The created script (Script).
|
script (Script): The created script.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
schedule(func, min=5, sec=0) # Will call next hour at :05.
|
||||||
|
schedule(func, hour=2, min=30, sec=0) # Will call the next day at 02:30.
|
||||||
|
Notes:
|
||||||
|
This function will setup a script that will be called when the
|
||||||
|
time corresponds to the game time. If the game is stopped for
|
||||||
|
more than a few seconds, the callback may be called with a
|
||||||
|
slight delay. If `repeat` is set to True, the callback will be
|
||||||
|
called again next time the game time matches the given time.
|
||||||
|
The time is given in units as keyword arguments.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
seconds = real_seconds_until(**kwargs)
|
seconds = real_seconds_until(**kwargs)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ from evennia.commands.cmdset import CmdSet
|
||||||
from evennia.utils import create, logger, utils, ansi
|
from evennia.utils import create, logger, utils, ansi
|
||||||
from evennia.commands.default.muxcommand import MuxCommand
|
from evennia.commands.default.muxcommand import MuxCommand
|
||||||
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||||
|
from evennia.commands.default import unloggedin as default_unloggedin # Used in CmdUnconnectedCreate
|
||||||
|
|
||||||
# limit symbol import for API
|
# limit symbol import for API
|
||||||
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
|
__all__ = ("CmdUnconnectedConnect", "CmdUnconnectedCreate",
|
||||||
|
|
@ -161,96 +162,74 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
"""Do checks and create account"""
|
"""Do checks and create account"""
|
||||||
|
|
||||||
session = self.caller
|
session = self.caller
|
||||||
|
|
||||||
try:
|
try:
|
||||||
playername, email, password = self.playerinfo
|
playername, email, password = self.playerinfo
|
||||||
except ValueError:
|
except ValueError:
|
||||||
string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>"
|
string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>"
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
return
|
return
|
||||||
if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30):
|
|
||||||
session.msg("\n\r Playername can be max 30 characters, or less. Letters, spaces,"
|
|
||||||
" digits and @/./+/-/_ only.") # this echoes the restrictions made by django's auth module.
|
|
||||||
return
|
|
||||||
if not email or not password:
|
if not email or not password:
|
||||||
session.msg("\n\r You have to supply an e-mail address followed by a password.")
|
session.msg("\n\r You have to supply an e-mail address followed by a password.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not utils.validate_email_address(email):
|
if not utils.validate_email_address(email):
|
||||||
# check so the email at least looks ok.
|
# check so the email at least looks ok.
|
||||||
session.msg("'%s' is not a valid e-mail address." % email)
|
session.msg("'%s' is not a valid e-mail address." % email)
|
||||||
return
|
return
|
||||||
|
# sanity checks
|
||||||
# Run sanity and security checks
|
if not re.findall(r"^[\w. @+\-']+$", playername) or not (0 < len(playername) <= 30):
|
||||||
|
# this echoes the restrictions made by django's auth
|
||||||
if PlayerDB.objects.filter(username=playername):
|
# module (except not allowing spaces, for convenience of
|
||||||
# player already exists
|
# logging in).
|
||||||
|
string = "\n\r Playername 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)
|
session.msg("Sorry, there is already a player with the name '%s'." % playername)
|
||||||
return
|
return
|
||||||
if PlayerDB.objects.get_player_from_email(email):
|
if PlayerDB.objects.get_player_from_email(email):
|
||||||
# email already set on a player
|
# email already set on a player
|
||||||
session.msg("Sorry, there is already a player with that email address.")
|
session.msg("Sorry, there is already a player with that email address.")
|
||||||
return
|
return
|
||||||
if len(password) < 3:
|
# Reserve playernames found in GUEST_LIST
|
||||||
# too short password
|
if settings.GUEST_LIST and playername.lower() in (guest.lower() for guest in settings.GUEST_LIST):
|
||||||
string = "Your password must be at least 3 characters or longer."
|
string = "\n\r That name is reserved. Please choose another Playername."
|
||||||
string += "\n\rFor best security, make it at least 8 characters long, "
|
|
||||||
string += "avoid making it a real word and mix numbers into it."
|
|
||||||
session.msg(string)
|
session.msg(string)
|
||||||
return
|
return
|
||||||
|
if not re.findall(r"^[\w. @+\-']+$", password) or not (3 < len(password)):
|
||||||
|
string = "\n\r Password should be longer than 3 characers. Letters, spaces, digits and @/./+/-/_/' only." \
|
||||||
|
"\nFor best security, make it longer than 8 characters. You can also use a phrase of" \
|
||||||
|
"\nmany words if you enclose the password in double quotes."
|
||||||
|
session.msg(string)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check IP and/or name bans
|
||||||
|
bans = ServerConfig.objects.conf("server_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"
|
||||||
|
session.msg(string)
|
||||||
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
|
return
|
||||||
|
|
||||||
# everything's ok. Create the new player account.
|
# everything's ok. Create the new player account.
|
||||||
try:
|
try:
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
|
||||||
|
|
||||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
|
||||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||||
|
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||||
try:
|
new_player = default_unloggedin._create_player(session, playername, password, permissions, email=email)
|
||||||
new_player = create.create_player(playername, email, password, permissions=permissions)
|
if new_player:
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
session.msg("There was an error creating the default Player/Character:\n%s\n"
|
|
||||||
" If this problem persists, contact an admin." % e)
|
|
||||||
logger.log_trace()
|
|
||||||
return
|
|
||||||
|
|
||||||
# This needs to be set so the engine knows this player is
|
|
||||||
# logging in for the first time. (so it knows to call the right
|
|
||||||
# hooks during login later)
|
|
||||||
new_player.db.FIRST_LOGIN = True
|
|
||||||
|
|
||||||
# join the new player to the public channel
|
|
||||||
pchanneldef = settings.CHANNEL_PUBLIC
|
|
||||||
if pchanneldef:
|
|
||||||
pchannel = ChannelDB.objects.get_channel(pchanneldef[0])
|
|
||||||
if not pchannel.connect(new_player):
|
|
||||||
string = "New player '%s' could not connect to public channel!" % new_player.key
|
|
||||||
logger.log_err(string)
|
|
||||||
|
|
||||||
if MULTISESSION_MODE < 2:
|
if MULTISESSION_MODE < 2:
|
||||||
# if we only allow one character, create one with the same name as Player
|
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||||
# (in mode 2, the character must be created manually once logging in)
|
default_unloggedin._create_character(session, new_player, typeclass, default_home, permissions)
|
||||||
new_character = create.create_object(typeclass, key=playername,
|
|
||||||
location=default_home, home=default_home,
|
|
||||||
permissions=permissions)
|
|
||||||
# set playable character list
|
|
||||||
new_player.db._playable_characters.append(new_character)
|
|
||||||
|
|
||||||
# allow only the character itself and the player to puppet this character (and Admin).
|
|
||||||
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Developer) or pperm(Developer)" %
|
|
||||||
(new_character.id, new_player.id))
|
|
||||||
|
|
||||||
# If no description is set, set a default description
|
|
||||||
if not new_character.db.desc:
|
|
||||||
new_character.db.desc = "This is a Player."
|
|
||||||
# We need to set this to have @ic auto-connect to this character
|
|
||||||
new_player.db._last_puppet = new_character
|
|
||||||
|
|
||||||
# tell the caller everything went well.
|
# tell the caller everything went well.
|
||||||
string = "A new account '%s' was created. Welcome!"
|
string = "A new account '%s' was created. Welcome!"
|
||||||
if " " in playername:
|
if " " in playername:
|
||||||
string += "\n\nYou can now log in with the command 'connect %s <your password>'."
|
string += "\n\nYou can now log in with the command 'connect \"%s\" <your password>'."
|
||||||
else:
|
else:
|
||||||
string += "\n\nYou can now log with the command 'connect %s <your password>'."
|
string += "\n\nYou can now log with the command 'connect %s <your password>'."
|
||||||
session.msg(string % (playername, email))
|
session.msg(string % (playername, email))
|
||||||
|
|
@ -261,6 +240,7 @@ class CmdUnconnectedCreate(MuxCommand):
|
||||||
# we won't see any errors at all.
|
# we won't see any errors at all.
|
||||||
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
|
session.msg("An error occurred. Please e-mail an admin if the problem persists.")
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedQuit(MuxCommand):
|
class CmdUnconnectedQuit(MuxCommand):
|
||||||
|
|
@ -276,8 +256,7 @@ class CmdUnconnectedQuit(MuxCommand):
|
||||||
def func(self):
|
def func(self):
|
||||||
"""Simply close the connection."""
|
"""Simply close the connection."""
|
||||||
session = self.caller
|
session = self.caller
|
||||||
session.msg("Good bye! Disconnecting ...")
|
session.sessionhandler.disconnect(session, "Good bye! Disconnecting.")
|
||||||
session.session_disconnect()
|
|
||||||
|
|
||||||
|
|
||||||
class CmdUnconnectedLook(MuxCommand):
|
class CmdUnconnectedLook(MuxCommand):
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ Installation/testing:
|
||||||
"""
|
"""
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
|
import datetime
|
||||||
import re
|
import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia import DefaultRoom
|
from evennia import DefaultRoom
|
||||||
|
|
@ -129,12 +130,13 @@ class ExtendedRoom(DefaultRoom):
|
||||||
"""
|
"""
|
||||||
Calculate the current time and season ids.
|
Calculate the current time and season ids.
|
||||||
"""
|
"""
|
||||||
# get the current time as parts of year and parts of day
|
# get the current time as parts of year and parts of day.
|
||||||
# returns a tuple (years,months,weeks,days,hours,minutes,sec)
|
# we assume a standard calendar here and use 24h format.
|
||||||
time = gametime.gametime(format=True)
|
timestamp = gametime.gametime(absolute=True)
|
||||||
month, hour = time[1], time[4]
|
# note that fromtimestamp includes the effects of server time zone!
|
||||||
season = float(month) / MONTHS_PER_YEAR
|
datestamp = datetime.datetime.fromtimestamp(timestamp)
|
||||||
timeslot = float(hour) / HOURS_PER_DAY
|
season = float(datestamp.month) / MONTHS_PER_YEAR
|
||||||
|
timeslot = float(datestamp.hour) / HOURS_PER_DAY
|
||||||
|
|
||||||
# figure out which slots these represent
|
# figure out which slots these represent
|
||||||
if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]:
|
if SEASONAL_BOUNDARIES[0] <= season < SEASONAL_BOUNDARIES[1]:
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,18 @@ When in use, all messages being sent to the character will make use of
|
||||||
the character's gender, for example the echo
|
the character's gender, for example the echo
|
||||||
|
|
||||||
```
|
```
|
||||||
char.msg("%s falls on {p face with a thud." % char.key)
|
char.msg("%s falls on |p face with a thud." % char.key)
|
||||||
```
|
```
|
||||||
|
|
||||||
will result in "Tom falls on his|her|its face with a thud" depending
|
will result in "Tom falls on his|her|its|their face with a thud"
|
||||||
on the gender of the object being messaged. Default gender is
|
depending on the gender of the object being messaged. Default gender
|
||||||
"neutral".
|
is "ambiguous" (they).
|
||||||
|
|
||||||
To use, have DefaultCharacter inherit from this, or change
|
To use, have DefaultCharacter inherit from this, or change
|
||||||
setting.DEFAULT_CHARACTER to point to this class.
|
setting.DEFAULT_CHARACTER to point to this class.
|
||||||
|
|
||||||
The `@gender` command needs to be added to the default cmdset
|
The `@gender` command needs to be added to the default cmdset before
|
||||||
before it becomes available.
|
it becomes available.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ _GENDER_PRONOUN_MAP = {"male": {"s": "he",
|
||||||
"p": "their",
|
"p": "their",
|
||||||
"a": "theirs"}
|
"a": "theirs"}
|
||||||
}
|
}
|
||||||
_RE_GENDER_PRONOUN = re.compile(r'({s|{S|{o|{O|{p|{P|{a|{A)')
|
_RE_GENDER_PRONOUN = re.compile(r'(?<!\|)\|(?!\|)[sSoOpPaA]')
|
||||||
|
|
||||||
# in-game command for setting the gender
|
# in-game command for setting the gender
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ class SetGender(Command):
|
||||||
Sets gender on yourself
|
Sets gender on yourself
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@gender male|female|neutral|ambiguous
|
@gender male||female||neutral||ambiguous
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key = "@gender"
|
key = "@gender"
|
||||||
|
|
@ -73,7 +73,7 @@ class SetGender(Command):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
arg = self.args.strip().lower()
|
arg = self.args.strip().lower()
|
||||||
if not arg in ("male", "female", "neutral", "ambiguous"):
|
if not arg in ("male", "female", "neutral", "ambiguous"):
|
||||||
caller.msg("Usage: @gender male|female|neutral|ambiguous")
|
caller.msg("Usage: @gender male||female||neutral||ambiguous")
|
||||||
return
|
return
|
||||||
caller.db.gender = arg
|
caller.db.gender = arg
|
||||||
caller.msg("Your gender was set to %s." % arg)
|
caller.msg("Your gender was set to %s." % arg)
|
||||||
|
|
@ -103,10 +103,10 @@ class GenderCharacter(DefaultCharacter):
|
||||||
regex_match (MatchObject): the regular expression match.
|
regex_match (MatchObject): the regular expression match.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- `{s`, `{S`: Subjective form: he, she, it, He, She, It, They
|
- `|s`, `|S`: Subjective form: he, she, it, He, She, It, They
|
||||||
- `{o`, `{O`: Objective form: him, her, it, Him, Her, It, Them
|
- `|o`, `|O`: Objective form: him, her, it, Him, Her, It, Them
|
||||||
- `{p`, `{P`: Possessive form: his, her, its, His, Her, Its, Their
|
- `|p`, `|P`: Possessive form: his, her, its, His, Her, Its, Their
|
||||||
- `{a`, `{A`: Absolute Possessive form: his, hers, its, His, Hers, Its, Theirs
|
- `|a`, `|A`: Absolute Possessive form: his, hers, its, His, Hers, Its, Theirs
|
||||||
|
|
||||||
"""
|
"""
|
||||||
typ = regex_match.group()[1] # "s", "O" etc
|
typ = regex_match.group()[1] # "s", "O" etc
|
||||||
|
|
@ -134,5 +134,8 @@ class GenderCharacter(DefaultCharacter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# pre-process the text before continuing
|
# pre-process the text before continuing
|
||||||
|
try:
|
||||||
text = _RE_GENDER_PRONOUN.sub(self._get_pronoun, text)
|
text = _RE_GENDER_PRONOUN.sub(self._get_pronoun, text)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
super(GenderCharacter, self).msg(text, from_obj=from_obj, session=session, **kwargs)
|
super(GenderCharacter, self).msg(text, from_obj=from_obj, session=session, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,15 @@ Evennia Contribution - grungies1138 2016
|
||||||
A simple Brandymail style @mail system that uses the Msg class from Evennia Core.
|
A simple Brandymail style @mail system that uses the Msg class from Evennia Core.
|
||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
import MailCommand from this module into the default Player or Character command set
|
import CmdMail from this module (from evennia.contrib.mail import CmdMail),
|
||||||
|
and add into the default Player or Character command set (self.add(CmdMail)).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from evennia import ObjectDB, PlayerDB
|
||||||
from evennia import default_cmds
|
from evennia import default_cmds
|
||||||
from evennia.utils import create, evtable
|
from evennia.utils import create, evtable, make_iter
|
||||||
from evennia.comms.models import Msg
|
from evennia.comms.models import Msg
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,7 +52,7 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
@mail 2
|
@mail 2
|
||||||
@mail Griatch=New mail/Hey man, I am sending you a message!
|
@mail Griatch=New mail/Hey man, I am sending you a message!
|
||||||
@mail/delete 6
|
@mail/delete 6
|
||||||
@mail/forward feend78 Griatch=You guys should read this.
|
@mail/forward feend78 Griatch=4/You guys should read this.
|
||||||
@mail/reply 9=Thanks for the info!
|
@mail/reply 9=Thanks for the info!
|
||||||
"""
|
"""
|
||||||
key = "@mail"
|
key = "@mail"
|
||||||
|
|
@ -56,9 +60,67 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
lock = "cmd:all()"
|
lock = "cmd:all()"
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
||||||
|
def search_targets(self, namelist):
|
||||||
|
"""
|
||||||
|
Search a list of targets of the same type as caller.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
caller (Object or Player): The type of object to search.
|
||||||
|
namelist (list): List of strings for objects to search for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
targetlist (list): List of matches, if any.
|
||||||
|
|
||||||
|
"""
|
||||||
|
nameregex = r"|".join(r"^%s$" % re.escape(name) for name in make_iter(namelist))
|
||||||
|
if hasattr(self.caller, "player") and self.caller.player:
|
||||||
|
matches = list(ObjectDB.objects.filter(db_key__iregex=nameregex))
|
||||||
|
else:
|
||||||
|
matches = list(PlayerDB.objects.filter(username__iregex=nameregex))
|
||||||
|
return matches
|
||||||
|
|
||||||
|
def get_all_mail(self):
|
||||||
|
"""
|
||||||
|
Returns a list of all the messages where the caller is a recipient.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
messages (list): list of Msg objects.
|
||||||
|
"""
|
||||||
|
# mail_messages = Msg.objects.get_by_tag(category="mail")
|
||||||
|
# messages = []
|
||||||
|
try:
|
||||||
|
player = self.caller.player
|
||||||
|
except AttributeError:
|
||||||
|
player = self.caller
|
||||||
|
messages = Msg.objects.get_by_tag(category="mail", raw_queryset=True).filter(db_receivers_players=player)
|
||||||
|
return messages
|
||||||
|
|
||||||
|
def send_mail(self, recipients, subject, message, caller):
|
||||||
|
"""
|
||||||
|
Function for sending new mail. Also useful for sending notifications from objects or systems.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
recipients (list): list of Player or character objects to receive the newly created mails.
|
||||||
|
subject (str): The header or subject of the message to be delivered.
|
||||||
|
message (str): The body of the message being sent.
|
||||||
|
caller (obj): The object (or Player or Character) that is sending the message.
|
||||||
|
"""
|
||||||
|
for recipient in recipients:
|
||||||
|
recipient.msg("You have received a new @mail from %s" % caller)
|
||||||
|
new_message = create.create_message(self.caller, message, receivers=recipient, header=subject)
|
||||||
|
new_message.tags.add("U", category="mail")
|
||||||
|
|
||||||
|
if recipients:
|
||||||
|
caller.msg("You sent your message.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
caller.msg("No valid players found. Cannot send message.")
|
||||||
|
return
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
subject = ""
|
subject = ""
|
||||||
body = ""
|
body = ""
|
||||||
|
|
||||||
if self.switches or self.args:
|
if self.switches or self.args:
|
||||||
if "delete" in self.switches:
|
if "delete" in self.switches:
|
||||||
try:
|
try:
|
||||||
|
|
@ -66,12 +128,15 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
self.caller.msg("No Message ID given. Unable to delete.")
|
self.caller.msg("No Message ID given. Unable to delete.")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if self.get_all_mail()[int(self.lhs) - 1]:
|
all_mail = self.get_all_mail()
|
||||||
self.get_all_mail()[int(self.lhs) - 1].delete()
|
mind = int(self.lhs) - 1
|
||||||
|
if all_mail[mind]:
|
||||||
|
all_mail[mind].delete()
|
||||||
self.caller.msg("Message %s deleted" % self.lhs)
|
self.caller.msg("Message %s deleted" % self.lhs)
|
||||||
else:
|
else:
|
||||||
|
raise IndexError
|
||||||
|
except IndexError:
|
||||||
self.caller.msg("That message does not exist.")
|
self.caller.msg("That message does not exist.")
|
||||||
return
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.caller.msg("Usage: @mail/delete <message ID>")
|
self.caller.msg("Usage: @mail/delete <message ID>")
|
||||||
elif "forward" in self.switches:
|
elif "forward" in self.switches:
|
||||||
|
|
@ -83,29 +148,33 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
self.caller.msg("You must define a message to forward.")
|
self.caller.msg("You must define a message to forward.")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
all_mail = self.get_all_mail()
|
||||||
if "/" in self.rhs:
|
if "/" in self.rhs:
|
||||||
message_number, message = self.rhs.split("/")
|
message_number, message = self.rhs.split("/")
|
||||||
if self.get_all_mail()[int(message_number) - 1]:
|
mind = int(message_number) - 1
|
||||||
old_message = self.get_all_mail()[int(message_number) - 1]
|
|
||||||
|
|
||||||
self.send_mail(self.lhslist, "FWD: " + old_message.header,
|
if all_mail[mind]:
|
||||||
|
old_message = all_mail[mind]
|
||||||
|
|
||||||
|
self.send_mail(self.search_targets(self.lhslist), "FWD: " + old_message.header,
|
||||||
message + "\n---- Original Message ----\n" + old_message.message,
|
message + "\n---- Original Message ----\n" + old_message.message,
|
||||||
self.caller)
|
self.caller)
|
||||||
self.caller.msg("Message forwarded.")
|
self.caller.msg("Message forwarded.")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("Message does not exist.")
|
raise IndexError
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
if self.get_all_mail()[int(self.rhs) - 1]:
|
mind = int(self.rhs) - 1
|
||||||
old_message = self.get_all_mail()[int(self.rhs) - 1]
|
if all_mail[mind]:
|
||||||
self.send_mail(self.lhslist, "FWD: " + old_message.header,
|
old_message = all_mail[mind]
|
||||||
|
self.send_mail(self.search_targets(self.lhslist), "FWD: " + old_message.header,
|
||||||
"\n---- Original Message ----\n" + old_message.message, self.caller)
|
"\n---- Original Message ----\n" + old_message.message, self.caller)
|
||||||
self.caller.msg("Message forwarded.")
|
self.caller.msg("Message forwarded.")
|
||||||
old_message.tags.remove("u", category="mail")
|
old_message.tags.remove("u", category="mail")
|
||||||
old_message.tags.add("f", category="mail")
|
old_message.tags.add("f", category="mail")
|
||||||
else:
|
else:
|
||||||
self.caller.msg("Message does not exist.")
|
raise IndexError
|
||||||
return
|
except IndexError:
|
||||||
|
self.caller.msg("Message does not exixt.")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.caller.msg("Usage: @mail/forward <player list>=<#>[/<Message>]")
|
self.caller.msg("Usage: @mail/forward <player list>=<#>[/<Message>]")
|
||||||
elif "reply" in self.switches:
|
elif "reply" in self.switches:
|
||||||
|
|
@ -117,29 +186,33 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
self.caller.msg("You must supply a reply message")
|
self.caller.msg("You must supply a reply message")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if self.get_all_mail()[int(self.lhs) - 1]:
|
all_mail = self.get_all_mail()
|
||||||
old_message = self.get_all_mail()[int(self.lhs) - 1]
|
mind = int(self.lhs) - 1
|
||||||
|
if all_mail[mind]:
|
||||||
|
old_message = all_mail[mind]
|
||||||
self.send_mail(old_message.senders, "RE: " + old_message.header,
|
self.send_mail(old_message.senders, "RE: " + old_message.header,
|
||||||
self.rhs + "\n---- Original Message ----\n" + old_message.message, self.caller)
|
self.rhs + "\n---- Original Message ----\n" + old_message.message, self.caller)
|
||||||
old_message.tags.remove("u", category="mail")
|
old_message.tags.remove("u", category="mail")
|
||||||
old_message.tags.add("r", category="mail")
|
old_message.tags.add("r", category="mail")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
raise IndexError
|
||||||
|
except IndexError:
|
||||||
self.caller.msg("Message does not exist.")
|
self.caller.msg("Message does not exist.")
|
||||||
return
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.caller.msg("Usage: @mail/reply <#>=<message>")
|
self.caller.msg("Usage: @mail/reply <#>=<message>")
|
||||||
else:
|
else:
|
||||||
|
# normal send
|
||||||
if self.rhs:
|
if self.rhs:
|
||||||
if "/" in self.rhs:
|
if "/" in self.rhs:
|
||||||
subject, body = self.rhs.split("/", 1)
|
subject, body = self.rhs.split("/", 1)
|
||||||
else:
|
else:
|
||||||
body = self.rhs
|
body = self.rhs
|
||||||
self.send_mail(self.lhslist, subject, body, self.caller)
|
self.send_mail(self.search_targets(self.lhslist), subject, body, self.caller)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
message = self.get_all_mail()[int(self.lhs) - 1]
|
message = self.get_all_mail()[int(self.lhs) - 1]
|
||||||
except ValueError:
|
except (ValueError, IndexError):
|
||||||
self.caller.msg("'%s' is not a valid mail id." % self.lhs)
|
self.caller.msg("'%s' is not a valid mail id." % self.lhs)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -176,47 +249,8 @@ class CmdMail(default_cmds.MuxCommand):
|
||||||
table.reformat_column(4, width=7)
|
table.reformat_column(4, width=7)
|
||||||
|
|
||||||
self.caller.msg(_HEAD_CHAR * _WIDTH)
|
self.caller.msg(_HEAD_CHAR * _WIDTH)
|
||||||
self.caller.msg(table)
|
self.caller.msg(unicode(table))
|
||||||
self.caller.msg(_HEAD_CHAR * _WIDTH)
|
self.caller.msg(_HEAD_CHAR * _WIDTH)
|
||||||
else:
|
else:
|
||||||
self.caller.msg("Sorry, you don't have any messages. What a pathetic loser!")
|
self.caller.msg("There are no messages in your inbox.")
|
||||||
|
|
||||||
def get_all_mail(self):
|
|
||||||
"""
|
|
||||||
Returns a list of all the messages where the caller is a recipient.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
messages (list): list of Msg objects.
|
|
||||||
"""
|
|
||||||
# mail_messages = Msg.objects.get_by_tag(category="mail")
|
|
||||||
# messages = []
|
|
||||||
messages = Msg.objects.get_by_tag(category="mail", raw_queryset=True).filter(db_receivers_players=self.caller.player)
|
|
||||||
return messages
|
|
||||||
|
|
||||||
def send_mail(self, recipients, subject, message, caller):
|
|
||||||
"""
|
|
||||||
Function for sending new mail. Also useful for sending notifications from objects or systems.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
recipients (list): list of Player or character objects to receive the newly created mails.
|
|
||||||
subject (str): The header or subject of the message to be delivered.
|
|
||||||
message (str): The body of the message being sent.
|
|
||||||
caller (obj): The object (or Player or Character) that is sending the message.
|
|
||||||
"""
|
|
||||||
recobjs = []
|
|
||||||
for char in recipients:
|
|
||||||
|
|
||||||
if self.caller.player.search(char) is not None:
|
|
||||||
recobjs.append(self.caller.player.search(char))
|
|
||||||
if recobjs:
|
|
||||||
for recipient in recobjs:
|
|
||||||
recipient.msg("You have received a new @mail from %s" % caller)
|
|
||||||
|
|
||||||
new_message = create.create_message(self.caller, message, receivers=recipient, header=subject)
|
|
||||||
new_message.tags.add("U", category="mail")
|
|
||||||
|
|
||||||
caller.msg("You sent your message.")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
caller.msg("No valid players found. Cannot send message.")
|
|
||||||
return
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ from typeclasses import rooms, exits
|
||||||
from random import randint
|
from random import randint
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
# A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
|
# A map with a temple (▲) amongst mountains (n,∩) in a forest (♣,♠) on an
|
||||||
# island surrounded by water (≈). By giving no instructions for the water
|
# island surrounded by water (≈). By giving no instructions for the water
|
||||||
# characters we effectively skip it and create no rooms for those squares.
|
# characters we effectively skip it and create no rooms for those squares.
|
||||||
|
|
@ -287,6 +288,88 @@ def _map_to_list(game_map):
|
||||||
else character for character in list_map]
|
else character for character in list_map]
|
||||||
|
|
||||||
|
|
||||||
|
def build_map(caller, game_map, legend, iterations=1, build_exits=True):
|
||||||
|
"""
|
||||||
|
Receives the fetched map and legend vars provided by the player.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
caller (Object): The creator of the map.
|
||||||
|
game_map (str): An ASCII map string.
|
||||||
|
legend (dict): Mapping of map symbols to object types.
|
||||||
|
iterations (int): The number of iteration passes.
|
||||||
|
build_exits (bool): Create exits between new rooms.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The map
|
||||||
|
is iterated over character by character, comparing it to the trigger
|
||||||
|
characters in the legend var and executing the build instructions on
|
||||||
|
finding a match. The map is iterated over according to the `iterations`
|
||||||
|
value and exits are optionally generated between adjacent rooms according
|
||||||
|
to the `build_exits` value.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Split map string to list of rows and create reference list.
|
||||||
|
caller.msg("Creating Map...")
|
||||||
|
caller.msg(game_map)
|
||||||
|
game_map = _map_to_list(game_map)
|
||||||
|
|
||||||
|
# Create a reference dictionary which be passed to build functions and
|
||||||
|
# will store obj returned by build functions so objs can be referenced.
|
||||||
|
room_dict = {}
|
||||||
|
|
||||||
|
caller.msg("Creating Landmass...")
|
||||||
|
for iteration in xrange(iterations):
|
||||||
|
for y in xrange(len(game_map)):
|
||||||
|
for x in xrange(len(game_map[y])):
|
||||||
|
for key in legend:
|
||||||
|
# obs - we must use == for unicode
|
||||||
|
if utils.to_unicode(game_map[y][x]) == utils.to_unicode(key):
|
||||||
|
room = legend[key](x, y, iteration=iteration,
|
||||||
|
room_dict=room_dict,
|
||||||
|
caller=caller)
|
||||||
|
if iteration == 0:
|
||||||
|
room_dict[(x, y)] = room
|
||||||
|
|
||||||
|
if build_exits:
|
||||||
|
# Creating exits. Assumes single room object in dict entry
|
||||||
|
caller.msg("Connecting Areas...")
|
||||||
|
for loc_key, location in room_dict.iteritems():
|
||||||
|
x = loc_key[0]
|
||||||
|
y = loc_key[1]
|
||||||
|
|
||||||
|
# north
|
||||||
|
if (x, y-1) in room_dict:
|
||||||
|
if room_dict[(x, y-1)]:
|
||||||
|
create_object(exits.Exit, key="north",
|
||||||
|
aliases=["n"], location=location,
|
||||||
|
destination=room_dict[(x, y-1)])
|
||||||
|
|
||||||
|
# east
|
||||||
|
if (x+1, y) in room_dict:
|
||||||
|
if room_dict[(x+1, y)]:
|
||||||
|
create_object(exits.Exit, key="east",
|
||||||
|
aliases=["e"], location=location,
|
||||||
|
destination=room_dict[(x+1, y)])
|
||||||
|
|
||||||
|
# south
|
||||||
|
if (x, y+1) in room_dict:
|
||||||
|
if room_dict[(x, y+1)]:
|
||||||
|
create_object(exits.Exit, key="south",
|
||||||
|
aliases=["s"], location=location,
|
||||||
|
destination=room_dict[(x, y+1)])
|
||||||
|
|
||||||
|
# west
|
||||||
|
if (x-1, y) in room_dict:
|
||||||
|
if room_dict[(x-1, y)]:
|
||||||
|
create_object(exits.Exit, key="west",
|
||||||
|
aliases=["w"], location=location,
|
||||||
|
destination=room_dict[(x-1, y)])
|
||||||
|
|
||||||
|
caller.msg("Map Created.")
|
||||||
|
|
||||||
|
# access command
|
||||||
|
|
||||||
class CmdMapBuilder(COMMAND_DEFAULT_CLASS):
|
class CmdMapBuilder(COMMAND_DEFAULT_CLASS):
|
||||||
"""
|
"""
|
||||||
Build a map from a 2D ASCII map.
|
Build a map from a 2D ASCII map.
|
||||||
|
|
@ -396,72 +479,3 @@ class CmdMapBuilder(COMMAND_DEFAULT_CLASS):
|
||||||
# Pass map and legend to the build function.
|
# Pass map and legend to the build function.
|
||||||
build_map(caller, game_map, legend, iterations, build_exits)
|
build_map(caller, game_map, legend, iterations, build_exits)
|
||||||
|
|
||||||
|
|
||||||
def build_map(caller, game_map, legend, iterations=1, build_exits=True):
|
|
||||||
"""
|
|
||||||
Receives the fetched map and legend vars provided by the player. The map
|
|
||||||
is iterated over character by character, comparing it to the trigger
|
|
||||||
characters in the legend var and executing the build instructions on
|
|
||||||
finding a match. The map is iterated over according to the `iterations`
|
|
||||||
value and exits are optionally generated between adjacent rooms according
|
|
||||||
to the `build_exits` value.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Split map string to list of rows and create reference list.
|
|
||||||
caller.msg("Creating Map...")
|
|
||||||
caller.msg(game_map)
|
|
||||||
game_map = _map_to_list(game_map)
|
|
||||||
|
|
||||||
# Create a reference dictionary which be passed to build functions and
|
|
||||||
# will store obj returned by build functions so objs can be referenced.
|
|
||||||
room_dict = {}
|
|
||||||
|
|
||||||
caller.msg("Creating Landmass...")
|
|
||||||
for iteration in xrange(iterations):
|
|
||||||
for y in xrange(len(game_map)):
|
|
||||||
for x in xrange(len(game_map[y])):
|
|
||||||
for key in legend:
|
|
||||||
if game_map[y][x] in key:
|
|
||||||
room = legend[key](x, y, iteration=iteration,
|
|
||||||
room_dict=room_dict,
|
|
||||||
caller=caller)
|
|
||||||
if iteration == 0:
|
|
||||||
room_dict[(x, y)] = room
|
|
||||||
|
|
||||||
if build_exits:
|
|
||||||
# Creating exits. Assumes single room object in dict entry
|
|
||||||
caller.msg("Connecting Areas...")
|
|
||||||
for loc_key, location in room_dict.iteritems():
|
|
||||||
x = loc_key[0]
|
|
||||||
y = loc_key[1]
|
|
||||||
|
|
||||||
# north
|
|
||||||
if (x, y-1) in room_dict:
|
|
||||||
if room_dict[(x, y-1)]:
|
|
||||||
create_object(exits.Exit, key="north",
|
|
||||||
aliases=["n"], location=location,
|
|
||||||
destination=room_dict[(x, y-1)])
|
|
||||||
|
|
||||||
# east
|
|
||||||
if (x+1, y) in room_dict:
|
|
||||||
if room_dict[(x+1, y)]:
|
|
||||||
create_object(exits.Exit, key="east",
|
|
||||||
aliases=["e"], location=location,
|
|
||||||
destination=room_dict[(x+1, y)])
|
|
||||||
|
|
||||||
# south
|
|
||||||
if (x, y+1) in room_dict:
|
|
||||||
if room_dict[(x, y+1)]:
|
|
||||||
create_object(exits.Exit, key="south",
|
|
||||||
aliases=["s"], location=location,
|
|
||||||
destination=room_dict[(x, y+1)])
|
|
||||||
|
|
||||||
# west
|
|
||||||
if (x-1, y) in room_dict:
|
|
||||||
if room_dict[(x-1, y)]:
|
|
||||||
create_object(exits.Exit, key="west",
|
|
||||||
aliases=["w"], location=location,
|
|
||||||
destination=room_dict[(x-1, y)])
|
|
||||||
|
|
||||||
caller.msg("Map Created.")
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,11 @@ Contribution - Griatch 2016
|
||||||
|
|
||||||
A simple two-way exit that represents a door that can be opened and
|
A simple two-way exit that represents a door that can be opened and
|
||||||
closed. Can easily be expanded from to make it lockable, destroyable
|
closed. Can easily be expanded from to make it lockable, destroyable
|
||||||
etc.
|
etc. Note that the simpledoor is based on Evennia locks, so it will
|
||||||
|
not work for a superuser (which bypasses all locks) - the superuser
|
||||||
|
will always appear to be able to close/open the door over and over
|
||||||
|
without the locks stopping you. To use the door, use `@quell` or a
|
||||||
|
non-superuser account.
|
||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,9 +135,10 @@ class CmdStop(Command):
|
||||||
stored deferred from the exit traversal above.
|
stored deferred from the exit traversal above.
|
||||||
"""
|
"""
|
||||||
currently_moving = self.caller.ndb.currently_moving
|
currently_moving = self.caller.ndb.currently_moving
|
||||||
if currently_moving:
|
if currently_moving and not currently_moving.called:
|
||||||
currently_moving.cancel()
|
currently_moving.cancel()
|
||||||
self.caller.msg("You stop moving.")
|
self.caller.msg("You stop moving.")
|
||||||
self.caller.location.msg_contents("%s stops." % self.get_display_name())
|
for observer in self.caller.location.contents_get(self.caller):
|
||||||
|
observer.msg("%s stops." % self.caller.get_display_name(observer))
|
||||||
else:
|
else:
|
||||||
self.caller.msg("You are not moving.")
|
self.caller.msg("You are not moving.")
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Testing suite for contrib folder
|
Testing suite for contrib folder
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
import datetime
|
||||||
from evennia.commands.default.tests import CommandTest
|
from evennia.commands.default.tests import CommandTest
|
||||||
from evennia.utils.test_resources import EvenniaTest
|
from evennia.utils.test_resources import EvenniaTest
|
||||||
from mock import Mock
|
from mock import Mock, patch
|
||||||
|
|
||||||
# Testing of rplanguage module
|
# Testing of rplanguage module
|
||||||
|
|
||||||
|
|
@ -168,44 +169,52 @@ class TestRPSystem(EvenniaTest):
|
||||||
|
|
||||||
# Testing of ExtendedRoom contrib
|
# Testing of ExtendedRoom contrib
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from evennia.contrib import extended_room
|
from evennia.contrib import extended_room
|
||||||
from evennia import gametime
|
from evennia import gametime
|
||||||
from evennia.objects.objects import DefaultRoom
|
from evennia.objects.objects import DefaultRoom
|
||||||
|
|
||||||
# mock gametime to return 7th month, 10 in morning
|
class ForceUTCDatetime(datetime.datetime):
|
||||||
gametime.gametime = Mock(return_value=(None, 7, None, None, 10))
|
|
||||||
# mock settings so we're not affected by a given server's hours of day/months in year
|
|
||||||
settings.TIME_MONTH_PER_YEAR = 12
|
|
||||||
settings.TIME_HOUR_PER_DAY = 24
|
|
||||||
|
|
||||||
|
"""Force UTC datetime."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromtimestamp(cls, timestamp):
|
||||||
|
"""Force fromtimestamp to run with naive datetimes."""
|
||||||
|
return datetime.datetime.utcfromtimestamp(timestamp)
|
||||||
|
|
||||||
|
@patch('evennia.contrib.extended_room.datetime.datetime', ForceUTCDatetime)
|
||||||
class TestExtendedRoom(CommandTest):
|
class TestExtendedRoom(CommandTest):
|
||||||
room_typeclass = extended_room.ExtendedRoom
|
room_typeclass = extended_room.ExtendedRoom
|
||||||
DETAIL_DESC = "A test detail."
|
DETAIL_DESC = "A test detail."
|
||||||
SUMMER_DESC = "A summer description."
|
SPRING_DESC = "A spring description."
|
||||||
OLD_DESC = "Old description."
|
OLD_DESC = "Old description."
|
||||||
|
settings.TIME_ZONE = "UTC"
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestExtendedRoom, self).setUp()
|
super(TestExtendedRoom, self).setUp()
|
||||||
self.room1.ndb.last_timeslot = "night"
|
self.room1.ndb.last_timeslot = "afternoon"
|
||||||
self.room1.ndb.last_season = "winter"
|
self.room1.ndb.last_season = "winter"
|
||||||
self.room1.db.details = {'testdetail': self.DETAIL_DESC}
|
self.room1.db.details = {'testdetail': self.DETAIL_DESC}
|
||||||
self.room1.db.summer_desc = self.SUMMER_DESC
|
self.room1.db.spring_desc = self.SPRING_DESC
|
||||||
self.room1.db.desc = self.OLD_DESC
|
self.room1.db.desc = self.OLD_DESC
|
||||||
|
# mock gametime to return April 9, 2064, at 21:06 (spring evening)
|
||||||
|
gametime.gametime = Mock(return_value=2975000766)
|
||||||
|
|
||||||
def test_return_appearance(self):
|
def test_return_appearance(self):
|
||||||
# get the appearance of a non-extended room for contrast purposes
|
# get the appearance of a non-extended room for contrast purposes
|
||||||
old_desc = DefaultRoom.return_appearance(self.room1, self.char1)
|
old_desc = DefaultRoom.return_appearance(self.room1, self.char1)
|
||||||
# the new appearance should be the old one, but with the desc switched
|
# the new appearance should be the old one, but with the desc switched
|
||||||
self.assertEqual(old_desc.replace(self.OLD_DESC, self.SUMMER_DESC), self.room1.return_appearance(self.char1))
|
self.assertEqual(old_desc.replace(self.OLD_DESC, self.SPRING_DESC),
|
||||||
self.assertEqual("summer", self.room1.ndb.last_season)
|
self.room1.return_appearance(self.char1))
|
||||||
self.assertEqual("morning", self.room1.ndb.last_timeslot)
|
self.assertEqual("spring", self.room1.ndb.last_season)
|
||||||
|
self.assertEqual("evening", self.room1.ndb.last_timeslot)
|
||||||
|
|
||||||
def test_return_detail(self):
|
def test_return_detail(self):
|
||||||
self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail"))
|
self.assertEqual(self.DETAIL_DESC, self.room1.return_detail("testdetail"))
|
||||||
|
|
||||||
def test_cmdextendedlook(self):
|
def test_cmdextendedlook(self):
|
||||||
self.call(extended_room.CmdExtendedLook(), "here", "Room(#1)\n%s" % self.SUMMER_DESC)
|
self.call(extended_room.CmdExtendedLook(), "here", "Room(#1)\n%s" % self.SPRING_DESC)
|
||||||
self.call(extended_room.CmdExtendedLook(), "testdetail", self.DETAIL_DESC)
|
self.call(extended_room.CmdExtendedLook(), "testdetail", self.DETAIL_DESC)
|
||||||
self.call(extended_room.CmdExtendedLook(), "nonexistent", "Could not find 'nonexistent'.")
|
self.call(extended_room.CmdExtendedLook(), "nonexistent", "Could not find 'nonexistent'.")
|
||||||
|
|
||||||
|
|
@ -219,7 +228,7 @@ class TestExtendedRoom(CommandTest):
|
||||||
self.call(extended_room.CmdExtendedDesc(), "", "Descriptions on Room:")
|
self.call(extended_room.CmdExtendedDesc(), "", "Descriptions on Room:")
|
||||||
|
|
||||||
def test_cmdgametime(self):
|
def test_cmdgametime(self):
|
||||||
self.call(extended_room.CmdGameTime(), "", "It's a summer day, in the morning.")
|
self.call(extended_room.CmdGameTime(), "", "It's a spring day, in the evening.")
|
||||||
|
|
||||||
|
|
||||||
# Test the contrib barter system
|
# Test the contrib barter system
|
||||||
|
|
@ -306,11 +315,11 @@ class TestBarter(CommandTest):
|
||||||
self.call(barter.CmdTradeHelp(), "", "Trading commands\n", caller=self.char1)
|
self.call(barter.CmdTradeHelp(), "", "Trading commands\n", caller=self.char1)
|
||||||
self.call(barter.CmdFinish(), ": Ending.", "You say, \"Ending.\"\n [You aborted trade. No deal was made.]")
|
self.call(barter.CmdFinish(), ": Ending.", "You say, \"Ending.\"\n [You aborted trade. No deal was made.]")
|
||||||
|
|
||||||
|
# Test wilderness
|
||||||
|
|
||||||
from evennia.contrib import wilderness
|
from evennia.contrib import wilderness
|
||||||
from evennia import DefaultCharacter
|
from evennia import DefaultCharacter
|
||||||
|
|
||||||
|
|
||||||
class TestWilderness(EvenniaTest):
|
class TestWilderness(EvenniaTest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
@ -426,3 +435,323 @@ class TestWilderness(EvenniaTest):
|
||||||
for direction, correct_loc in directions.iteritems(): # Not compatible with Python 3
|
for direction, correct_loc in directions.iteritems(): # Not compatible with Python 3
|
||||||
new_loc = wilderness.get_new_coordinates(loc, direction)
|
new_loc = wilderness.get_new_coordinates(loc, direction)
|
||||||
self.assertEquals(new_loc, correct_loc, direction)
|
self.assertEquals(new_loc, correct_loc, direction)
|
||||||
|
|
||||||
|
# Testing chargen contrib
|
||||||
|
from evennia.contrib import chargen
|
||||||
|
|
||||||
|
class TestChargen(CommandTest):
|
||||||
|
|
||||||
|
def test_ooclook(self):
|
||||||
|
self.call(chargen.CmdOOCLook(), "foo", "You have no characters to look at", caller=self.player)
|
||||||
|
self.call(chargen.CmdOOCLook(), "", "You, TestPlayer, are an OOC ghost without form.", caller=self.player)
|
||||||
|
|
||||||
|
def test_charcreate(self):
|
||||||
|
self.call(chargen.CmdOOCCharacterCreate(), "testchar", "The character testchar was successfully created!", caller=self.player)
|
||||||
|
self.call(chargen.CmdOOCCharacterCreate(), "testchar", "Character testchar already exists.", caller=self.player)
|
||||||
|
self.assertTrue(self.player.db._character_dbrefs)
|
||||||
|
self.call(chargen.CmdOOCLook(), "", "You, TestPlayer, are an OOC ghost without form.",caller=self.player)
|
||||||
|
self.call(chargen.CmdOOCLook(), "testchar", "testchar(", caller=self.player)
|
||||||
|
|
||||||
|
# Testing custom_gametime
|
||||||
|
from evennia.contrib import custom_gametime
|
||||||
|
|
||||||
|
def _testcallback():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestCustomGameTime(EvenniaTest):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestCustomGameTime, self).setUp()
|
||||||
|
gametime.gametime = Mock(return_value=2975000898.46) # does not seem to work
|
||||||
|
def tearDown(self):
|
||||||
|
if hasattr(self, "timescript"):
|
||||||
|
self.timescript.stop()
|
||||||
|
def test_time_to_tuple(self):
|
||||||
|
self.assertEqual(custom_gametime.time_to_tuple(10000, 34,2,4,6,1), (294, 2, 0, 0, 0, 0))
|
||||||
|
self.assertEqual(custom_gametime.time_to_tuple(10000, 3,3,4), (3333, 0, 0, 1))
|
||||||
|
self.assertEqual(custom_gametime.time_to_tuple(100000, 239,24,3), (418, 4, 0, 2))
|
||||||
|
def test_gametime_to_realtime(self):
|
||||||
|
self.assertEqual(custom_gametime.gametime_to_realtime(days=2, mins=4), 86520.0)
|
||||||
|
self.assertEqual(custom_gametime.gametime_to_realtime(format=True, days=2), (0,0,0,1,0,0,0))
|
||||||
|
def test_realtime_to_gametime(self):
|
||||||
|
self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34), 349680.0)
|
||||||
|
self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34, format=True), (0, 0, 0, 4, 1, 8, 0))
|
||||||
|
self.assertEqual(custom_gametime.realtime_to_gametime(format=True, days=2, mins=4), (0, 0, 0, 4, 0, 8, 0))
|
||||||
|
def test_custom_gametime(self):
|
||||||
|
self.assertEqual(custom_gametime.custom_gametime(), (102, 5, 2, 6, 21, 8, 18))
|
||||||
|
self.assertEqual(custom_gametime.custom_gametime(absolute=True), (102, 5, 2, 6, 21, 8, 18))
|
||||||
|
def test_real_seconds_until(self):
|
||||||
|
self.assertEqual(custom_gametime.real_seconds_until(year=2300, month=11, day=6), 31911667199.77)
|
||||||
|
def test_schedule(self):
|
||||||
|
self.timescript = custom_gametime.schedule(_testcallback, repeat=True, min=5, sec=0)
|
||||||
|
self.assertEqual(self.timescript.interval, 1700.7699999809265)
|
||||||
|
|
||||||
|
# Test dice module
|
||||||
|
|
||||||
|
|
||||||
|
@patch('random.randint', return_value=5)
|
||||||
|
class TestDice(CommandTest):
|
||||||
|
def test_roll_dice(self, mocked_randint):
|
||||||
|
# we must import dice here for the mocked randint to apply correctly.
|
||||||
|
from evennia.contrib import dice
|
||||||
|
self.assertEqual(dice.roll_dice(6, 6, modifier=('+', 4)), mocked_randint()*6 + 4)
|
||||||
|
self.assertEqual(dice.roll_dice(6, 6, conditional=('<', 35)), True)
|
||||||
|
self.assertEqual(dice.roll_dice(6, 6, conditional=('>', 33)), False)
|
||||||
|
def test_cmddice(self, mocked_randint):
|
||||||
|
from evennia.contrib import dice
|
||||||
|
self.call(dice.CmdDice(), "3d6 + 4", "You roll 3d6 + 4.| Roll(s): 5, 5 and 5. Total result is 19.")
|
||||||
|
self.call(dice.CmdDice(), "100000d1000", "The maximum roll allowed is 10000d10000.")
|
||||||
|
self.call(dice.CmdDice(), "/secret 3d6 + 4", "You roll 3d6 + 4 (secret, not echoed).")
|
||||||
|
|
||||||
|
# Test email-login
|
||||||
|
|
||||||
|
from evennia.contrib import email_login
|
||||||
|
|
||||||
|
class TestEmailLogin(CommandTest):
|
||||||
|
def test_connect(self):
|
||||||
|
self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test", "The email 'mytest@test.com' does not match any accounts.")
|
||||||
|
self.call(email_login.CmdUnconnectedCreate(), '"mytest" mytest@test.com test11111', "A new account 'mytest' was created. Welcome!")
|
||||||
|
self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test11111", "", caller=self.player.sessions.get()[0])
|
||||||
|
def test_quit(self):
|
||||||
|
self.call(email_login.CmdUnconnectedQuit(), "", "", caller=self.player.sessions.get()[0])
|
||||||
|
def test_unconnectedlook(self):
|
||||||
|
self.call(email_login.CmdUnconnectedLook(), "", "==========")
|
||||||
|
def test_unconnectedhelp(self):
|
||||||
|
self.call(email_login.CmdUnconnectedHelp(), "", "You are not yet logged into the game.")
|
||||||
|
|
||||||
|
# test gendersub contrib
|
||||||
|
|
||||||
|
from evennia.contrib import gendersub
|
||||||
|
|
||||||
|
class TestGenderSub(CommandTest):
|
||||||
|
def test_setgender(self):
|
||||||
|
self.call(gendersub.SetGender(), "male", "Your gender was set to male.")
|
||||||
|
self.call(gendersub.SetGender(), "ambiguous", "Your gender was set to ambiguous.")
|
||||||
|
self.call(gendersub.SetGender(), "Foo", "Usage: @gender")
|
||||||
|
def test_gendercharacter(self):
|
||||||
|
char = create_object(gendersub.GenderCharacter, key="Gendered", location=self.room1)
|
||||||
|
txt = "Test |p gender"
|
||||||
|
self.assertEqual(gendersub._RE_GENDER_PRONOUN.sub(char._get_pronoun, txt), "Test their gender")
|
||||||
|
|
||||||
|
# test mail contrib
|
||||||
|
|
||||||
|
from evennia.contrib import mail
|
||||||
|
|
||||||
|
class TestMail(CommandTest):
|
||||||
|
def test_mail(self):
|
||||||
|
self.call(mail.CmdMail(), "2", "'2' is not a valid mail id.", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "", "There are no messages in your inbox.", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "Char=Message 1", "You have received a new @mail from Char|You sent your message.", caller=self.char1)
|
||||||
|
self.call(mail.CmdMail(), "Char=Message 2", "You sent your message.", caller=self.char2)
|
||||||
|
self.call(mail.CmdMail(), "TestPlayer2=Message 2",
|
||||||
|
"You have received a new @mail from TestPlayer2(player 2)|You sent your message.", caller=self.player2)
|
||||||
|
self.call(mail.CmdMail(), "TestPlayer=Message 1", "You sent your message.", caller=self.player2)
|
||||||
|
self.call(mail.CmdMail(), "TestPlayer=Message 2", "You sent your message.", caller=self.player2)
|
||||||
|
self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "2", "From: TestPlayer2", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "/forward TestPlayer2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.player)
|
||||||
|
self.call(mail.CmdMail(), "/delete 2", "Message 2 deleted", caller=self.player)
|
||||||
|
|
||||||
|
# test map builder contrib
|
||||||
|
|
||||||
|
from evennia.contrib import mapbuilder
|
||||||
|
|
||||||
|
class TestMapBuilder(CommandTest):
|
||||||
|
def test_cmdmapbuilder(self):
|
||||||
|
self.call(mapbuilder.CmdMapBuilder(),
|
||||||
|
"evennia.contrib.mapbuilder.EXAMPLE1_MAP evennia.contrib.mapbuilder.EXAMPLE1_LEGEND",
|
||||||
|
"""Creating Map...|≈≈≈≈≈
|
||||||
|
≈♣n♣≈
|
||||||
|
≈∩▲∩≈
|
||||||
|
≈♠n♠≈
|
||||||
|
≈≈≈≈≈
|
||||||
|
|Creating Landmass...|""")
|
||||||
|
self.call(mapbuilder.CmdMapBuilder(),
|
||||||
|
"evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND",
|
||||||
|
"""Creating Map...|≈ ≈ ≈ ≈ ≈
|
||||||
|
|
||||||
|
≈ ♣♣♣ ≈
|
||||||
|
≈ ♣ ♣ ♣ ≈
|
||||||
|
≈ ♣♣♣ ≈
|
||||||
|
|
||||||
|
≈ ≈ ≈ ≈ ≈
|
||||||
|
|Creating Landmass...|""")
|
||||||
|
|
||||||
|
|
||||||
|
# test menu_login
|
||||||
|
|
||||||
|
from evennia.contrib import menu_login
|
||||||
|
|
||||||
|
class TestMenuLogin(CommandTest):
|
||||||
|
def test_cmdunloggedlook(self):
|
||||||
|
self.call(menu_login.CmdUnloggedinLook(), "", "======")
|
||||||
|
|
||||||
|
|
||||||
|
# test multidescer contrib
|
||||||
|
|
||||||
|
from evennia.contrib import multidescer
|
||||||
|
|
||||||
|
class TestMultidescer(CommandTest):
|
||||||
|
def test_cmdmultidesc(self):
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"/list", "Stored descs:\ncaller:")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"test = Desc 1", "Stored description 'test': \"Desc 1\"")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"test2 = Desc 2", "Stored description 'test2': \"Desc 2\"")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"/swap test-test2", "Swapped descs 'test' and 'test2'.")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"test3 = Desc 3init", "Stored description 'test3': \"Desc 3init\"")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"/list", "Stored descs:\ntest3: Desc 3init\ntest: Desc 1\ntest2: Desc 2\ncaller:")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"test3 = Desc 3", "Stored description 'test3': \"Desc 3\"")
|
||||||
|
self.call(multidescer.CmdMultiDesc(),"/set test1 + test2 + + test3", "test1 Desc 2 Desc 3\n\n"
|
||||||
|
"The above was set as the current description.")
|
||||||
|
self.assertEqual(self.char1.db.desc, "test1 Desc 2 Desc 3")
|
||||||
|
|
||||||
|
# test simpledoor contrib
|
||||||
|
|
||||||
|
from evennia.contrib import simpledoor
|
||||||
|
|
||||||
|
class TestSimpleDoor(CommandTest):
|
||||||
|
def test_cmdopen(self):
|
||||||
|
self.call(simpledoor.CmdOpen(), "newdoor;door:contrib.simpledoor.SimpleDoor,backdoor;door = Room2",
|
||||||
|
"Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A doortype exit was "
|
||||||
|
"created ignored eventual custom returnexit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).")
|
||||||
|
self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You close newdoor.", cmdstring="close")
|
||||||
|
self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already closed.", cmdstring="close")
|
||||||
|
self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You open newdoor.", cmdstring="open")
|
||||||
|
self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already open.", cmdstring="open")
|
||||||
|
|
||||||
|
# test slow_exit contrib
|
||||||
|
|
||||||
|
from evennia.contrib import slow_exit
|
||||||
|
slow_exit.MOVE_DELAY = {"stroll":0, "walk": 0, "run": 0, "sprint": 0}
|
||||||
|
|
||||||
|
class TestSlowExit(CommandTest):
|
||||||
|
def test_exit(self):
|
||||||
|
exi = create_object(slow_exit.SlowExit, key="slowexit", location=self.room1, destination=self.room2)
|
||||||
|
exi.at_traverse(self.char1, self.room2)
|
||||||
|
self.call(slow_exit.CmdSetSpeed(), "walk", "You are now walking.")
|
||||||
|
self.call(slow_exit.CmdStop(), "", "You stop moving.")
|
||||||
|
|
||||||
|
# test talking npc contrib
|
||||||
|
|
||||||
|
from evennia.contrib import talking_npc
|
||||||
|
|
||||||
|
class TestTalkingNPC(CommandTest):
|
||||||
|
def test_talkingnpc(self):
|
||||||
|
npc = create_object(talking_npc.TalkingNPC, key="npctalker", location=self.room1)
|
||||||
|
self.call(talking_npc.CmdTalk(), "","(You walk up and talk to Char.)|")
|
||||||
|
npc.delete()
|
||||||
|
|
||||||
|
|
||||||
|
# tests for the tutorial world
|
||||||
|
|
||||||
|
# test tutorial_world/mob
|
||||||
|
|
||||||
|
from evennia.contrib.tutorial_world import mob
|
||||||
|
|
||||||
|
class TestTutorialWorldMob(EvenniaTest):
|
||||||
|
def test_mob(self):
|
||||||
|
mobobj = create_object(mob.Mob, key="mob")
|
||||||
|
self.assertEqual(mobobj.db.is_dead, True)
|
||||||
|
mobobj.set_alive()
|
||||||
|
self.assertEqual(mobobj.db.is_dead, False)
|
||||||
|
mobobj.set_dead()
|
||||||
|
self.assertEqual(mobobj.db.is_dead, True)
|
||||||
|
mobobj._set_ticker(0, "foo", stop=True)
|
||||||
|
#TODO should be expanded with further tests of the modes and damage etc.
|
||||||
|
|
||||||
|
# test tutorial_world/objects
|
||||||
|
|
||||||
|
from evennia.contrib.tutorial_world import objects as tutobjects
|
||||||
|
|
||||||
|
class TestTutorialWorldObjects(CommandTest):
|
||||||
|
def test_tutorialobj(self):
|
||||||
|
obj1 = create_object(tutobjects.TutorialObject, key="tutobj")
|
||||||
|
obj1.reset()
|
||||||
|
self.assertEqual(obj1.location, obj1.home)
|
||||||
|
def test_readable(self):
|
||||||
|
readable = create_object(tutobjects.Readable, key="book", location=self.room1)
|
||||||
|
readable.db.readable_text = "Text to read"
|
||||||
|
self.call(tutobjects.CmdRead(), "book","You read book:\n Text to read", obj=readable)
|
||||||
|
def test_climbable(self):
|
||||||
|
climbable = create_object(tutobjects.Climbable, key="tree", location=self.room1)
|
||||||
|
self.call(tutobjects.CmdClimb(), "tree", "You climb tree. Having looked around, you climb down again.", obj=climbable)
|
||||||
|
self.assertEqual(self.char1.tags.get("tutorial_climbed_tree", category="tutorial_world"), "tutorial_climbed_tree")
|
||||||
|
def test_obelisk(self):
|
||||||
|
obelisk = create_object(tutobjects.Obelisk, key="obelisk", location=self.room1)
|
||||||
|
self.assertEqual(obelisk.return_appearance(self.char1).startswith("|cobelisk("), True)
|
||||||
|
def test_lightsource(self):
|
||||||
|
light = create_object(tutobjects.LightSource, key="torch", location=self.room1)
|
||||||
|
self.call(tutobjects.CmdLight(), "", "You light torch.", obj=light)
|
||||||
|
light._burnout()
|
||||||
|
if hasattr(light, "deferred"):
|
||||||
|
light.deferred.cancel()
|
||||||
|
self.assertFalse(light.pk)
|
||||||
|
def test_crumblingwall(self):
|
||||||
|
wall = create_object(tutobjects.CrumblingWall, key="wall", location=self.room1)
|
||||||
|
self.assertFalse(wall.db.button_exposed)
|
||||||
|
self.assertFalse(wall.db.exit_open)
|
||||||
|
wall.db.root_pos = {"yellow":0, "green":0,"red":0,"blue":0}
|
||||||
|
self.call(tutobjects.CmdShiftRoot(), "blue root right",
|
||||||
|
"You shove the root adorned with small blue flowers to the right.", obj=wall)
|
||||||
|
self.call(tutobjects.CmdShiftRoot(), "red root left",
|
||||||
|
"You shift the reddish root to the left.", obj=wall)
|
||||||
|
self.call(tutobjects.CmdShiftRoot(), "yellow root down",
|
||||||
|
"You shove the root adorned with small yellow flowers downwards.", obj=wall)
|
||||||
|
self.call(tutobjects.CmdShiftRoot(), "green root up",
|
||||||
|
"You shift the weedy green root upwards.|Holding aside the root you think you notice something behind it ...", obj=wall)
|
||||||
|
self.call(tutobjects.CmdPressButton(), "",
|
||||||
|
"You move your fingers over the suspicious depression, then gives it a decisive push. First", obj=wall)
|
||||||
|
self.assertTrue(wall.db.button_exposed)
|
||||||
|
self.assertTrue(wall.db.exit_open)
|
||||||
|
wall.reset()
|
||||||
|
if hasattr(wall, "deferred"):
|
||||||
|
wall.deferred.cancel()
|
||||||
|
wall.delete()
|
||||||
|
def test_weapon(self):
|
||||||
|
weapon = create_object(tutobjects.Weapon, key="sword", location=self.char1)
|
||||||
|
self.call(tutobjects.CmdAttack(), "Char", "You stab with sword.", obj=weapon, cmdstring="stab")
|
||||||
|
self.call(tutobjects.CmdAttack(), "Char", "You slash with sword.", obj=weapon, cmdstring="slash")
|
||||||
|
def test_weaponrack(self):
|
||||||
|
rack = create_object(tutobjects.WeaponRack, key="rack", location=self.room1)
|
||||||
|
rack.db.available_weapons = ["sword"]
|
||||||
|
self.call(tutobjects.CmdGetWeapon(), "", "You find Rusty sword.", obj=rack)
|
||||||
|
|
||||||
|
# test tutorial_world/
|
||||||
|
from evennia.contrib.tutorial_world import rooms as tutrooms
|
||||||
|
|
||||||
|
class TestTutorialWorldRooms(CommandTest):
|
||||||
|
def test_cmdtutorial(self):
|
||||||
|
room = create_object(tutrooms.TutorialRoom, key="tutroom")
|
||||||
|
self.char1.location = room
|
||||||
|
self.call(tutrooms.CmdTutorial(), "", "Sorry, there is no tutorial help available here.")
|
||||||
|
self.call(tutrooms.CmdTutorialSetDetail(), "detail;foo;foo2 = A detail", "Detail set: 'detail;foo;foo2': 'A detail'", obj=room)
|
||||||
|
self.call(tutrooms.CmdTutorialLook(), "", "tutroom(", obj=room)
|
||||||
|
self.call(tutrooms.CmdTutorialLook(), "detail", "A detail", obj=room)
|
||||||
|
self.call(tutrooms.CmdTutorialLook(), "foo", "A detail", obj=room)
|
||||||
|
room.delete()
|
||||||
|
def test_weatherroom(self):
|
||||||
|
room = create_object(tutrooms.WeatherRoom, key="weatherroom")
|
||||||
|
room.update_weather()
|
||||||
|
tutrooms.TICKER_HANDLER.remove(interval=room.db.interval, callback=room.update_weather, idstring="tutorial")
|
||||||
|
room.delete()
|
||||||
|
def test_introroom(self):
|
||||||
|
room = create_object(tutrooms.IntroRoom, key="introroom")
|
||||||
|
room.at_object_receive(self.char1, self.room1)
|
||||||
|
def test_bridgeroom(self):
|
||||||
|
room = create_object(tutrooms.BridgeRoom, key="bridgeroom")
|
||||||
|
room.update_weather()
|
||||||
|
self.char1.move_to(room)
|
||||||
|
self.call(tutrooms.CmdBridgeHelp(), "", "You are trying hard not to fall off the bridge ...", obj=room)
|
||||||
|
self.call(tutrooms.CmdLookBridge(), "", "bridgeroom\nYou are standing very close to the the bridge's western foundation.", obj=room)
|
||||||
|
room.at_object_leave(self.char1, self.room1)
|
||||||
|
tutrooms.TICKER_HANDLER.remove(interval=room.db.interval, callback=room.update_weather, idstring="tutorial")
|
||||||
|
room.delete()
|
||||||
|
def test_darkroom(self):
|
||||||
|
room = create_object(tutrooms.DarkRoom, key="darkroom")
|
||||||
|
self.char1.move_to(room)
|
||||||
|
self.call(tutrooms.CmdDarkHelp(), "", "Can't help you until")
|
||||||
|
def test_teleportroom(self):
|
||||||
|
create_object(tutrooms.TeleportRoom, key="teleportroom")
|
||||||
|
def test_outroroom(self):
|
||||||
|
create_object(tutrooms.OutroRoom, key="outroroom")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ Example script for testing. This adds a simple timer that has your
|
||||||
character make observations and notices at irregular intervals.
|
character make observations and notices at irregular intervals.
|
||||||
|
|
||||||
To test, use
|
To test, use
|
||||||
@script me = examples.bodyfunctions.BodyFunctions
|
@script me = tutorial_examples.bodyfunctions.BodyFunctions
|
||||||
|
|
||||||
The script will only send messages to the object it is stored on, so
|
The script will only send messages to the object it is stored on, so
|
||||||
make sure to put it on yourself or you won't see any messages!
|
make sure to put it on yourself or you won't see any messages!
|
||||||
|
|
|
||||||
|
|
@ -367,8 +367,9 @@ class LightSource(TutorialObject):
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
# start the burn timer. When it runs out, self._burnout
|
# start the burn timer. When it runs out, self._burnout
|
||||||
# will be called.
|
# will be called. We store the deferred so it can be
|
||||||
utils.delay(60 * 3, self._burnout)
|
# killed in unittesting.
|
||||||
|
self.deferred = utils.delay(60 * 3, self._burnout)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -636,9 +637,10 @@ class CrumblingWall(TutorialObject, DefaultExit):
|
||||||
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
|
self.caller.msg("The exit leads nowhere, there's just more stone behind it ...")
|
||||||
else:
|
else:
|
||||||
self.destination = eloc[0]
|
self.destination = eloc[0]
|
||||||
self.exit_open = True
|
self.db.exit_open = True
|
||||||
# start a 45 second timer before closing again
|
# start a 45 second timer before closing again. We store the deferred so it can be
|
||||||
utils.delay(45, self.reset)
|
# killed in unittesting.
|
||||||
|
self.deferred = utils.delay(45, self.reset)
|
||||||
|
|
||||||
def _translate_position(self, root, ipos):
|
def _translate_position(self, root, ipos):
|
||||||
"""Translates the position into words"""
|
"""Translates the position into words"""
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ class TutorialRoom(DefaultRoom):
|
||||||
# These are rainy weather strings
|
# These are rainy weather strings
|
||||||
WEATHER_STRINGS = (
|
WEATHER_STRINGS = (
|
||||||
"The rain coming down from the iron-grey sky intensifies.",
|
"The rain coming down from the iron-grey sky intensifies.",
|
||||||
"A gush of wind throws the rain right in your face. Despite your cloak you shiver.",
|
"A gust of wind throws the rain right in your face. Despite your cloak you shiver.",
|
||||||
"The rainfall eases a bit and the sky momentarily brightens.",
|
"The rainfall eases a bit and the sky momentarily brightens.",
|
||||||
"For a moment it looks like the rain is slowing, then it begins anew with renewed force.",
|
"For a moment it looks like the rain is slowing, then it begins anew with renewed force.",
|
||||||
"The rain pummels you with large, heavy drops. You hear the rumble of thunder in the distance.",
|
"The rain pummels you with large, heavy drops. You hear the rumble of thunder in the distance.",
|
||||||
|
|
@ -313,8 +313,8 @@ class WeatherRoom(TutorialRoom):
|
||||||
# subscribe ourselves to a ticker to repeatedly call the hook
|
# subscribe ourselves to a ticker to repeatedly call the hook
|
||||||
# "update_weather" on this object. The interval is randomized
|
# "update_weather" on this object. The interval is randomized
|
||||||
# so as to not have all weather rooms update at the same time.
|
# so as to not have all weather rooms update at the same time.
|
||||||
interval = random.randint(50, 70)
|
self.db.interval = random.randint(50, 70)
|
||||||
TICKER_HANDLER.add(interval=interval, callback=self.update_weather, idstring="tutorial")
|
TICKER_HANDLER.add(interval=self.db.interval, callback=self.update_weather, idstring="tutorial")
|
||||||
# this is parsed by the 'tutorial' command on TutorialRooms.
|
# this is parsed by the 'tutorial' command on TutorialRooms.
|
||||||
self.db.tutorial_info = \
|
self.db.tutorial_info = \
|
||||||
"This room has a Script running that has it echo a weather-related message at irregular intervals."
|
"This room has a Script running that has it echo a weather-related message at irregular intervals."
|
||||||
|
|
@ -590,7 +590,7 @@ class BridgeCmdSet(CmdSet):
|
||||||
|
|
||||||
BRIDGE_WEATHER = (
|
BRIDGE_WEATHER = (
|
||||||
"The rain intensifies, making the planks of the bridge even more slippery.",
|
"The rain intensifies, making the planks of the bridge even more slippery.",
|
||||||
"A gush of wind throws the rain right in your face.",
|
"A gust of wind throws the rain right in your face.",
|
||||||
"The rainfall eases a bit and the sky momentarily brightens.",
|
"The rainfall eases a bit and the sky momentarily brightens.",
|
||||||
"The bridge shakes under the thunder of a closeby thunder strike.",
|
"The bridge shakes under the thunder of a closeby thunder strike.",
|
||||||
"The rain pummels you with large, heavy drops. You hear the distinct howl of a large hound in the distance.",
|
"The rain pummels you with large, heavy drops. You hear the distinct howl of a large hound in the distance.",
|
||||||
|
|
|
||||||
|
|
@ -216,20 +216,20 @@ msgstr ""
|
||||||
#: server/initial_setup.py:29
|
#: server/initial_setup.py:29
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"Welcome to your new {wEvennia{n-based game! Visit http://www.evennia.com if "
|
"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if "
|
||||||
"you need\n"
|
"you need\n"
|
||||||
"help, want to contribute, report issues or just join the community.\n"
|
"help, want to contribute, report issues or just join the community.\n"
|
||||||
"As Player #1 you can create a demo/tutorial area with {w@batchcommand "
|
"As Player #1 you can create a demo/tutorial area with |w@batchcommand "
|
||||||
"tutorial_world.build{n.\n"
|
"tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Bienvenue dans ton nouveau jeu basé sur {wEvennia{n ! Visitez http://www."
|
"Bienvenue dans ton nouveau jeu basé sur |wEvennia|n ! Visitez http://www."
|
||||||
"evennia.com si vous avez besoin\n"
|
"evennia.com si vous avez besoin\n"
|
||||||
"d'aide, si vous voulez contribuer, rapporter des problèmes ou faire partie "
|
"d'aide, si vous voulez contribuer, rapporter des problèmes ou faire partie "
|
||||||
"de la communauté.\n"
|
"de la communauté.\n"
|
||||||
"En tant que Joueur #1 vous pouvez créer une zone de démo/tutoriel avec "
|
"En tant que Joueur #1 vous pouvez créer une zone de démo/tutoriel avec "
|
||||||
"{w@batchcommand tutorial_world.build{n.\n"
|
"|w@batchcommand tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: server/initial_setup.py:102
|
#: server/initial_setup.py:102
|
||||||
|
|
|
||||||
|
|
@ -221,16 +221,16 @@ msgstr "Aggiorna l'handler del canale."
|
||||||
#: .\server\initial_setup.py:29
|
#: .\server\initial_setup.py:29
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"Welcome to your new {wEvennia{n-based game! Visit http://www.evennia.com if you need\n"
|
"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if you need\n"
|
||||||
"help, want to contribute, report issues or just join the community.\n"
|
"help, want to contribute, report issues or just join the community.\n"
|
||||||
"As Player #1 you can create a demo/tutorial area with {w@batchcommand tutorial_world.build{n.\n"
|
"As Player #1 you can create a demo/tutorial area with |w@batchcommand tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Benvenuto al tuo nuovo gioco creato con {wEvennia{n! Visita http://www.evennia.com se ti\n"
|
"Benvenuto al tuo nuovo gioco creato con |wEvennia|n! Visita http://www.evennia.com se ti\n"
|
||||||
"serve aiuto, se vuoi collaborare, segnalare errori o se desideri unirti alla comunità online.\n"
|
"serve aiuto, se vuoi collaborare, segnalare errori o se desideri unirti alla comunità online.\n"
|
||||||
"In qualità di Giocatore #1 puoi creare un'area dimostrativa/tutorial digitando il comando:\n"
|
"In qualità di Giocatore #1 puoi creare un'area dimostrativa/tutorial digitando il comando:\n"
|
||||||
"{w@batchcommand tutorial_world.build{n.\n"
|
"|w@batchcommand tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: .\server\initial_setup.py:99
|
#: .\server\initial_setup.py:99
|
||||||
|
|
|
||||||
|
|
@ -208,19 +208,19 @@ msgstr ""
|
||||||
#: server/initial_setup.py:30
|
#: server/initial_setup.py:30
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"Welcome to your new {wEvennia{n-based game! Visit http://www.evennia.com if "
|
"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if "
|
||||||
"you need\n"
|
"you need\n"
|
||||||
"help, want to contribute, report issues or just join the community.\n"
|
"help, want to contribute, report issues or just join the community.\n"
|
||||||
"As Player #1 you can create a demo/tutorial area with {w@batchcommand "
|
"As Player #1 you can create a demo/tutorial area with |w@batchcommand "
|
||||||
"tutorial_world.build{n.\n"
|
"tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Bem-vindo a seu novo jogo criado com {wEvennia{n! Visite http://www.evennia.com\n"
|
"Bem-vindo a seu novo jogo criado com |wEvennia|n! Visite http://www.evennia.com\n"
|
||||||
"se você precisar de ajuda, desejar contribuir, reportar bugs ou apenas\n"
|
"se você precisar de ajuda, desejar contribuir, reportar bugs ou apenas\n"
|
||||||
"juntar-se à comunidade.\n"
|
"juntar-se à comunidade.\n"
|
||||||
"Como Player #1 você pode criar uma área demonstrativa/tutorial digitando\n"
|
"Como Player #1 você pode criar uma área demonstrativa/tutorial digitando\n"
|
||||||
"o comando {w@batchcommand tutorial_world.build{n.\n"
|
"o comando |w@batchcommand tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
|
|
||||||
#: server/initial_setup.py:103
|
#: server/initial_setup.py:103
|
||||||
|
|
|
||||||
|
|
@ -214,11 +214,11 @@ msgstr "Detta är en generisk lagringskontainer."
|
||||||
#: server/initial_setup.py:29
|
#: server/initial_setup.py:29
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"Welcome to your new {wEvennia{n-based game! Visit http://www.evennia.com if "
|
"Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if "
|
||||||
"you need\n"
|
"you need\n"
|
||||||
"help, want to contribute, report issues or just join the community.\n"
|
"help, want to contribute, report issues or just join the community.\n"
|
||||||
"As Player #1 you can create a demo/tutorial area with {w@batchcommand "
|
"As Player #1 you can create a demo/tutorial area with |w@batchcommand "
|
||||||
"tutorial_world.build{n.\n"
|
"tutorial_world.build|n.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,21 @@ class ObjectCreateForm(forms.ModelForm):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
db_key = forms.CharField(label="Name/Key",
|
db_key = forms.CharField(label="Name/Key",
|
||||||
widget=forms.TextInput(attrs={'size': '78'}),
|
widget=forms.TextInput(attrs={'size': '78'}),
|
||||||
help_text="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. If creating a Character, check so the name is unique among characters!",)
|
help_text="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. "
|
||||||
|
"If creating a Character, check so the name is unique among characters!",)
|
||||||
db_typeclass_path = forms.CharField(label="Typeclass",
|
db_typeclass_path = forms.CharField(label="Typeclass",
|
||||||
initial=settings.BASE_OBJECT_TYPECLASS,
|
initial=settings.BASE_OBJECT_TYPECLASS,
|
||||||
widget=forms.TextInput(attrs={'size': '78'}),
|
widget=forms.TextInput(attrs={'size': '78'}),
|
||||||
help_text="This defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. If you are creating a Character you should use the typeclass defined by settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
help_text="This defines what 'type' of entity this is. This variable holds a "
|
||||||
|
"Python path to a module with a valid Evennia Typeclass. If you are "
|
||||||
|
"creating a Character you should use the typeclass defined by "
|
||||||
|
"settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
||||||
db_cmdset_storage = forms.CharField(label="CmdSet",
|
db_cmdset_storage = forms.CharField(label="CmdSet",
|
||||||
initial="",
|
initial="",
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(attrs={'size': '78'}),
|
widget=forms.TextInput(attrs={'size': '78'}),
|
||||||
help_text="Most non-character objects don't need a cmdset and can leave this field blank.")
|
help_text="Most non-character objects don't need a cmdset"
|
||||||
|
" and can leave this field blank.")
|
||||||
raw_id_fields = ('db_destination', 'db_location', 'db_home')
|
raw_id_fields = ('db_destination', 'db_location', 'db_home')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -64,7 +69,9 @@ class ObjectEditForm(ObjectCreateForm):
|
||||||
db_lock_storage = forms.CharField(label="Locks",
|
db_lock_storage = forms.CharField(label="Locks",
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.Textarea(attrs={'cols': '100', 'rows': '2'}),
|
widget=forms.Textarea(attrs={'cols': '100', 'rows': '2'}),
|
||||||
help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form <i>type:lockfunction(args);type2:lockfunction2(args);...")
|
help_text="In-game lock definition string. If not given, defaults will be used. "
|
||||||
|
"This string should be on the form "
|
||||||
|
"<i>type:lockfunction(args);type2:lockfunction2(args);...")
|
||||||
|
|
||||||
|
|
||||||
class ObjectDBAdmin(admin.ModelAdmin):
|
class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ _MULTIMATCH_REGEX = re.compile(settings.SEARCH_MULTIMATCH_REGEX, re.I + re.U)
|
||||||
|
|
||||||
# Try to use a custom way to parse id-tagged multimatches.
|
# Try to use a custom way to parse id-tagged multimatches.
|
||||||
|
|
||||||
|
|
||||||
class ObjectDBManager(TypedObjectManager):
|
class ObjectDBManager(TypedObjectManager):
|
||||||
"""
|
"""
|
||||||
This ObjectManager implements methods for searching
|
This ObjectManager implements methods for searching
|
||||||
|
|
@ -79,11 +80,13 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
if dbref:
|
if dbref:
|
||||||
return dbref
|
return dbref
|
||||||
# not a dbref. Search by name.
|
# not a dbref. Search by name.
|
||||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
if exact:
|
if exact:
|
||||||
return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
|
return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
|
||||||
else: # fuzzy matching
|
else: # fuzzy matching
|
||||||
ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True)
|
ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)
|
||||||
|
).values_list("db_key", flat=True)
|
||||||
if candidates:
|
if candidates:
|
||||||
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
|
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
|
||||||
return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
|
return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
|
||||||
|
|
@ -103,7 +106,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
Returns:
|
Returns:
|
||||||
matches (list): The matching objects.
|
matches (list): The matching objects.
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
return self.filter(cand_restriction & Q(db_key__iexact=oname, db_typeclass_path__exact=otypeclass_path))
|
return self.filter(cand_restriction & Q(db_key__iexact=oname, db_typeclass_path__exact=otypeclass_path))
|
||||||
|
|
||||||
# attr/property related
|
# attr/property related
|
||||||
|
|
@ -121,7 +125,9 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
matches (list): All objects having the given attribute_name defined at all.
|
matches (list): All objects having the given attribute_name defined at all.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates != None and Q(db_attributes__db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(db_attributes__db_obj__pk__in=[_GA(obj, "id") for obj
|
||||||
|
in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
return list(self.filter(cand_restriction & Q(db_attributes__db_key=attribute_name)))
|
return list(self.filter(cand_restriction & Q(db_attributes__db_key=attribute_name)))
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
|
|
@ -144,20 +150,23 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
cannot be indexed, searching by Attribute key is to be preferred whenever possible.
|
cannot be indexed, searching by Attribute key is to be preferred whenever possible.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||||
|
|
||||||
## This doesn't work if attribute_value is an object. Workaround below
|
# This doesn't work if attribute_value is an object. Workaround below
|
||||||
|
|
||||||
if isinstance(attribute_value, (basestring, int, float, bool)):
|
if isinstance(attribute_value, (basestring, int, float, bool)):
|
||||||
return self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name, db_attributes__db_value=attribute_value))
|
return self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name,
|
||||||
|
db_attributes__db_value=attribute_value))
|
||||||
else:
|
else:
|
||||||
# We have to loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
|
# We must loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
|
||||||
global _ATTR
|
global _ATTR
|
||||||
if not _ATTR:
|
if not _ATTR:
|
||||||
from evennia.typeclasses.models import Attribute as _ATTR
|
from evennia.typeclasses.models import Attribute as _ATTR
|
||||||
cands = list(self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name)))
|
cands = list(self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name)))
|
||||||
results = [attr.objectdb_set.all() for attr in _ATTR.objects.filter(objectdb__in=cands, db_value=attribute_value)]
|
results = [attr.objectdb_set.all() for attr in _ATTR.objects.filter(objectdb__in=cands,
|
||||||
|
db_value=attribute_value)]
|
||||||
return chain(*results)
|
return chain(*results)
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
|
|
@ -174,7 +183,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
property_name = "db_%s" % property_name.lstrip('db_')
|
property_name = "db_%s" % property_name.lstrip('db_')
|
||||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
querykwargs = {property_name: None}
|
querykwargs = {property_name: None}
|
||||||
try:
|
try:
|
||||||
return list(self.filter(cand_restriction).exclude(Q(**querykwargs)))
|
return list(self.filter(cand_restriction).exclude(Q(**querykwargs)))
|
||||||
|
|
@ -199,7 +209,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
if not property_name.startswith('db_'):
|
if not property_name.startswith('db_'):
|
||||||
property_name = "db_%s" % property_name
|
property_name = "db_%s" % property_name
|
||||||
querykwargs = {property_name: property_value}
|
querykwargs = {property_name: property_value}
|
||||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||||
|
if obj]) or Q()
|
||||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||||
try:
|
try:
|
||||||
return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs)))
|
return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs)))
|
||||||
|
|
@ -207,7 +218,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
return []
|
return []
|
||||||
except ValueError:
|
except ValueError:
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
logger.log_err("The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value)))
|
logger.log_err("The property '%s' does not support search criteria of the type %s." %
|
||||||
|
(property_name, type(property_value)))
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
|
|
@ -253,7 +265,7 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
|
|
||||||
# build query objects
|
# build query objects
|
||||||
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
|
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
|
||||||
cand_restriction = candidates != None and Q(pk__in=candidates_id) or Q()
|
cand_restriction = candidates is not None and Q(pk__in=candidates_id) or Q()
|
||||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||||
if exact:
|
if exact:
|
||||||
# exact match - do direct search
|
# exact match - do direct search
|
||||||
|
|
@ -264,7 +276,8 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
search_candidates = self.filter(cand_restriction & type_restriction)
|
search_candidates = self.filter(cand_restriction & type_restriction)
|
||||||
else:
|
else:
|
||||||
# fuzzy without supplied candidates - we select our own candidates
|
# fuzzy without supplied candidates - we select our own candidates
|
||||||
search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring))).distinct()
|
search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) |
|
||||||
|
Q(db_tags__db_key__istartswith=ostring))).distinct()
|
||||||
# fuzzy matching
|
# fuzzy matching
|
||||||
key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")
|
key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")
|
||||||
|
|
||||||
|
|
@ -278,7 +291,7 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
db_tags__db_key__icontains=ostring)
|
db_tags__db_key__icontains=ostring)
|
||||||
alias_strings = []
|
alias_strings = []
|
||||||
alias_candidates = []
|
alias_candidates = []
|
||||||
#TODO create the alias_strings and alias_candidates lists more effiently?
|
# TODO create the alias_strings and alias_candidates lists more efficiently?
|
||||||
for candidate in search_candidates:
|
for candidate in search_candidates:
|
||||||
for alias in candidate.aliases.all():
|
for alias in candidate.aliases.all():
|
||||||
alias_strings.append(alias)
|
alias_strings.append(alias)
|
||||||
|
|
@ -343,13 +356,16 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
"""
|
"""
|
||||||
if attribute_name:
|
if attribute_name:
|
||||||
# attribute/property search (always exact).
|
# attribute/property search (always exact).
|
||||||
matches = self.get_objs_with_db_property_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
matches = self.get_objs_with_db_property_value(attribute_name, searchdata,
|
||||||
|
candidates=candidates, typeclasses=typeclass)
|
||||||
if matches:
|
if matches:
|
||||||
return matches
|
return matches
|
||||||
return self.get_objs_with_attr_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
return self.get_objs_with_attr_value(attribute_name, searchdata,
|
||||||
|
candidates=candidates, typeclasses=typeclass)
|
||||||
else:
|
else:
|
||||||
# normal key/alias search
|
# normal key/alias search
|
||||||
return self.get_objs_with_key_or_alias(searchdata, exact=exact, candidates=candidates, typeclasses=typeclass)
|
return self.get_objs_with_key_or_alias(searchdata, exact=exact,
|
||||||
|
candidates=candidates, typeclasses=typeclass)
|
||||||
|
|
||||||
if not searchdata and searchdata != 0:
|
if not searchdata and searchdata != 0:
|
||||||
return []
|
return []
|
||||||
|
|
@ -418,7 +434,6 @@ class ObjectDBManager(TypedObjectManager):
|
||||||
|
|
||||||
#
|
#
|
||||||
# ObjectManager Copy method
|
# ObjectManager Copy method
|
||||||
#
|
|
||||||
|
|
||||||
def copy_object(self, original_object, new_key=None,
|
def copy_object(self, original_object, new_key=None,
|
||||||
new_location=None, new_home=None,
|
new_location=None, new_home=None,
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,7 @@ class ContentsHandler(object):
|
||||||
Re-initialize the content cache
|
Re-initialize the content cache
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._pkcache.update(dict((obj.pk, None) for obj in
|
self._pkcache.update(dict((obj.pk, None) for obj in ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
||||||
ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
|
||||||
|
|
||||||
def get(self, exclude=None):
|
def get(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -79,7 +78,7 @@ class ContentsHandler(object):
|
||||||
return [self._idcache[pk] for pk in pks]
|
return [self._idcache[pk] for pk in pks]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# this means an actual failure of caching. Return real database match.
|
# this means an actual failure of caching. Return real database match.
|
||||||
logger.log_err("contents cache failed for %s." % (self.obj.key))
|
logger.log_err("contents cache failed for %s." % self.obj.key)
|
||||||
return list(ObjectDB.objects.filter(db_location=self.obj))
|
return list(ObjectDB.objects.filter(db_location=self.obj))
|
||||||
|
|
||||||
def add(self, obj):
|
def add(self, obj):
|
||||||
|
|
@ -110,11 +109,12 @@ class ContentsHandler(object):
|
||||||
self._pkcache = {}
|
self._pkcache = {}
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# ObjectDB
|
# ObjectDB
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class ObjectDB(TypedObject):
|
class ObjectDB(TypedObject):
|
||||||
"""
|
"""
|
||||||
|
|
@ -183,7 +183,8 @@ class ObjectDB(TypedObject):
|
||||||
db_home = models.ForeignKey('self', related_name="homes_set", on_delete=models.SET_NULL,
|
db_home = models.ForeignKey('self', related_name="homes_set", on_delete=models.SET_NULL,
|
||||||
blank=True, null=True, verbose_name='home location')
|
blank=True, null=True, verbose_name='home location')
|
||||||
# destination of this object - primarily used by exits.
|
# destination of this object - primarily used by exits.
|
||||||
db_destination = models.ForeignKey('self', related_name="destinations_set", db_index=True, on_delete=models.SET_NULL,
|
db_destination = models.ForeignKey('self', related_name="destinations_set",
|
||||||
|
db_index=True, on_delete=models.SET_NULL,
|
||||||
blank=True, null=True, verbose_name='destination',
|
blank=True, null=True, verbose_name='destination',
|
||||||
help_text='a destination, used only by exit objects.')
|
help_text='a destination, used only by exit objects.')
|
||||||
# database storage of persistant cmdsets.
|
# database storage of persistant cmdsets.
|
||||||
|
|
@ -204,28 +205,28 @@ class ObjectDB(TypedObject):
|
||||||
|
|
||||||
# cmdset_storage property handling
|
# cmdset_storage property handling
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
"getter"
|
"""getter"""
|
||||||
storage = self.db_cmdset_storage
|
storage = self.db_cmdset_storage
|
||||||
return [path.strip() for path in storage.split(',')] if storage else []
|
return [path.strip() for path in storage.split(',')] if storage else []
|
||||||
|
|
||||||
def __cmdset_storage_set(self, value):
|
def __cmdset_storage_set(self, value):
|
||||||
"setter"
|
"""setter"""
|
||||||
self.db_cmdset_storage = ",".join(str(val).strip() for val in make_iter(value))
|
self.db_cmdset_storage = ",".join(str(val).strip() for val in make_iter(value))
|
||||||
self.save(update_fields=["db_cmdset_storage"])
|
self.save(update_fields=["db_cmdset_storage"])
|
||||||
|
|
||||||
def __cmdset_storage_del(self):
|
def __cmdset_storage_del(self):
|
||||||
"deleter"
|
"""deleter"""
|
||||||
self.db_cmdset_storage = None
|
self.db_cmdset_storage = None
|
||||||
self.save(update_fields=["db_cmdset_storage"])
|
self.save(update_fields=["db_cmdset_storage"])
|
||||||
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
|
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
|
||||||
|
|
||||||
# location getsetter
|
# location getsetter
|
||||||
def __location_get(self):
|
def __location_get(self):
|
||||||
"Get location"
|
"""Get location"""
|
||||||
return self.db_location
|
return self.db_location
|
||||||
|
|
||||||
def __location_set(self, location):
|
def __location_set(self, location):
|
||||||
"Set location, checking for loops and allowing dbref"
|
"""Set location, checking for loops and allowing dbref"""
|
||||||
if isinstance(location, (basestring, int)):
|
if isinstance(location, (basestring, int)):
|
||||||
# allow setting of #dbref
|
# allow setting of #dbref
|
||||||
dbid = dbref(location, reqhash=False)
|
dbid = dbref(location, reqhash=False)
|
||||||
|
|
@ -237,9 +238,9 @@ class ObjectDB(TypedObject):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
def is_loc_loop(loc, depth=0):
|
def is_loc_loop(loc, depth=0):
|
||||||
"Recursively traverse target location, trying to catch a loop."
|
"""Recursively traverse target location, trying to catch a loop."""
|
||||||
if depth > 10:
|
if depth > 10:
|
||||||
return
|
return None
|
||||||
elif loc == self:
|
elif loc == self:
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
elif loc is None:
|
elif loc is None:
|
||||||
|
|
@ -248,7 +249,7 @@ class ObjectDB(TypedObject):
|
||||||
try:
|
try:
|
||||||
is_loc_loop(location)
|
is_loc_loop(location)
|
||||||
except RuntimeWarning:
|
except RuntimeWarning:
|
||||||
# we caught a infitite location loop!
|
# we caught an infinite location loop!
|
||||||
# (location1 is in location2 which is in location1 ...)
|
# (location1 is in location2 which is in location1 ...)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -281,7 +282,7 @@ class ObjectDB(TypedObject):
|
||||||
return
|
return
|
||||||
|
|
||||||
def __location_del(self):
|
def __location_del(self):
|
||||||
"Cleanly delete the location reference"
|
"""Cleanly delete the location reference"""
|
||||||
self.db_location = None
|
self.db_location = None
|
||||||
self.save(update_fields=["db_location"])
|
self.save(update_fields=["db_location"])
|
||||||
location = property(__location_get, __location_set, __location_del)
|
location = property(__location_get, __location_set, __location_del)
|
||||||
|
|
@ -311,7 +312,6 @@ class ObjectDB(TypedObject):
|
||||||
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"Define Django meta options"
|
"""Define Django meta options"""
|
||||||
verbose_name = "Object"
|
verbose_name = "Object"
|
||||||
verbose_name_plural = "Objects"
|
verbose_name_plural = "Objects"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ from evennia.commands.cmdsethandler import CmdSetHandler
|
||||||
from evennia.commands import cmdhandler
|
from evennia.commands import cmdhandler
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
from evennia.utils.utils import (variable_from_module, lazy_property,
|
from evennia.utils.utils import (variable_from_module, lazy_property,
|
||||||
make_iter, to_unicode, calledby)
|
make_iter, to_unicode, calledby, is_iter)
|
||||||
|
|
||||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||||
|
|
||||||
|
|
@ -34,6 +34,7 @@ _SESSID_MAX = 16 if _MULTISESSION_MODE in (1, 3) else 1
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
class ObjectSessionHandler(object):
|
class ObjectSessionHandler(object):
|
||||||
"""
|
"""
|
||||||
Handles the get/setting of the sessid
|
Handles the get/setting of the sessid
|
||||||
|
|
@ -133,7 +134,7 @@ class ObjectSessionHandler(object):
|
||||||
Remove session from handler.
|
Remove session from handler.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sessid (Session or int): Session or session id to remove.
|
session (Session or int): Session or session id to remove.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|
@ -167,10 +168,9 @@ class ObjectSessionHandler(object):
|
||||||
return len(self._sessid_cache)
|
return len(self._sessid_cache)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base class to inherit from.
|
# Base class to inherit from.
|
||||||
#
|
|
||||||
|
|
||||||
class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
"""
|
"""
|
||||||
|
|
@ -241,7 +241,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
con = self.contents_cache.get(exclude=exclude)
|
con = self.contents_cache.get(exclude=exclude)
|
||||||
#print "contents_get:", self, con, id(self), calledby()
|
# print "contents_get:", self, con, id(self), calledby() # DEBUG
|
||||||
return con
|
return con
|
||||||
contents = property(contents_get)
|
contents = property(contents_get)
|
||||||
|
|
||||||
|
|
@ -474,11 +474,9 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
# nick replacement - we require full-word matching.
|
# nick replacement - we require full-word matching.
|
||||||
# do text encoding conversion
|
# do text encoding conversion
|
||||||
raw_string = to_unicode(raw_string)
|
raw_string = to_unicode(raw_string)
|
||||||
raw_string = self.nicks.nickreplace(raw_string,
|
raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=True)
|
||||||
categories=("inputline", "channel"), include_player=True)
|
|
||||||
return cmdhandler.cmdhandler(self, raw_string, callertype="object", session=session, **kwargs)
|
return cmdhandler.cmdhandler(self, raw_string, callertype="object", session=session, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Emits something to a session attached to the object.
|
Emits something to a session attached to the object.
|
||||||
|
|
@ -549,21 +547,28 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
for obj in contents:
|
for obj in contents:
|
||||||
func(obj, **kwargs)
|
func(obj, **kwargs)
|
||||||
|
|
||||||
def msg_contents(self, message, exclude=None, from_obj=None, mapping=None, **kwargs):
|
def msg_contents(self, text=None, exclude=None, from_obj=None, mapping=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Emits a message to all objects inside this object.
|
Emits a message to all objects inside this object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message (str): Message to send.
|
text (str or tuple): Message to send. If a tuple, this should be
|
||||||
|
on the valid OOB outmessage form `(message, {kwargs})`,
|
||||||
|
where kwargs are optional data passed to the `text`
|
||||||
|
outputfunc.
|
||||||
exclude (list, optional): A list of objects not to send to.
|
exclude (list, optional): A list of objects not to send to.
|
||||||
from_obj (Object, optional): An object designated as the
|
from_obj (Object, optional): An object designated as the
|
||||||
"sender" of the message. See `DefaultObject.msg()` for
|
"sender" of the message. See `DefaultObject.msg()` for
|
||||||
more info.
|
more info.
|
||||||
mapping (dict, optional): A mapping of formatting keys
|
mapping (dict, optional): A mapping of formatting keys
|
||||||
`{"key":<object>, "key2":<object2>,...}. The keys
|
`{"key":<object>, "key2":<object2>,...}. The keys
|
||||||
must match `{key}` markers in `message` and will be
|
must match `{key}` markers in the `text` if this is a string or
|
||||||
|
in the internal `message` if `text` is a tuple. These
|
||||||
|
formatting statements will be
|
||||||
replaced by the return of `<object>.get_display_name(looker)`
|
replaced by the return of `<object>.get_display_name(looker)`
|
||||||
for every looker that is messaged.
|
for every looker in contents that receives the
|
||||||
|
message. This allows for every object to potentially
|
||||||
|
get its own customized string.
|
||||||
Kwargs:
|
Kwargs:
|
||||||
Keyword arguments will be passed on to `obj.msg()` for all
|
Keyword arguments will be passed on to `obj.msg()` for all
|
||||||
messaged objects.
|
messaged objects.
|
||||||
|
|
@ -577,14 +582,23 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
not have `get_display_name()`, its string value will be used.
|
not have `get_display_name()`, its string value will be used.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
Say char is a Character object and npc is an NPC object:
|
Say Char is a Character object and Npc is an NPC object:
|
||||||
|
|
||||||
action = 'kicks'
|
|
||||||
char.location.msg_contents(
|
char.location.msg_contents(
|
||||||
"{attacker} {action} {defender}",
|
"{attacker} kicks {defender}",
|
||||||
mapping=dict(attacker=char, defender=npc, action=action),
|
mapping=dict(attacker=char, defender=npc), exclude=(char, npc))
|
||||||
exclude=(char, npc))
|
|
||||||
|
This will result in everyone in the room seeing 'Char kicks NPC'
|
||||||
|
where everyone may potentially see different results for Char and Npc
|
||||||
|
depending on the results of `char.get_display_name(looker)` and
|
||||||
|
`npc.get_display_name(looker)` for each particular onlooker
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# we also accept an outcommand on the form (message, {kwargs})
|
||||||
|
is_outcmd = text and is_iter(text)
|
||||||
|
inmessage = text[0] if is_outcmd else text
|
||||||
|
outkwargs = text[1] if is_outcmd and len(text) > 1 else {}
|
||||||
|
|
||||||
contents = self.contents
|
contents = self.contents
|
||||||
if exclude:
|
if exclude:
|
||||||
exclude = make_iter(exclude)
|
exclude = make_iter(exclude)
|
||||||
|
|
@ -593,11 +607,11 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
if mapping:
|
if mapping:
|
||||||
substitutions = {t: sub.get_display_name(obj)
|
substitutions = {t: sub.get_display_name(obj)
|
||||||
if hasattr(sub, 'get_display_name')
|
if hasattr(sub, 'get_display_name')
|
||||||
else str(sub)
|
else str(sub) for t, sub in mapping.items()}
|
||||||
for t, sub in mapping.items()}
|
outmessage = inmessage.format(**substitutions)
|
||||||
obj.msg(message.format(**substitutions), from_obj=from_obj, **kwargs)
|
|
||||||
else:
|
else:
|
||||||
obj.msg(message, from_obj=from_obj, **kwargs)
|
outmessage = inmessage
|
||||||
|
obj.msg(text=(outmessage, outkwargs), from_obj=from_obj, **kwargs)
|
||||||
|
|
||||||
def move_to(self, destination, quiet=False,
|
def move_to(self, destination, quiet=False,
|
||||||
emit_to_obj=None, use_destination=True, to_none=False, move_hooks=True):
|
emit_to_obj=None, use_destination=True, to_none=False, move_hooks=True):
|
||||||
|
|
@ -642,7 +656,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def logerr(string="", err=None):
|
def logerr(string="", err=None):
|
||||||
"Simple log helper method"
|
"""Simple log helper method"""
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
self.msg("%s%s" % (string, "" if err is None else " (%s)" % err))
|
self.msg("%s%s" % (string, "" if err is None else " (%s)" % err))
|
||||||
return
|
return
|
||||||
|
|
@ -658,7 +672,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
self.location = None
|
self.location = None
|
||||||
return True
|
return True
|
||||||
emit_to_obj.msg(_("The destination doesn't exist."))
|
emit_to_obj.msg(_("The destination doesn't exist."))
|
||||||
return
|
return False
|
||||||
if destination.destination and use_destination:
|
if destination.destination and use_destination:
|
||||||
# traverse exits
|
# traverse exits
|
||||||
destination = destination.destination
|
destination = destination.destination
|
||||||
|
|
@ -667,7 +681,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
if move_hooks:
|
if move_hooks:
|
||||||
try:
|
try:
|
||||||
if not self.at_before_move(destination):
|
if not self.at_before_move(destination):
|
||||||
return
|
return False
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logerr(errtxt % "at_before_move()", err)
|
logerr(errtxt % "at_before_move()", err)
|
||||||
return False
|
return False
|
||||||
|
|
@ -779,7 +793,6 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
obj.msg(_(string))
|
obj.msg(_(string))
|
||||||
obj.move_to(home)
|
obj.move_to(home)
|
||||||
|
|
||||||
|
|
||||||
def copy(self, new_key=None):
|
def copy(self, new_key=None):
|
||||||
"""
|
"""
|
||||||
Makes an identical copy of this object, identical except for a
|
Makes an identical copy of this object, identical except for a
|
||||||
|
|
@ -803,15 +816,15 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
"""
|
"""
|
||||||
key = self.key
|
key = self.key
|
||||||
num = 1
|
num = 1
|
||||||
for obj in (obj for obj in self.location.contents
|
for _ in (obj for obj in self.location.contents
|
||||||
if obj.key.startswith(key) and
|
if obj.key.startswith(key) and obj.key.lstrip(key).isdigit()):
|
||||||
obj.key.lstrip(key).isdigit()):
|
|
||||||
num += 1
|
num += 1
|
||||||
return "%s%03i" % (key, num)
|
return "%s%03i" % (key, num)
|
||||||
new_key = new_key or find_clone_key()
|
new_key = new_key or find_clone_key()
|
||||||
return ObjectDB.objects.copy_object(self, new_key=new_key)
|
return ObjectDB.objects.copy_object(self, new_key=new_key)
|
||||||
|
|
||||||
delete_iter = 0
|
delete_iter = 0
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Deletes this object. Before deletion, this method makes sure
|
Deletes this object. Before deletion, this method makes sure
|
||||||
|
|
@ -955,8 +968,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
self.basetype_posthook_setup()
|
self.basetype_posthook_setup()
|
||||||
|
|
||||||
|
# hooks called by the game engine #
|
||||||
## hooks called by the game engine
|
|
||||||
|
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1032,8 +1044,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
have no cmdsets.
|
have no cmdsets.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
Usually not set but could be used e.g. to force rebuilding
|
caller (Session, Object or Player): The caller requesting
|
||||||
of a dynamically created cmdset or similar.
|
this cmdset.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
@ -1118,9 +1130,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
result (bool): The outcome of the access call.
|
result (bool): The outcome of the access call.
|
||||||
accessing_obj (Object or Player): The entity trying to
|
accessing_obj (Object or Player): The entity trying to gain access.
|
||||||
gain access. access_type (str): The type of access that
|
access_type (str): The type of access that was requested.
|
||||||
was requested.
|
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
Not used by default, added for possible expandability in a
|
Not used by default, added for possible expandability in a
|
||||||
|
|
@ -1150,7 +1161,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
# return has_perm(self, destination, "can_move")
|
# return has_perm(self, destination, "can_move")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def announce_move_from(self, destination):
|
def announce_move_from(self, destination, msg=None, mapping=None):
|
||||||
"""
|
"""
|
||||||
Called if the move is to be announced. This is
|
Called if the move is to be announced. This is
|
||||||
called while we are still standing in the old
|
called while we are still standing in the old
|
||||||
|
|
@ -1158,25 +1169,56 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
destination (Object): The place we are going to.
|
destination (Object): The place we are going to.
|
||||||
|
msg (str, optional): a replacement message.
|
||||||
|
mapping (dict, optional): additional mapping objects.
|
||||||
|
|
||||||
|
You can override this method and call its parent with a
|
||||||
|
message to simply change the default message. In the string,
|
||||||
|
you can use the following as mappings (between braces):
|
||||||
|
object: the object which is moving.
|
||||||
|
exit: the exit from which the object is moving (if found).
|
||||||
|
origin: the location of the object before the move.
|
||||||
|
destination: the location of the object after moving.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self.location:
|
if not self.location:
|
||||||
return
|
return
|
||||||
string = "%s is leaving %s, heading for %s."
|
if msg:
|
||||||
location = self.location
|
string = msg
|
||||||
for obj in self.location.contents:
|
else:
|
||||||
if obj != self:
|
string = "{object} is leaving {origin}, heading for {destination}."
|
||||||
obj.msg(string % (self.get_display_name(obj),
|
|
||||||
location.get_display_name(obj) if location else "nowhere",
|
|
||||||
destination.get_display_name(obj)))
|
|
||||||
|
|
||||||
def announce_move_to(self, source_location):
|
location = self.location
|
||||||
|
exits = [o for o in location.contents if o.location is location and o.destination is destination]
|
||||||
|
if not mapping:
|
||||||
|
mapping = {}
|
||||||
|
|
||||||
|
mapping.update({
|
||||||
|
"object": self,
|
||||||
|
"exit": exits[0] if exits else "somwhere",
|
||||||
|
"origin": location or "nowhere",
|
||||||
|
"destination": destination or "nowhere",
|
||||||
|
})
|
||||||
|
|
||||||
|
location.msg_contents(string, exclude=(self, ), mapping=mapping)
|
||||||
|
|
||||||
|
def announce_move_to(self, source_location, msg=None, mapping=None):
|
||||||
"""
|
"""
|
||||||
Called after the move if the move was not quiet. At this point
|
Called after the move if the move was not quiet. At this point
|
||||||
we are standing in the new location.
|
we are standing in the new location.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
source_location (Object): The place we came from
|
source_location (Object): The place we came from
|
||||||
|
msg (str, optional): the replacement message if location.
|
||||||
|
mapping (dict, optional): additional mapping objects.
|
||||||
|
|
||||||
|
You can override this method and call its parent with a
|
||||||
|
message to simply change the default message. In the string,
|
||||||
|
you can use the following as mappings (between braces):
|
||||||
|
object: the object which is moving.
|
||||||
|
exit: the exit from which the object is moving (if found).
|
||||||
|
origin: the location of the object before the move.
|
||||||
|
destination: the location of the object after moving.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -1187,13 +1229,31 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
self.location.msg(string)
|
self.location.msg(string)
|
||||||
return
|
return
|
||||||
|
|
||||||
string = "%s arrives to %s%s."
|
if source_location:
|
||||||
location = self.location
|
if msg:
|
||||||
for obj in self.location.contents:
|
string = msg
|
||||||
if obj != self:
|
else:
|
||||||
obj.msg(string % (self.get_display_name(obj),
|
string = "{object} arrives to {destination} from {origin}."
|
||||||
location.get_display_name(obj) if location else "nowhere",
|
else:
|
||||||
" from %s" % source_location.get_display_name(obj) if source_location else ""))
|
string = "{object} arrives to {destination}."
|
||||||
|
|
||||||
|
origin = source_location
|
||||||
|
destination = self.location
|
||||||
|
exits = []
|
||||||
|
if origin:
|
||||||
|
exits = [o for o in destination.contents if o.location is destination and o.destination is origin]
|
||||||
|
|
||||||
|
if not mapping:
|
||||||
|
mapping = {}
|
||||||
|
|
||||||
|
mapping.update({
|
||||||
|
"object": self,
|
||||||
|
"exit": exits[0] if exits else "somewhere",
|
||||||
|
"origin": origin or "nowhere",
|
||||||
|
"destination": destination or "nowhere",
|
||||||
|
})
|
||||||
|
|
||||||
|
destination.msg_contents(string, exclude=(self, ), mapping=mapping)
|
||||||
|
|
||||||
def at_after_move(self, source_location):
|
def at_after_move(self, source_location):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1288,7 +1348,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
check for this. .
|
check for this. .
|
||||||
|
|
||||||
Consider this a pre-processing method before msg is passed on
|
Consider this a pre-processing method before msg is passed on
|
||||||
to the user sesssion. If this method returns False, the msg
|
to the user session. If this method returns False, the msg
|
||||||
will not be passed on.
|
will not be passed on.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -1416,6 +1476,22 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def at_give(self, giver, getter):
|
||||||
|
"""
|
||||||
|
Called by the default `give` command when this object has been
|
||||||
|
given.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
giver (Object): The object giving this object.
|
||||||
|
getter (Object): The object getting this object.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
This hook cannot stop the give from happening. Use
|
||||||
|
permissions for that.
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def at_drop(self, dropper):
|
def at_drop(self, dropper):
|
||||||
"""
|
"""
|
||||||
Called by the default `drop` command when this object has been
|
Called by the default `drop` command when this object has been
|
||||||
|
|
@ -1425,7 +1501,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
dropper (Object): The object which just dropped this object.
|
dropper (Object): The object which just dropped this object.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
This hook cannot stop the pickup from happening. Use
|
This hook cannot stop the drop from happening. Use
|
||||||
permissions from that.
|
permissions from that.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -1565,7 +1641,7 @@ class DefaultCharacter(DefaultObject):
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base Room object
|
# Base Room object
|
||||||
#
|
|
||||||
|
|
||||||
class DefaultRoom(DefaultObject):
|
class DefaultRoom(DefaultObject):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1631,7 +1707,7 @@ class ExitCommand(command.Command):
|
||||||
|
|
||||||
#
|
#
|
||||||
# Base Exit object
|
# Base Exit object
|
||||||
#
|
|
||||||
|
|
||||||
class DefaultExit(DefaultObject):
|
class DefaultExit(DefaultObject):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1683,8 +1759,8 @@ class DefaultExit(DefaultObject):
|
||||||
exit_cmdset.add(cmd)
|
exit_cmdset.add(cmd)
|
||||||
return exit_cmdset
|
return exit_cmdset
|
||||||
|
|
||||||
|
|
||||||
# Command hooks
|
# Command hooks
|
||||||
|
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
Setup exit-security
|
Setup exit-security
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ from evennia.utils import utils
|
||||||
|
|
||||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||||
|
|
||||||
|
_IRC_ENABLED = settings.IRC_ENABLED
|
||||||
|
_RSS_ENABLED = settings.RSS_ENABLED
|
||||||
|
|
||||||
_SESSIONS = None
|
_SESSIONS = None
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,8 +81,10 @@ class BotStarter(DefaultScript):
|
||||||
"""
|
"""
|
||||||
self.db.started = False
|
self.db.started = False
|
||||||
|
|
||||||
|
#
|
||||||
# Bot base class
|
# Bot base class
|
||||||
|
|
||||||
|
|
||||||
class Bot(DefaultPlayer):
|
class Bot(DefaultPlayer):
|
||||||
"""
|
"""
|
||||||
A Bot will start itself when the server starts (it will generally
|
A Bot will start itself when the server starts (it will generally
|
||||||
|
|
@ -157,6 +162,12 @@ class IRCBot(Bot):
|
||||||
irc_ssl (bool): Indicates whether to use SSL connection.
|
irc_ssl (bool): Indicates whether to use SSL connection.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if not _IRC_ENABLED:
|
||||||
|
# the bot was created, then IRC was turned off. We delete
|
||||||
|
# ourselves (this will also kill the start script)
|
||||||
|
self.delete()
|
||||||
|
return
|
||||||
|
|
||||||
global _SESSIONS
|
global _SESSIONS
|
||||||
if not _SESSIONS:
|
if not _SESSIONS:
|
||||||
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
|
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
|
||||||
|
|
@ -328,7 +339,6 @@ class IRCBot(Bot):
|
||||||
if kwargs["type"] == "action":
|
if kwargs["type"] == "action":
|
||||||
# An action (irc pose)
|
# An action (irc pose)
|
||||||
text = "%s@%s %s" % (kwargs["user"], kwargs["channel"], txt)
|
text = "%s@%s %s" % (kwargs["user"], kwargs["channel"], txt)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# msg - A normal channel message
|
# msg - A normal channel message
|
||||||
text = "%s@%s: %s" % (kwargs["user"], kwargs["channel"], txt)
|
text = "%s@%s: %s" % (kwargs["user"], kwargs["channel"], txt)
|
||||||
|
|
@ -339,8 +349,10 @@ class IRCBot(Bot):
|
||||||
if self.ndb.ev_channel:
|
if self.ndb.ev_channel:
|
||||||
self.ndb.ev_channel.msg(text, senders=self.id)
|
self.ndb.ev_channel.msg(text, senders=self.id)
|
||||||
|
|
||||||
|
#
|
||||||
# RSS
|
# RSS
|
||||||
|
|
||||||
|
|
||||||
class RSSBot(Bot):
|
class RSSBot(Bot):
|
||||||
"""
|
"""
|
||||||
An RSS relayer. The RSS protocol itself runs a ticker to update
|
An RSS relayer. The RSS protocol itself runs a ticker to update
|
||||||
|
|
@ -354,12 +366,17 @@ class RSSBot(Bot):
|
||||||
Args:
|
Args:
|
||||||
ev_channel (str): Key of the Evennia channel to connect to.
|
ev_channel (str): Key of the Evennia channel to connect to.
|
||||||
rss_url (str): Full URL to the RSS feed to subscribe to.
|
rss_url (str): Full URL to the RSS feed to subscribe to.
|
||||||
rss_update_rate (int): How often for the feedreader to update.
|
rss_rate (int): How often for the feedreader to update.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If `ev_channel` does not exist.
|
RuntimeError: If `ev_channel` does not exist.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if not _RSS_ENABLED:
|
||||||
|
# The bot was created, then RSS was turned off. Delete ourselves.
|
||||||
|
self.delete()
|
||||||
|
return
|
||||||
|
|
||||||
global _SESSIONS
|
global _SESSIONS
|
||||||
if not _SESSIONS:
|
if not _SESSIONS:
|
||||||
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
|
from evennia.server.sessionhandler import SESSIONS as _SESSIONS
|
||||||
|
|
@ -390,7 +407,7 @@ class RSSBot(Bot):
|
||||||
Args:
|
Args:
|
||||||
session (Session, optional): Session responsible for this
|
session (Session, optional): Session responsible for this
|
||||||
command.
|
command.
|
||||||
text (str, optional): Command string.
|
txt (str, optional): Command string.
|
||||||
kwargs (dict, optional): Additional Information passed from bot.
|
kwargs (dict, optional): Additional Information passed from bot.
|
||||||
Not used by the RSSbot by default.
|
Not used by the RSSbot by default.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,17 +104,6 @@ class PlayerDB(TypedObject, AbstractUser):
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
verbose_name = 'Player'
|
verbose_name = 'Player'
|
||||||
|
|
||||||
# alias to the objs property
|
|
||||||
def __characters_get(self):
|
|
||||||
return self.objs
|
|
||||||
|
|
||||||
def __characters_set(self, value):
|
|
||||||
self.objs = value
|
|
||||||
|
|
||||||
def __characters_del(self):
|
|
||||||
raise Exception("Cannot delete name")
|
|
||||||
characters = property(__characters_get, __characters_set, __characters_del)
|
|
||||||
|
|
||||||
# cmdset_storage property
|
# cmdset_storage property
|
||||||
# This seems very sensitive to caching, so leaving it be for now /Griatch
|
# This seems very sensitive to caching, so leaving it be for now /Griatch
|
||||||
#@property
|
#@property
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ _MAX_NR_CHARACTERS = settings.MAX_NR_CHARACTERS
|
||||||
_CMDSET_PLAYER = settings.CMDSET_PLAYER
|
_CMDSET_PLAYER = settings.CMDSET_PLAYER
|
||||||
_CONNECT_CHANNEL = None
|
_CONNECT_CHANNEL = None
|
||||||
|
|
||||||
|
|
||||||
class PlayerSessionHandler(object):
|
class PlayerSessionHandler(object):
|
||||||
"""
|
"""
|
||||||
Manages the session(s) attached to a player.
|
Manages the session(s) attached to a player.
|
||||||
|
|
@ -99,7 +100,6 @@ class PlayerSessionHandler(object):
|
||||||
return len(self.get())
|
return len(self.get())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
"""
|
"""
|
||||||
This is the base Typeclass for all Players. Players represent
|
This is the base Typeclass for all Players. Players represent
|
||||||
|
|
@ -188,7 +188,6 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
def sessions(self):
|
def sessions(self):
|
||||||
return PlayerSessionHandler(self)
|
return PlayerSessionHandler(self)
|
||||||
|
|
||||||
|
|
||||||
# session-related methods
|
# session-related methods
|
||||||
|
|
||||||
def disconnect_session_from_player(self, session):
|
def disconnect_session_from_player(self, session):
|
||||||
|
|
@ -337,8 +336,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
by this Player.
|
by this Player.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return list(set(session.puppet for session in self.sessions.all()
|
return list(set(session.puppet for session in self.sessions.all() if session.puppet))
|
||||||
if session.puppet))
|
|
||||||
|
|
||||||
def __get_single_puppet(self):
|
def __get_single_puppet(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -383,7 +381,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
self.nicks.clear()
|
self.nicks.clear()
|
||||||
self.aliases.clear()
|
self.aliases.clear()
|
||||||
super(DefaultPlayer, self).delete(*args, **kwargs)
|
super(DefaultPlayer, self).delete(*args, **kwargs)
|
||||||
## methods inherited from database model
|
# methods inherited from database model
|
||||||
|
|
||||||
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -447,8 +445,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raw_string = to_unicode(raw_string)
|
raw_string = to_unicode(raw_string)
|
||||||
raw_string = self.nicks.nickreplace(raw_string,
|
raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=False)
|
||||||
categories=("inputline", "channel"), include_player=False)
|
|
||||||
if not session and _MULTISESSION_MODE in (0, 1):
|
if not session and _MULTISESSION_MODE in (0, 1):
|
||||||
# for these modes we use the first/only session
|
# for these modes we use the first/only session
|
||||||
sessions = self.sessions.get()
|
sessions = self.sessions.get()
|
||||||
|
|
@ -557,7 +554,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
return time.time() - float(min(conn))
|
return time.time() - float(min(conn))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
## player hooks
|
# player hooks
|
||||||
|
|
||||||
def basetype_setup(self):
|
def basetype_setup(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -600,7 +597,6 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Note that the hooks below also exist in the character object's
|
# Note that the hooks below also exist in the character object's
|
||||||
# typeclass. You can often ignore these and rely on the character
|
# typeclass. You can often ignore these and rely on the character
|
||||||
# ones instead, unless you are implementing a multi-character game
|
# ones instead, unless you are implementing a multi-character game
|
||||||
|
|
@ -847,34 +843,36 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
is_su = self.is_superuser
|
is_su = self.is_superuser
|
||||||
|
|
||||||
# text shown when looking in the ooc area
|
# text shown when looking in the ooc area
|
||||||
string = "Account |g%s|n (you are Out-of-Character)" % (self.key)
|
result = ["Account |g%s|n (you are Out-of-Character)" % self.key]
|
||||||
|
|
||||||
nsess = len(sessions)
|
nsess = len(sessions)
|
||||||
string += nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess
|
result.append(nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess)
|
||||||
for isess, sess in enumerate(sessions):
|
for isess, sess in enumerate(sessions):
|
||||||
csessid = sess.sessid
|
csessid = sess.sessid
|
||||||
addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and str(sess.address[0]) or str(sess.address))
|
addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple)
|
||||||
string += "\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) or " %s" % (isess + 1), addr)
|
and str(sess.address[0]) or str(sess.address))
|
||||||
string += "\n\n |whelp|n - more commands"
|
result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1)
|
||||||
string += "\n |wooc <Text>|n - talk on public channel"
|
or " %s" % (isess + 1), addr))
|
||||||
|
result.append("\n\n |whelp|n - more commands")
|
||||||
|
result.append("\n |wooc <Text>|n - talk on public channel")
|
||||||
|
|
||||||
charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1
|
charmax = _MAX_NR_CHARACTERS if _MULTISESSION_MODE > 1 else 1
|
||||||
|
|
||||||
if is_su or len(characters) < charmax:
|
if is_su or len(characters) < charmax:
|
||||||
if not characters:
|
if not characters:
|
||||||
string += "\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one."
|
result.append("\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one.")
|
||||||
else:
|
else:
|
||||||
string += "\n |w@charcreate <name> [=description]|n - create new character"
|
result.append("\n |w@charcreate <name> [=description]|n - create new character")
|
||||||
string += "\n |w@chardelete <name>|n - delete a character (cannot be undone!)"
|
result.append("\n |w@chardelete <name>|n - delete a character (cannot be undone!)")
|
||||||
|
|
||||||
if characters:
|
if characters:
|
||||||
string_s_ending = len(characters) > 1 and "s" or ""
|
string_s_ending = len(characters) > 1 and "s" or ""
|
||||||
string += "\n |w@ic <character>|n - enter the game (|w@ooc|n to get back here)"
|
result.append("\n |w@ic <character>|n - enter the game (|w@ooc|n to get back here)")
|
||||||
if is_su:
|
if is_su:
|
||||||
string += "\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters))
|
result.append("\n\nAvailable character%s (%i/unlimited):" % (string_s_ending, len(characters)))
|
||||||
else:
|
else:
|
||||||
string += "\n\nAvailable character%s%s:" % (string_s_ending,
|
result.append("\n\nAvailable character%s%s:"
|
||||||
charmax > 1 and " (%i/%i)" % (len(characters), charmax) or "")
|
% (string_s_ending, charmax > 1 and " (%i/%i)" % (len(characters), charmax) or ""))
|
||||||
|
|
||||||
for char in characters:
|
for char in characters:
|
||||||
csessions = char.sessions.all()
|
csessions = char.sessions.all()
|
||||||
|
|
@ -883,14 +881,16 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
# character is already puppeted
|
# character is already puppeted
|
||||||
sid = sess in sessions and sessions.index(sess) + 1
|
sid = sess in sessions and sessions.index(sess) + 1
|
||||||
if sess and sid:
|
if sess and sid:
|
||||||
string += "\n - |G%s|n [%s] (played by you in session %i)" % (char.key, ", ".join(char.permissions.all()), sid)
|
result.append("\n - |G%s|n [%s] (played by you in session %i)"
|
||||||
|
% (char.key, ", ".join(char.permissions.all()), sid))
|
||||||
else:
|
else:
|
||||||
string += "\n - |R%s|n [%s] (played by someone else)" % (char.key, ", ".join(char.permissions.all()))
|
result.append("\n - |R%s|n [%s] (played by someone else)"
|
||||||
|
% (char.key, ", ".join(char.permissions.all())))
|
||||||
else:
|
else:
|
||||||
# character is "free to puppet"
|
# character is "free to puppet"
|
||||||
string += "\n - %s [%s]" % (char.key, ", ".join(char.permissions.all()))
|
result.append("\n - %s [%s]" % (char.key, ", ".join(char.permissions.all())))
|
||||||
string = ("-" * 68) + "\n" + string + "\n" + ("-" * 68)
|
look_string = ("-" * 68) + "\n" + "".join(result) + "\n" + ("-" * 68)
|
||||||
return string
|
return look_string
|
||||||
|
|
||||||
|
|
||||||
class DefaultGuest(DefaultPlayer):
|
class DefaultGuest(DefaultPlayer):
|
||||||
|
|
@ -910,7 +910,6 @@ class DefaultGuest(DefaultPlayer):
|
||||||
self._send_to_connect_channel("|G%s connected|n" % self.key)
|
self._send_to_connect_channel("|G%s connected|n" % self.key)
|
||||||
self.puppet_object(session, self.db._last_puppet)
|
self.puppet_object(session, self.db._last_puppet)
|
||||||
|
|
||||||
|
|
||||||
def at_server_shutdown(self):
|
def at_server_shutdown(self):
|
||||||
"""
|
"""
|
||||||
We repeat the functionality of `at_disconnect()` here just to
|
We repeat the functionality of `at_disconnect()` here just to
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ The separation works like this:
|
||||||
|
|
||||||
Portal - (AMP client) handles protocols. It contains a list of connected
|
Portal - (AMP client) handles protocols. It contains a list of connected
|
||||||
sessions in a dictionary for identifying the respective player
|
sessions in a dictionary for identifying the respective player
|
||||||
connected. If it looses the AMP connection it will automatically
|
connected. If it loses the AMP connection it will automatically
|
||||||
try to reconnect.
|
try to reconnect.
|
||||||
|
|
||||||
Server - (AMP server) Handles all mud operations. The server holds its own list
|
Server - (AMP server) Handles all mud operations. The server holds its own list
|
||||||
|
|
@ -32,6 +32,7 @@ from twisted.internet import protocol
|
||||||
from twisted.internet.defer import Deferred
|
from twisted.internet.defer import Deferred
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
from evennia.utils.utils import to_str, variable_from_module
|
from evennia.utils.utils import to_str, variable_from_module
|
||||||
|
import zlib # Used in Compressed class
|
||||||
|
|
||||||
DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
|
DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
|
||||||
|
|
||||||
|
|
@ -58,7 +59,6 @@ BATCH_TIMEOUT = 0.5 # how often to poll to empty batch queue, in seconds
|
||||||
_SENDBATCH = defaultdict(list)
|
_SENDBATCH = defaultdict(list)
|
||||||
_MSGBUFFER = defaultdict(list)
|
_MSGBUFFER = defaultdict(list)
|
||||||
|
|
||||||
import zlib
|
|
||||||
|
|
||||||
def get_restart_mode(restart_file):
|
def get_restart_mode(restart_file):
|
||||||
"""
|
"""
|
||||||
|
|
@ -323,9 +323,9 @@ dumps = lambda data: to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL))
|
||||||
loads = lambda data: pickle.loads(to_str(data))
|
loads = lambda data: pickle.loads(to_str(data))
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Core AMP protocol for communication Server <-> Portal
|
# Core AMP protocol for communication Server <-> Portal
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class AMPProtocol(amp.AMP):
|
class AMPProtocol(amp.AMP):
|
||||||
"""
|
"""
|
||||||
|
|
@ -385,7 +385,6 @@ class AMPProtocol(amp.AMP):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Error handling
|
# Error handling
|
||||||
|
|
||||||
def errback(self, e, info):
|
def errback(self, e, info):
|
||||||
|
|
@ -447,7 +446,7 @@ class AMPProtocol(amp.AMP):
|
||||||
Access method called by the Portal and executed on the Portal.
|
Access method called by the Portal and executed on the Portal.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sessid (int): Unique Session id.
|
session (session): Session
|
||||||
kwargs (any, optional): Optional data.
|
kwargs (any, optional): Optional data.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -473,7 +472,6 @@ class AMPProtocol(amp.AMP):
|
||||||
self.factory.portal.sessions.data_out(session, **kwargs)
|
self.factory.portal.sessions.data_out(session, **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def send_MsgServer2Portal(self, session, **kwargs):
|
def send_MsgServer2Portal(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Access method - executed on the Server for sending data
|
Access method - executed on the Server for sending data
|
||||||
|
|
@ -545,7 +543,7 @@ class AMPProtocol(amp.AMP):
|
||||||
"""
|
"""
|
||||||
return self.send_data(AdminPortal2Server, session.sessid, operation=operation, **kwargs)
|
return self.send_data(AdminPortal2Server, session.sessid, operation=operation, **kwargs)
|
||||||
|
|
||||||
# Portal administraton from the Server side
|
# Portal administration from the Server side
|
||||||
|
|
||||||
@AdminServer2Portal.responder
|
@AdminServer2Portal.responder
|
||||||
def portal_receive_adminserver2portal(self, packed_data):
|
def portal_receive_adminserver2portal(self, packed_data):
|
||||||
|
|
@ -562,7 +560,6 @@ class AMPProtocol(amp.AMP):
|
||||||
operation = kwargs.pop("operation")
|
operation = kwargs.pop("operation")
|
||||||
portal_sessionhandler = self.factory.portal.sessions
|
portal_sessionhandler = self.factory.portal.sessions
|
||||||
|
|
||||||
|
|
||||||
if operation == SLOGIN: # server_session_login
|
if operation == SLOGIN: # server_session_login
|
||||||
# a session has authenticated; sync it.
|
# a session has authenticated; sync it.
|
||||||
session = portal_sessionhandler.get(sessid)
|
session = portal_sessionhandler.get(sessid)
|
||||||
|
|
@ -665,4 +662,5 @@ class AMPProtocol(amp.AMP):
|
||||||
module=modulepath,
|
module=modulepath,
|
||||||
function=functionname,
|
function=functionname,
|
||||||
args=dumps(args),
|
args=dumps(args),
|
||||||
kwargs=dumps(kwargs)).addCallback(lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall")
|
kwargs=dumps(kwargs)).addCallback(
|
||||||
|
lambda r: loads(r["result"])).addErrback(self.errback, "FunctionCall")
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ from evennia.server.models import ServerConfig
|
||||||
from evennia.utils import create, logger
|
from evennia.utils import create, logger
|
||||||
|
|
||||||
|
|
||||||
ERROR_NO_SUPERUSER = \
|
ERROR_NO_SUPERUSER = """
|
||||||
"""
|
|
||||||
No superuser exists yet. The superuser is the 'owner' account on
|
No superuser exists yet. The superuser is the 'owner' account on
|
||||||
the Evennia server. Create a new superuser using the command
|
the Evennia server. Create a new superuser using the command
|
||||||
|
|
||||||
|
|
@ -26,16 +25,14 @@ ERROR_NO_SUPERUSER = \
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
LIMBO_DESC = \
|
LIMBO_DESC = _("""
|
||||||
_("""
|
Welcome to your new |wEvennia|n-based game! Visit http://www.evennia.com if you need
|
||||||
Welcome to your new {wEvennia{n-based game! Visit http://www.evennia.com if you need
|
|
||||||
help, want to contribute, report issues or just join the community.
|
help, want to contribute, report issues or just join the community.
|
||||||
As Player #1 you can create a demo/tutorial area with {w@batchcommand tutorial_world.build{n.
|
As Player #1 you can create a demo/tutorial area with |w@batchcommand tutorial_world.build|n.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
WARNING_POSTGRESQL_FIX = \
|
WARNING_POSTGRESQL_FIX = """
|
||||||
"""
|
|
||||||
PostgreSQL-psycopg2 compatibility fix:
|
PostgreSQL-psycopg2 compatibility fix:
|
||||||
The in-game channels {chan1}, {chan2} and {chan3} were created,
|
The in-game channels {chan1}, {chan2} and {chan3} were created,
|
||||||
but the superuser was not yet connected to them. Please use in
|
but the superuser was not yet connected to them. Please use in
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,16 @@ IRC_MAGENTA = "13"
|
||||||
IRC_DGREY = "14"
|
IRC_DGREY = "14"
|
||||||
IRC_GRAY = "15"
|
IRC_GRAY = "15"
|
||||||
|
|
||||||
# test:
|
# obsolete test:
|
||||||
# {rred {ggreen {yyellow {bblue {mmagenta {ccyan {wwhite {xdgrey
|
# {rred {ggreen {yyellow {bblue {mmagenta {ccyan {wwhite {xdgrey
|
||||||
# {Rdred {Gdgreen {Ydyellow {Bdblue {Mdmagenta {Cdcyan {Wlgrey {Xblack
|
# {Rdred {Gdgreen {Ydyellow {Bdblue {Mdmagenta {Cdcyan {Wlgrey {Xblack
|
||||||
# {[rredbg {[ggreenbg {[yyellowbg {[bbluebg {[mmagentabg {[ccyanbg {[wlgreybg {[xblackbg
|
# {[rredbg {[ggreenbg {[yyellowbg {[bbluebg {[mmagentabg {[ccyanbg {[wlgreybg {[xblackbg
|
||||||
|
|
||||||
|
# test:
|
||||||
|
# |rred |ggreen |yyellow |bblue |mmagenta |ccyan |wwhite |xdgrey
|
||||||
|
# |Rdred |Gdgreen |Ydyellow |Bdblue |Mdmagenta |Cdcyan |Wlgrey |Xblack
|
||||||
|
# |[rredbg |[ggreenbg |[yyellowbg |[bbluebg |[mmagentabg |[ccyanbg |[wlgreybg |[xblackbg
|
||||||
|
|
||||||
IRC_COLOR_MAP = dict([
|
IRC_COLOR_MAP = dict([
|
||||||
# obs - {-type colors are deprecated but still used in many places.
|
# obs - {-type colors are deprecated but still used in many places.
|
||||||
(r'{n', IRC_RESET), # reset
|
(r'{n', IRC_RESET), # reset
|
||||||
|
|
@ -117,9 +122,10 @@ IRC_COLOR_MAP = dict([
|
||||||
(r'|[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK) # pure black background
|
(r'|[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK) # pure black background
|
||||||
])
|
])
|
||||||
RE_IRC_COLOR = re.compile(r"|".join([re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL)
|
RE_IRC_COLOR = re.compile(r"|".join([re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL)
|
||||||
RE_MXP = re.compile(r'\{lc(.*?)\{lt(.*?)\{le', re.DOTALL)
|
RE_MXP = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL)
|
||||||
RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL)
|
RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
def sub_irc(ircmatch):
|
def sub_irc(ircmatch):
|
||||||
"""
|
"""
|
||||||
Substitute irc color info. Used by re.sub.
|
Substitute irc color info. Used by re.sub.
|
||||||
|
|
@ -133,6 +139,7 @@ def sub_irc(ircmatch):
|
||||||
"""
|
"""
|
||||||
return IRC_COLOR_MAP.get(ircmatch.group(), "")
|
return IRC_COLOR_MAP.get(ircmatch.group(), "")
|
||||||
|
|
||||||
|
|
||||||
def parse_irc_colors(string):
|
def parse_irc_colors(string):
|
||||||
"""
|
"""
|
||||||
Parse {-type syntax and replace with IRC color markers
|
Parse {-type syntax and replace with IRC color markers
|
||||||
|
|
@ -156,9 +163,10 @@ def parse_irc_colors(string):
|
||||||
|
|
||||||
# IRC bot
|
# IRC bot
|
||||||
|
|
||||||
|
|
||||||
class IRCBot(irc.IRCClient, Session):
|
class IRCBot(irc.IRCClient, Session):
|
||||||
"""
|
"""
|
||||||
An IRC bot that tracks actitivity in a channel as well
|
An IRC bot that tracks activity in a channel as well
|
||||||
as sends text to it when prompted
|
as sends text to it when prompted
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -190,7 +198,7 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
logger.log_info("IRC bot '%s' connected to %s at %s:%s." % (self.nickname, self.channel,
|
logger.log_info("IRC bot '%s' connected to %s at %s:%s." % (self.nickname, self.channel,
|
||||||
self.network, self.port))
|
self.network, self.port))
|
||||||
|
|
||||||
def disconnect(self, reason=None):
|
def disconnect(self, reason=""):
|
||||||
"""
|
"""
|
||||||
Called by sessionhandler to disconnect this protocol.
|
Called by sessionhandler to disconnect this protocol.
|
||||||
|
|
||||||
|
|
@ -198,7 +206,7 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
reason (str): Motivation for the disconnect.
|
reason (str): Motivation for the disconnect.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.sessionhandler.disconnect(self)
|
self.sessionhandler.disconnect(self, reason=reason)
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
|
|
@ -246,14 +254,14 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
self.sendLine("NAMES %s" % self.channel)
|
self.sendLine("NAMES %s" % self.channel)
|
||||||
|
|
||||||
def irc_RPL_NAMREPLY(self, prefix, params):
|
def irc_RPL_NAMREPLY(self, prefix, params):
|
||||||
"Handles IRC NAME request returns (nicklist)"
|
""""Handles IRC NAME request returns (nicklist)"""
|
||||||
channel = params[2].lower()
|
channel = params[2].lower()
|
||||||
if channel != self.channel.lower():
|
if channel != self.channel.lower():
|
||||||
return
|
return
|
||||||
self.nicklist += params[3].split(' ')
|
self.nicklist += params[3].split(' ')
|
||||||
|
|
||||||
def irc_RPL_ENDOFNAMES(self, prefix, params):
|
def irc_RPL_ENDOFNAMES(self, prefix, params):
|
||||||
"Called when the nicklist has finished being returned."
|
"""Called when the nicklist has finished being returned."""
|
||||||
channel = params[1].lower()
|
channel = params[1].lower()
|
||||||
if channel != self.channel.lower():
|
if channel != self.channel.lower():
|
||||||
return
|
return
|
||||||
|
|
@ -271,7 +279,6 @@ class IRCBot(irc.IRCClient, Session):
|
||||||
"""
|
"""
|
||||||
self.data_in(text="", type="ping", user="server", channel=self.channel, timing=time)
|
self.data_in(text="", type="ping", user="server", channel=self.channel, timing=time)
|
||||||
|
|
||||||
|
|
||||||
def data_in(self, text=None, **kwargs):
|
def data_in(self, text=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Data IRC -> Server.
|
Data IRC -> Server.
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,9 @@ if os.name == 'nt':
|
||||||
# For Windows we need to handle pid files manually.
|
# For Windows we need to handle pid files manually.
|
||||||
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'portal.pid')
|
PORTAL_PIDFILE = os.path.join(settings.GAME_DIR, "server", 'portal.pid')
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Evennia Portal settings
|
# Evennia Portal settings
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
VERSION = get_evennia_version()
|
VERSION = get_evennia_version()
|
||||||
|
|
||||||
|
|
@ -76,6 +76,8 @@ AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE
|
||||||
# Maintenance function - this is called repeatedly by the portal.
|
# Maintenance function - this is called repeatedly by the portal.
|
||||||
|
|
||||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||||
|
|
||||||
|
|
||||||
def _portal_maintenance():
|
def _portal_maintenance():
|
||||||
"""
|
"""
|
||||||
The maintenance function handles repeated checks and updates that
|
The maintenance function handles repeated checks and updates that
|
||||||
|
|
@ -97,9 +99,9 @@ if _IDLE_TIMEOUT > 0:
|
||||||
_maintenance_task.start(60) # called every minute
|
_maintenance_task.start(60) # called every minute
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Portal Service object
|
# Portal Service object
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
class Portal(object):
|
class Portal(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -180,11 +182,11 @@ class Portal(object):
|
||||||
self.shutdown_complete = True
|
self.shutdown_complete = True
|
||||||
reactor.callLater(0, reactor.stop)
|
reactor.callLater(0, reactor.stop)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Start the Portal proxy server and add all active services
|
# Start the Portal proxy server and add all active services
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
# twistd requires us to define the variable 'application' so it knows
|
# twistd requires us to define the variable 'application' so it knows
|
||||||
# what to execute from.
|
# what to execute from.
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,11 @@ _CONNECTION_QUEUE = deque()
|
||||||
|
|
||||||
DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
|
DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Portal-SessionHandler class
|
# Portal-SessionHandler class
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class PortalSessionHandler(SessionHandler):
|
class PortalSessionHandler(SessionHandler):
|
||||||
"""
|
"""
|
||||||
This object holds the sessions connected to the portal at any time.
|
This object holds the sessions connected to the portal at any time.
|
||||||
|
|
@ -176,8 +178,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
del self[session.sessid]
|
del self[session.sessid]
|
||||||
|
|
||||||
# Tell the Server to disconnect its version of the Session as well.
|
# Tell the Server to disconnect its version of the Session as well.
|
||||||
self.portal.amp_protocol.send_AdminPortal2Server(session,
|
self.portal.amp_protocol.send_AdminPortal2Server(session, operation=PDISCONN)
|
||||||
operation=PDISCONN)
|
|
||||||
|
|
||||||
def disconnect_all(self):
|
def disconnect_all(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -233,8 +234,8 @@ class PortalSessionHandler(SessionHandler):
|
||||||
Called by server to force a disconnect by sessid.
|
Called by server to force a disconnect by sessid.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sessid (int): Session id to disconnect.
|
session (portalsession): Session to disconnect.
|
||||||
reason (str, optional): Motivation for disconect.
|
reason (str, optional): Motivation for disconnect.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if session:
|
if session:
|
||||||
|
|
@ -352,8 +353,8 @@ class PortalSessionHandler(SessionHandler):
|
||||||
Data is serialized before passed on.
|
Data is serialized before passed on.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
# from evennia.server.profiling.timetrace import timetrace # DEBUG
|
||||||
#text = timetrace(text, "portalsessionhandler.data_in")
|
# text = timetrace(text, "portalsessionhandler.data_in") # DEBUG
|
||||||
try:
|
try:
|
||||||
text = kwargs['text']
|
text = kwargs['text']
|
||||||
if (_MAX_CHAR_LIMIT > 0) and len(text) > _MAX_CHAR_LIMIT:
|
if (_MAX_CHAR_LIMIT > 0) and len(text) > _MAX_CHAR_LIMIT:
|
||||||
|
|
@ -365,12 +366,12 @@ class PortalSessionHandler(SessionHandler):
|
||||||
pass
|
pass
|
||||||
if session:
|
if session:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if self.command_counter > _MAX_COMMAND_RATE:
|
if self.command_counter > _MAX_COMMAND_RATE > 0:
|
||||||
# data throttle (anti DoS measure)
|
# data throttle (anti DoS measure)
|
||||||
dT = now - self.command_counter_reset
|
delta_time = now - self.command_counter_reset
|
||||||
self.command_counter = 0
|
self.command_counter = 0
|
||||||
self.command_counter_reset = now
|
self.command_counter_reset = now
|
||||||
self.command_overflow = dT < 1.0
|
self.command_overflow = delta_time < 1.0
|
||||||
if self.command_overflow:
|
if self.command_overflow:
|
||||||
reactor.callLater(1.0, self.data_in, None)
|
reactor.callLater(1.0, self.data_in, None)
|
||||||
if self.command_overflow:
|
if self.command_overflow:
|
||||||
|
|
@ -405,8 +406,8 @@ class PortalSessionHandler(SessionHandler):
|
||||||
method exixts, it sends the data to a method send_default.
|
method exixts, it sends the data to a method send_default.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
# from evennia.server.profiling.timetrace import timetrace # DEBUG
|
||||||
#text = timetrace(text, "portalsessionhandler.data_out")
|
# text = timetrace(text, "portalsessionhandler.data_out") # DEBUG
|
||||||
|
|
||||||
# distribute outgoing data to the correct session methods.
|
# distribute outgoing data to the correct session methods.
|
||||||
if session:
|
if session:
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ from evennia.players.models import PlayerDB
|
||||||
from evennia.utils import ansi
|
from evennia.utils import ansi
|
||||||
from evennia.utils.utils import to_str
|
from evennia.utils.utils import to_str
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\|n$")
|
||||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||||
_GAME_DIR = settings.GAME_DIR
|
_GAME_DIR = settings.GAME_DIR
|
||||||
|
|
||||||
|
|
@ -206,7 +206,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for line in string.split('\n'):
|
for line in string.split('\n'):
|
||||||
#this is the telnet-specific method for sending
|
# the telnet-specific method for sending
|
||||||
self.terminal.write(line)
|
self.terminal.write(line)
|
||||||
self.terminal.nextLine()
|
self.terminal.nextLine()
|
||||||
|
|
||||||
|
|
@ -255,7 +255,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
Note that it must be actively turned back on again!
|
Note that it must be actively turned back on again!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#print "telnet.send_text", args,kwargs
|
# print "telnet.send_text", args,kwargs # DEBUG
|
||||||
text = args[0] if args else ""
|
text = args[0] if args else ""
|
||||||
if text is None:
|
if text is None:
|
||||||
return
|
return
|
||||||
|
|
@ -268,7 +268,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
useansi = options.get("ansi", flags.get('ANSI', True))
|
useansi = options.get("ansi", flags.get('ANSI', True))
|
||||||
raw = options.get("raw", flags.get("RAW", False))
|
raw = options.get("raw", flags.get("RAW", False))
|
||||||
nocolor = options.get("nocolor", flags.get("NOCOLOR") or not (xterm256 or useansi))
|
nocolor = options.get("nocolor", flags.get("NOCOLOR") or not (xterm256 or useansi))
|
||||||
#echo = options.get("echo", None)
|
# echo = options.get("echo", None) # DEBUG
|
||||||
screenreader = options.get("screenreader", flags.get("SCREENREADER", False))
|
screenreader = options.get("screenreader", flags.get("SCREENREADER", False))
|
||||||
|
|
||||||
if screenreader:
|
if screenreader:
|
||||||
|
|
@ -283,7 +283,8 @@ class SshProtocol(Manhole, session.Session):
|
||||||
else:
|
else:
|
||||||
# we need to make sure to kill the color at the end in order
|
# we need to make sure to kill the color at the end in order
|
||||||
# to match the webclient output.
|
# to match the webclient output.
|
||||||
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nocolor, xterm256=xterm256, mxp=False)
|
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + ("|n" if text[-1] != "|" else "||n"),
|
||||||
|
strip_ansi=nocolor, xterm256=xterm256, mxp=False)
|
||||||
self.sendLine(linetosend)
|
self.sendLine(linetosend)
|
||||||
|
|
||||||
def send_prompt(self, *args, **kwargs):
|
def send_prompt(self, *args, **kwargs):
|
||||||
|
|
@ -453,11 +454,10 @@ def makeFactory(configdict):
|
||||||
factory.publicKeys = {'ssh-rsa': publicKey}
|
factory.publicKeys = {'ssh-rsa': publicKey}
|
||||||
factory.privateKeys = {'ssh-rsa': privateKey}
|
factory.privateKeys = {'ssh-rsa': privateKey}
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print ( "getKeyPair error: {err}\n WARNING: Evennia could not " \
|
print("getKeyPair error: {err}\n WARNING: Evennia could not "
|
||||||
"auto-generate SSH keypair. Using conch default keys instead.\n" \
|
"auto-generate SSH keypair. Using conch default keys instead.\n"
|
||||||
"If this error persists, create {pub} and " \
|
"If this error persists, create {pub} and "
|
||||||
"{priv} yourself using third-party tools.".format(
|
"{priv} yourself using third-party tools.".format(err=err, pub=pubkeyfile, priv=privkeyfile))
|
||||||
err=err, pub=pubkeyfile, priv=privkeyfile))
|
|
||||||
|
|
||||||
factory.services = factory.services.copy()
|
factory.services = factory.services.copy()
|
||||||
factory.services['ssh-userauth'] = ExtraInfoAuthServer
|
factory.services['ssh-userauth'] = ExtraInfoAuthServer
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ class SSLProtocol(TelnetProtocol):
|
||||||
super(SSLProtocol, self).__init__(*args, **kwargs)
|
super(SSLProtocol, self).__init__(*args, **kwargs)
|
||||||
self.protocol_name = "ssl"
|
self.protocol_name = "ssl"
|
||||||
|
|
||||||
|
|
||||||
def verify_SSL_key_and_cert(keyfile, certfile):
|
def verify_SSL_key_and_cert(keyfile, certfile):
|
||||||
"""
|
"""
|
||||||
This function looks for RSA key and certificate in the current
|
This function looks for RSA key and certificate in the current
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ from evennia.server.portal.mxp import Mxp, mxp_parse
|
||||||
from evennia.utils import ansi
|
from evennia.utils import ansi
|
||||||
from evennia.utils.utils import to_str
|
from evennia.utils.utils import to_str
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\|n$")
|
||||||
_RE_LEND = re.compile(r"\n$|\r$|\r\n$|\r\x00$|", re.MULTILINE)
|
_RE_LEND = re.compile(r"\n$|\r$|\r\n$|\r\x00$|", re.MULTILINE)
|
||||||
_RE_LINEBREAK = re.compile(r"\n\r|\r\n|\n|\r", re.DOTALL + re.MULTILINE)
|
_RE_LINEBREAK = re.compile(r"\n\r|\r\n|\n|\r", re.DOTALL + re.MULTILINE)
|
||||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||||
_IDLE_COMMAND = settings.IDLE_COMMAND + "\n"
|
_IDLE_COMMAND = settings.IDLE_COMMAND + "\n"
|
||||||
|
|
||||||
|
|
||||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
"""
|
"""
|
||||||
Each player connecting over telnet (ie using most traditional mud
|
Each player connecting over telnet (ie using most traditional mud
|
||||||
|
|
@ -79,7 +80,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.toggle_nop_keepalive()
|
self.toggle_nop_keepalive()
|
||||||
|
|
||||||
def _send_nop_keepalive(self):
|
def _send_nop_keepalive(self):
|
||||||
"Send NOP keepalive unless flag is set"
|
"""Send NOP keepalive unless flag is set"""
|
||||||
if self.protocol_flags.get("NOPKEEPALIVE"):
|
if self.protocol_flags.get("NOPKEEPALIVE"):
|
||||||
self._write(IAC + NOP)
|
self._write(IAC + NOP)
|
||||||
|
|
||||||
|
|
@ -140,7 +141,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
enable (bool): If this option should be enabled.
|
enable (bool): If this option should be enabled.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return (option == MCCP or option==ECHO)
|
return option == MCCP or option == ECHO
|
||||||
|
|
||||||
def disableLocal(self, option):
|
def disableLocal(self, option):
|
||||||
"""
|
"""
|
||||||
|
|
@ -178,7 +179,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
directly.
|
directly.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
string (str): Incoming data.
|
data (str): Incoming data.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
|
|
@ -188,7 +189,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
# legacy clients. There should never be a reason to send a
|
# legacy clients. There should never be a reason to send a
|
||||||
# lone NULL character so this seems to be a safe thing to
|
# lone NULL character so this seems to be a safe thing to
|
||||||
# support for backwards compatibility. It also stops the
|
# support for backwards compatibility. It also stops the
|
||||||
# NULL from continously popping up as an unknown command.
|
# NULL from continuously popping up as an unknown command.
|
||||||
data = [_IDLE_COMMAND]
|
data = [_IDLE_COMMAND]
|
||||||
else:
|
else:
|
||||||
data = _RE_LINEBREAK.split(data)
|
data = _RE_LINEBREAK.split(data)
|
||||||
|
|
@ -205,7 +206,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.data_in(text=dat + "\n")
|
self.data_in(text=dat + "\n")
|
||||||
|
|
||||||
def _write(self, data):
|
def _write(self, data):
|
||||||
"hook overloading the one used in plain telnet"
|
"""hook overloading the one used in plain telnet"""
|
||||||
data = data.replace('\n', '\r\n').replace('\r\r\n', '\r\n')
|
data = data.replace('\n', '\r\n').replace('\r\r\n', '\r\n')
|
||||||
super(TelnetProtocol, self)._write(mccp_compress(self, data))
|
super(TelnetProtocol, self)._write(mccp_compress(self, data))
|
||||||
|
|
||||||
|
|
@ -222,19 +223,18 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
line = line.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
line = line.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
||||||
return self.transport.write(mccp_compress(self, line))
|
return self.transport.write(mccp_compress(self, line))
|
||||||
|
|
||||||
|
|
||||||
# Session hooks
|
# Session hooks
|
||||||
|
|
||||||
def disconnect(self, reason=None):
|
def disconnect(self, reason=""):
|
||||||
"""
|
"""
|
||||||
generic hook for the engine to call in order to
|
generic hook for the engine to call in order to
|
||||||
disconnect this protocol.
|
disconnect this protocol.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
reason (str): Reason for disconnecting.
|
reason (str, optional): Reason for disconnecting.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.data_out(text=((reason or "",), {}))
|
self.data_out(text=((reason,), {}))
|
||||||
self.connectionLost(reason)
|
self.connectionLost(reason)
|
||||||
|
|
||||||
def data_in(self, **kwargs):
|
def data_in(self, **kwargs):
|
||||||
|
|
@ -245,8 +245,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
kwargs (any): Options from the protocol.
|
kwargs (any): Options from the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
# from evennia.server.profiling.timetrace import timetrace # DEBUG
|
||||||
#text = timetrace(text, "telnet.data_in")
|
# text = timetrace(text, "telnet.data_in") # DEBUG
|
||||||
|
|
||||||
self.sessionhandler.data_in(self, **kwargs)
|
self.sessionhandler.data_in(self, **kwargs)
|
||||||
|
|
||||||
|
|
@ -306,9 +306,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
|
|
||||||
if options.get("send_prompt"):
|
if options.get("send_prompt"):
|
||||||
# send a prompt instead.
|
# send a prompt instead.
|
||||||
|
prompt = text
|
||||||
if not raw:
|
if not raw:
|
||||||
# processing
|
# processing
|
||||||
prompt = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nocolor, xterm256=xterm256)
|
prompt = ansi.parse_ansi(_RE_N.sub("", prompt) + ("|n" if prompt[-1] != "|" else "||n"),
|
||||||
|
strip_ansi=nocolor, xterm256=xterm256)
|
||||||
if mxp:
|
if mxp:
|
||||||
prompt = mxp_parse(prompt)
|
prompt = mxp_parse(prompt)
|
||||||
prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
||||||
|
|
@ -335,7 +337,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
else:
|
else:
|
||||||
# we need to make sure to kill the color at the end in order
|
# we need to make sure to kill the color at the end in order
|
||||||
# to match the webclient output.
|
# to match the webclient output.
|
||||||
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nocolor, xterm256=xterm256, mxp=mxp)
|
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + ("|n" if text[-1] != "|" else "||n"),
|
||||||
|
strip_ansi=nocolor, xterm256=xterm256, mxp=mxp)
|
||||||
if mxp:
|
if mxp:
|
||||||
linetosend = mxp_parse(linetosend)
|
linetosend = mxp_parse(linetosend)
|
||||||
self.sendLine(linetosend)
|
self.sendLine(linetosend)
|
||||||
|
|
@ -348,7 +351,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
kwargs["options"].update({"send_prompt": True})
|
kwargs["options"].update({"send_prompt": True})
|
||||||
self.send_text(*args, **kwargs)
|
self.send_text(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def send_default(self, cmdname, *args, **kwargs):
|
def send_default(self, cmdname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Send other oob data
|
Send other oob data
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,13 @@ force_str = lambda inp: to_str(inp, force_string=True)
|
||||||
|
|
||||||
# pre-compiled regexes
|
# pre-compiled regexes
|
||||||
# returns 2-tuple
|
# returns 2-tuple
|
||||||
msdp_regex_table = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
msdp_regex_table = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s"
|
||||||
|
% (MSDP_VAR, MSDP_VAL,
|
||||||
MSDP_TABLE_OPEN,
|
MSDP_TABLE_OPEN,
|
||||||
MSDP_TABLE_CLOSE))
|
MSDP_TABLE_CLOSE))
|
||||||
# returns 2-tuple
|
# returns 2-tuple
|
||||||
msdp_regex_array = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
msdp_regex_array = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s"
|
||||||
|
% (MSDP_VAR, MSDP_VAL,
|
||||||
MSDP_ARRAY_OPEN,
|
MSDP_ARRAY_OPEN,
|
||||||
MSDP_ARRAY_CLOSE))
|
MSDP_ARRAY_CLOSE))
|
||||||
msdp_regex_var = re.compile(r"%s" % MSDP_VAR)
|
msdp_regex_var = re.compile(r"%s" % MSDP_VAR)
|
||||||
|
|
@ -67,7 +69,8 @@ EVENNIA_TO_GMCP = {"client_options": "Core.Supports.Get",
|
||||||
"repeat": "Char.Repeat.Update",
|
"repeat": "Char.Repeat.Update",
|
||||||
"monitor": "Char.Monitor.Update"}
|
"monitor": "Char.Monitor.Update"}
|
||||||
|
|
||||||
# Msdp object handler
|
|
||||||
|
# MSDP/GMCP communication handler
|
||||||
|
|
||||||
class TelnetOOB(object):
|
class TelnetOOB(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -100,7 +103,7 @@ class TelnetOOB(object):
|
||||||
Client reports No msdp supported or wanted.
|
Client reports No msdp supported or wanted.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options (Option): Not used.
|
option (Option): Not used.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# no msdp, check GMCP
|
# no msdp, check GMCP
|
||||||
|
|
@ -173,7 +176,7 @@ class TelnetOOB(object):
|
||||||
if not (args or kwargs):
|
if not (args or kwargs):
|
||||||
return msdp_cmdname
|
return msdp_cmdname
|
||||||
|
|
||||||
#print "encode_msdp in:", cmdname, args, kwargs
|
# print("encode_msdp in:", cmdname, args, kwargs) # DEBUG
|
||||||
|
|
||||||
msdp_args = ''
|
msdp_args = ''
|
||||||
if args:
|
if args:
|
||||||
|
|
@ -186,11 +189,10 @@ class TelnetOOB(object):
|
||||||
"{msdp_array_close}".format(
|
"{msdp_array_close}".format(
|
||||||
msdp_array_open=MSDP_ARRAY_OPEN,
|
msdp_array_open=MSDP_ARRAY_OPEN,
|
||||||
msdp_array_close=MSDP_ARRAY_CLOSE,
|
msdp_array_close=MSDP_ARRAY_CLOSE,
|
||||||
msdp_args= "".join("%s%s" % (
|
msdp_args="".join("%s%s"
|
||||||
MSDP_VAL, json.dumps(val))
|
% (MSDP_VAL, json.dumps(val))
|
||||||
for val in args))
|
for val in args))
|
||||||
|
|
||||||
|
|
||||||
msdp_kwargs = ""
|
msdp_kwargs = ""
|
||||||
if kwargs:
|
if kwargs:
|
||||||
msdp_kwargs = msdp_cmdname
|
msdp_kwargs = msdp_cmdname
|
||||||
|
|
@ -199,13 +201,14 @@ class TelnetOOB(object):
|
||||||
"{msdp_table_close}".format(
|
"{msdp_table_close}".format(
|
||||||
msdp_table_open=MSDP_TABLE_OPEN,
|
msdp_table_open=MSDP_TABLE_OPEN,
|
||||||
msdp_table_close=MSDP_TABLE_CLOSE,
|
msdp_table_close=MSDP_TABLE_CLOSE,
|
||||||
msdp_kwargs = "".join("%s%s%s%s" % (
|
msdp_kwargs="".join("%s%s%s%s"
|
||||||
MSDP_VAR, key, MSDP_VAL, json.dumps(val))
|
% (MSDP_VAR, key, MSDP_VAL,
|
||||||
|
json.dumps(val))
|
||||||
for key, val in kwargs.iteritems()))
|
for key, val in kwargs.iteritems()))
|
||||||
|
|
||||||
msdp_string = msdp_args + msdp_kwargs
|
msdp_string = msdp_args + msdp_kwargs
|
||||||
|
|
||||||
#print "msdp_string:", msdp_string
|
# print("msdp_string:", msdp_string) # DEBUG
|
||||||
return msdp_string
|
return msdp_string
|
||||||
|
|
||||||
def encode_gmcp(self, cmdname, *args, **kwargs):
|
def encode_gmcp(self, cmdname, *args, **kwargs):
|
||||||
|
|
@ -241,7 +244,7 @@ class TelnetOOB(object):
|
||||||
else: # only kwargs
|
else: # only kwargs
|
||||||
gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs))
|
gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs))
|
||||||
|
|
||||||
#print "gmcp string", gmcp_string
|
# print("gmcp string", gmcp_string) # DEBUG
|
||||||
return gmcp_string
|
return gmcp_string
|
||||||
|
|
||||||
def decode_msdp(self, data):
|
def decode_msdp(self, data):
|
||||||
|
|
@ -271,7 +274,7 @@ class TelnetOOB(object):
|
||||||
if hasattr(data, "__iter__"):
|
if hasattr(data, "__iter__"):
|
||||||
data = "".join(data)
|
data = "".join(data)
|
||||||
|
|
||||||
#print "decode_msdp in:", data
|
# print("decode_msdp in:", data) # DEBUG
|
||||||
|
|
||||||
tables = {}
|
tables = {}
|
||||||
arrays = {}
|
arrays = {}
|
||||||
|
|
@ -279,7 +282,7 @@ class TelnetOOB(object):
|
||||||
|
|
||||||
# decode tables
|
# decode tables
|
||||||
for key, table in msdp_regex_table.findall(data):
|
for key, table in msdp_regex_table.findall(data):
|
||||||
tables[key] = {} if not key in tables else tables[key]
|
tables[key] = {} if key not in tables else tables[key]
|
||||||
for varval in msdp_regex_var.split(table)[1:]:
|
for varval in msdp_regex_var.split(table)[1:]:
|
||||||
var, val = msdp_regex_val.split(varval, 1)
|
var, val = msdp_regex_val.split(varval, 1)
|
||||||
if var:
|
if var:
|
||||||
|
|
@ -288,7 +291,7 @@ class TelnetOOB(object):
|
||||||
# decode arrays from all that was not a table
|
# decode arrays from all that was not a table
|
||||||
data_no_tables = msdp_regex_table.sub("", data)
|
data_no_tables = msdp_regex_table.sub("", data)
|
||||||
for key, array in msdp_regex_array.findall(data_no_tables):
|
for key, array in msdp_regex_array.findall(data_no_tables):
|
||||||
arrays[key] = [] if not key in arrays else arrays[key]
|
arrays[key] = [] if key not in arrays else arrays[key]
|
||||||
parts = msdp_regex_val.split(array)
|
parts = msdp_regex_val.split(array)
|
||||||
if len(parts) == 2:
|
if len(parts) == 2:
|
||||||
arrays[key].append(parts[1])
|
arrays[key].append(parts[1])
|
||||||
|
|
@ -326,10 +329,9 @@ class TelnetOOB(object):
|
||||||
for key, var in variables.iteritems():
|
for key, var in variables.iteritems():
|
||||||
cmds[key] = [[var], {}]
|
cmds[key] = [[var], {}]
|
||||||
|
|
||||||
#print "msdp data in:", cmds
|
# print("msdp data in:", cmds) # DEBUG
|
||||||
self.protocol.data_in(**cmds)
|
self.protocol.data_in(**cmds)
|
||||||
|
|
||||||
|
|
||||||
def decode_gmcp(self, data):
|
def decode_gmcp(self, data):
|
||||||
"""
|
"""
|
||||||
Decodes incoming GMCP data on the form 'varname <structure>'.
|
Decodes incoming GMCP data on the form 'varname <structure>'.
|
||||||
|
|
@ -353,7 +355,7 @@ class TelnetOOB(object):
|
||||||
if hasattr(data, "__iter__"):
|
if hasattr(data, "__iter__"):
|
||||||
data = "".join(data)
|
data = "".join(data)
|
||||||
|
|
||||||
#print "decode_gmcp in:", data
|
# print("decode_gmcp in:", data) # DEBUG
|
||||||
if data:
|
if data:
|
||||||
try:
|
try:
|
||||||
cmdname, structure = data.split(None, 1)
|
cmdname, structure = data.split(None, 1)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ MTTS = [(128, 'PROXY'),
|
||||||
(2, 'VT100'),
|
(2, 'VT100'),
|
||||||
(1, 'ANSI')]
|
(1, 'ANSI')]
|
||||||
|
|
||||||
|
|
||||||
class Ttype(object):
|
class Ttype(object):
|
||||||
"""
|
"""
|
||||||
Handles ttype negotiations. Called and initiated by the
|
Handles ttype negotiations. Called and initiated by the
|
||||||
|
|
@ -104,7 +105,6 @@ class Ttype(object):
|
||||||
# use name to identify support for xterm256. Many of these
|
# use name to identify support for xterm256. Many of these
|
||||||
# only support after a certain version, but all support
|
# only support after a certain version, but all support
|
||||||
# it since at least 4 years. We assume recent client here for now.
|
# it since at least 4 years. We assume recent client here for now.
|
||||||
xterm256 = False
|
|
||||||
cupper = clientname.upper()
|
cupper = clientname.upper()
|
||||||
if cupper.startswith("MUDLET"):
|
if cupper.startswith("MUDLET"):
|
||||||
# supports xterm256 stably since 1.1 (2010?)
|
# supports xterm256 stably since 1.1 (2010?)
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,9 @@ from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
# Handlers for Session.db/ndb operation
|
# Handlers for Session.db/ndb operation
|
||||||
|
|
||||||
|
|
||||||
class NDbHolder(object):
|
class NDbHolder(object):
|
||||||
"Holder for allowing property access of attributes"
|
"""Holder for allowing property access of attributes"""
|
||||||
def __init__(self, obj, name, manager_name='attributes'):
|
def __init__(self, obj, name, manager_name='attributes'):
|
||||||
_SA(self, name, _GA(obj, manager_name))
|
_SA(self, name, _GA(obj, manager_name))
|
||||||
_SA(self, 'name', name)
|
_SA(self, 'name', name)
|
||||||
|
|
@ -145,9 +146,9 @@ class NAttributeHandler(object):
|
||||||
return [key for key in self._store if not key.startswith("_")]
|
return [key for key in self._store if not key.startswith("_")]
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Server Session
|
# Server Session
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class ServerSession(Session):
|
class ServerSession(Session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -160,7 +161,7 @@ class ServerSession(Session):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"Initiate to avoid AttributeErrors down the line"
|
"""Initiate to avoid AttributeErrors down the line"""
|
||||||
self.puppet = None
|
self.puppet = None
|
||||||
self.player = None
|
self.player = None
|
||||||
self.cmdset_storage_string = ""
|
self.cmdset_storage_string = ""
|
||||||
|
|
@ -264,7 +265,6 @@ class ServerSession(Session):
|
||||||
MONITOR_HANDLER.remove(player, "_saved_webclient_options",
|
MONITOR_HANDLER.remove(player, "_saved_webclient_options",
|
||||||
self.sessid)
|
self.sessid)
|
||||||
|
|
||||||
|
|
||||||
def get_player(self):
|
def get_player(self):
|
||||||
"""
|
"""
|
||||||
Get the player associated with this session
|
Get the player associated with this session
|
||||||
|
|
@ -364,7 +364,6 @@ class ServerSession(Session):
|
||||||
self.protocol_flags.update(kwargs)
|
self.protocol_flags.update(kwargs)
|
||||||
self.sessionhandler.session_portal_sync(self)
|
self.sessionhandler.session_portal_sync(self)
|
||||||
|
|
||||||
|
|
||||||
def data_out(self, **kwargs):
|
def data_out(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Sending data from Evennia->Client
|
Sending data from Evennia->Client
|
||||||
|
|
@ -437,7 +436,7 @@ class ServerSession(Session):
|
||||||
self.sessionhandler.data_in(self, **kwargs)
|
self.sessionhandler.data_in(self, **kwargs)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"Handle session comparisons"
|
"""Handle session comparisons"""
|
||||||
try:
|
try:
|
||||||
return self.address == other.address
|
return self.address == other.address
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -462,11 +461,9 @@ class ServerSession(Session):
|
||||||
return "%s%s@%s" % (self.uname, symbol, address)
|
return "%s%s@%s" % (self.uname, symbol, address)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
"Unicode representation"
|
"""Unicode representation"""
|
||||||
return u"%s" % str(self)
|
return u"%s" % str(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Dummy API hooks for use during non-loggedin operation
|
# Dummy API hooks for use during non-loggedin operation
|
||||||
|
|
||||||
def at_cmdset_get(self, **kwargs):
|
def at_cmdset_get(self, **kwargs):
|
||||||
|
|
@ -518,7 +515,7 @@ class ServerSession(Session):
|
||||||
|
|
||||||
# @ndb.deleter
|
# @ndb.deleter
|
||||||
def ndb_del(self):
|
def ndb_del(self):
|
||||||
"Stop accidental deletion."
|
"""Stop accidental deletion."""
|
||||||
raise Exception("Cannot delete the ndb object!")
|
raise Exception("Cannot delete the ndb object!")
|
||||||
ndb = property(ndb_get, ndb_set, ndb_del)
|
ndb = property(ndb_get, ndb_set, ndb_del)
|
||||||
db = property(ndb_get, ndb_set, ndb_del)
|
db = property(ndb_get, ndb_set, ndb_del)
|
||||||
|
|
@ -526,5 +523,5 @@ class ServerSession(Session):
|
||||||
# Mock access method for the session (there is no lock info
|
# Mock access method for the session (there is no lock info
|
||||||
# at this stage, so we just present a uniform API)
|
# at this stage, so we just present a uniform API)
|
||||||
def access(self, *args, **kwargs):
|
def access(self, *args, **kwargs):
|
||||||
"Dummy method to mimic the logged-in API."
|
"""Dummy method to mimic the logged-in API."""
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ from twisted.web.wsgi import WSGIResource
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.handlers.wsgi import WSGIHandler
|
from django.core.handlers.wsgi import WSGIHandler
|
||||||
|
|
||||||
|
from evennia.utils import logger
|
||||||
|
|
||||||
_UPSTREAM_IPS = settings.UPSTREAM_IPS
|
_UPSTREAM_IPS = settings.UPSTREAM_IPS
|
||||||
_DEBUG = settings.DEBUG
|
_DEBUG = settings.DEBUG
|
||||||
|
|
||||||
|
|
@ -70,6 +72,7 @@ class EvenniaReverseProxyResource(ReverseProxyResource):
|
||||||
resource (EvenniaReverseProxyResource): A proxy resource.
|
resource (EvenniaReverseProxyResource): A proxy resource.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
request.notifyFinish().addErrback(lambda f: logger.log_trace("%s\nCaught errback in webserver.py:75." % f))
|
||||||
return EvenniaReverseProxyResource(
|
return EvenniaReverseProxyResource(
|
||||||
self.host, self.port, self.path + '/' + urlquote(path, safe=""),
|
self.host, self.port, self.path + '/' + urlquote(path, safe=""),
|
||||||
self.reactor)
|
self.reactor)
|
||||||
|
|
@ -98,6 +101,8 @@ class EvenniaReverseProxyResource(ReverseProxyResource):
|
||||||
request.getAllHeaders(), request.content.read(), request)
|
request.getAllHeaders(), request.content.read(), request)
|
||||||
clientFactory.noisy = False
|
clientFactory.noisy = False
|
||||||
self.reactor.connectTCP(self.host, self.port, clientFactory)
|
self.reactor.connectTCP(self.host, self.port, clientFactory)
|
||||||
|
# don't trigger traceback if connection is lost before request finish.
|
||||||
|
request.notifyFinish().addErrback(lambda f: f.cancel())
|
||||||
return NOT_DONE_YET
|
return NOT_DONE_YET
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,12 @@ from evennia.utils.utils import lazy_property, to_str, make_iter
|
||||||
|
|
||||||
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Attributes
|
# Attributes
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class Attribute(SharedMemoryModel):
|
class Attribute(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
|
|
@ -110,12 +111,15 @@ class Attribute(SharedMemoryModel):
|
||||||
|
|
||||||
def __lock_storage_get(self):
|
def __lock_storage_get(self):
|
||||||
return self.db_lock_storage
|
return self.db_lock_storage
|
||||||
|
|
||||||
def __lock_storage_set(self, value):
|
def __lock_storage_set(self, value):
|
||||||
self.db_lock_storage = value
|
self.db_lock_storage = value
|
||||||
self.save(update_fields=["db_lock_storage"])
|
self.save(update_fields=["db_lock_storage"])
|
||||||
|
|
||||||
def __lock_storage_del(self):
|
def __lock_storage_del(self):
|
||||||
self.db_lock_storage = ""
|
self.db_lock_storage = ""
|
||||||
self.save(update_fields=["db_lock_storage"])
|
self.save(update_fields=["db_lock_storage"])
|
||||||
|
|
||||||
lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del)
|
lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del)
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
|
|
@ -144,12 +148,12 @@ class Attribute(SharedMemoryModel):
|
||||||
see self.__value_get.
|
see self.__value_get.
|
||||||
"""
|
"""
|
||||||
self.db_value = to_pickle(new_value)
|
self.db_value = to_pickle(new_value)
|
||||||
#print "value_set, self.db_value:", repr(self.db_value)
|
# print("value_set, self.db_value:", repr(self.db_value)) # DEBUG
|
||||||
self.save(update_fields=["db_value"])
|
self.save(update_fields=["db_value"])
|
||||||
|
|
||||||
# @value.deleter
|
# @value.deleter
|
||||||
def __value_del(self):
|
def __value_del(self):
|
||||||
"Deleter. Allows for del attr.value. This removes the entire attribute."
|
"""Deleter. Allows for del attr.value. This removes the entire attribute."""
|
||||||
self.delete()
|
self.delete()
|
||||||
value = property(__value_get, __value_set, __value_del)
|
value = property(__value_get, __value_set, __value_del)
|
||||||
|
|
||||||
|
|
@ -202,7 +206,7 @@ class AttributeHandler(object):
|
||||||
_attrtype = None
|
_attrtype = None
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
"Initialize handler."
|
"""Initialize handler."""
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self._objid = obj.id
|
self._objid = obj.id
|
||||||
self._model = to_str(obj.__dbclass__.__name__.lower())
|
self._model = to_str(obj.__dbclass__.__name__.lower())
|
||||||
|
|
@ -213,7 +217,7 @@ class AttributeHandler(object):
|
||||||
self._cache_complete = False
|
self._cache_complete = False
|
||||||
|
|
||||||
def _fullcache(self):
|
def _fullcache(self):
|
||||||
"Cache all attributes of this object"
|
"""Cache all attributes of this object"""
|
||||||
query = {"%s__id" % self._model: self._objid,
|
query = {"%s__id" % self._model: self._objid,
|
||||||
"attribute__db_model": self._model,
|
"attribute__db_model": self._model,
|
||||||
"attribute__db_attrtype": self._attrtype}
|
"attribute__db_attrtype": self._attrtype}
|
||||||
|
|
@ -290,15 +294,15 @@ class AttributeHandler(object):
|
||||||
# for this category before
|
# for this category before
|
||||||
catkey = "-%s" % category
|
catkey = "-%s" % category
|
||||||
if _TYPECLASS_AGGRESSIVE_CACHE and catkey in self._catcache:
|
if _TYPECLASS_AGGRESSIVE_CACHE and catkey in self._catcache:
|
||||||
return [attr for key, attr in self._cache.items() if key.endswith(catkey)]
|
return [attr for key, attr in self._cache.items() if key.endswith(catkey) and attr]
|
||||||
else:
|
else:
|
||||||
# we have to query to make this category up-date in the cache
|
# we have to query to make this category up-date in the cache
|
||||||
query = {"%s__id" % self._model: self._objid,
|
query = {"%s__id" % self._model: self._objid,
|
||||||
"attribute__db_model": self._model,
|
"attribute__db_model": self._model,
|
||||||
"attribute__db_attrtype": self._attrtype,
|
"attribute__db_attrtype": self._attrtype,
|
||||||
"attribute__db_category__iexact": category.lower() if category else None}
|
"attribute__db_category__iexact": category.lower() if category else None}
|
||||||
attrs = [conn.attribute for conn in getattr(self.obj,
|
attrs = [conn.attribute for conn
|
||||||
self._m2m_fieldname).through.objects.filter(**query)]
|
in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)]
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
if attr.pk:
|
if attr.pk:
|
||||||
cachekey = "%s-%s" % (attr.db_key, category)
|
cachekey = "%s-%s" % (attr.db_key, category)
|
||||||
|
|
@ -306,7 +310,6 @@ class AttributeHandler(object):
|
||||||
# mark category cache as up-to-date
|
# mark category cache as up-to-date
|
||||||
self._catcache[catkey] = True
|
self._catcache[catkey] = True
|
||||||
return attrs
|
return attrs
|
||||||
return []
|
|
||||||
|
|
||||||
def _setcache(self, key, category, attr_obj):
|
def _setcache(self, key, category, attr_obj):
|
||||||
"""
|
"""
|
||||||
|
|
@ -402,6 +405,7 @@ class AttributeHandler(object):
|
||||||
accessing_obj (object, optional): If set, an `attrread`
|
accessing_obj (object, optional): If set, an `attrread`
|
||||||
permission lock will be checked before returning each
|
permission lock will be checked before returning each
|
||||||
looked-after Attribute.
|
looked-after Attribute.
|
||||||
|
default_access (bool, optional):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
result (any, Attribute or list): This will be the value of the found
|
result (any, Attribute or list): This will be the value of the found
|
||||||
|
|
@ -416,7 +420,7 @@ class AttributeHandler(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class RetDefault(object):
|
class RetDefault(object):
|
||||||
"Holds default values"
|
"""Holds default values"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.key = None
|
self.key = None
|
||||||
self.value = default
|
self.value = default
|
||||||
|
|
@ -446,7 +450,6 @@ class AttributeHandler(object):
|
||||||
return ret if len(key) > 1 else default
|
return ret if len(key) > 1 else default
|
||||||
return ret[0] if len(ret) == 1 else ret
|
return ret[0] if len(ret) == 1 else ret
|
||||||
|
|
||||||
|
|
||||||
def add(self, key, value, category=None, lockstring="",
|
def add(self, key, value, category=None, lockstring="",
|
||||||
strattr=False, accessing_obj=None, default_access=True):
|
strattr=False, accessing_obj=None, default_access=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -470,8 +473,7 @@ class AttributeHandler(object):
|
||||||
`attrcreate` is defined on the Attribute in question.
|
`attrcreate` is defined on the Attribute in question.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if accessing_obj and not self.obj.access(accessing_obj,
|
if accessing_obj and not self.obj.access(accessing_obj, self._attrcreate, default=default_access):
|
||||||
self._attrcreate, default=default_access):
|
|
||||||
# check create access
|
# check create access
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -506,7 +508,6 @@ class AttributeHandler(object):
|
||||||
# update cache
|
# update cache
|
||||||
self._setcache(keystr, category, new_attr)
|
self._setcache(keystr, category, new_attr)
|
||||||
|
|
||||||
|
|
||||||
def batch_add(self, key, value, category=None, lockstring="",
|
def batch_add(self, key, value, category=None, lockstring="",
|
||||||
strattr=False, accessing_obj=None, default_access=True):
|
strattr=False, accessing_obj=None, default_access=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -535,8 +536,7 @@ class AttributeHandler(object):
|
||||||
RuntimeError: If `key` and `value` lists are not of the
|
RuntimeError: If `key` and `value` lists are not of the
|
||||||
same lengths.
|
same lengths.
|
||||||
"""
|
"""
|
||||||
if accessing_obj and not self.obj.access(accessing_obj,
|
if accessing_obj and not self.obj.access(accessing_obj, self._attrcreate, default=default_access):
|
||||||
self._attrcreate, default=default_access):
|
|
||||||
# check create access
|
# check create access
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -578,7 +578,6 @@ class AttributeHandler(object):
|
||||||
# Add new objects to m2m field all at once
|
# Add new objects to m2m field all at once
|
||||||
getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
|
getattr(self.obj, self._m2m_fieldname).add(*new_attrobjs)
|
||||||
|
|
||||||
|
|
||||||
def remove(self, key, raise_exception=False, category=None,
|
def remove(self, key, raise_exception=False, category=None,
|
||||||
accessing_obj=None, default_access=True):
|
accessing_obj=None, default_access=True):
|
||||||
"""
|
"""
|
||||||
|
|
@ -729,7 +728,6 @@ def initialize_nick_templates(in_template, out_template):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# create the regex for in_template
|
# create the regex for in_template
|
||||||
regex_string = fnmatch.translate(in_template)
|
regex_string = fnmatch.translate(in_template)
|
||||||
# we must account for a possible line break coming over the wire
|
# we must account for a possible line break coming over the wire
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@ Use the codes defined in ANSIPARSER in your text
|
||||||
to apply colour to text according to the ANSI standard.
|
to apply colour to text according to the ANSI standard.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
This is %crRed text%cn and this is normal again.
|
This is |rRed text|n and this is normal again.
|
||||||
This is {rRed text{n and this is normal again.
|
This is {rRed text{n and this is normal again. # soon to be depreciated
|
||||||
|
This is %crRed text%cn and this is normal again. # depreciated
|
||||||
|
|
||||||
|
|
||||||
Mostly you should not need to call parse_ansi() explicitly;
|
Mostly you should not need to call parse_ansi() explicitly;
|
||||||
it is run by Evennia just before returning data to/from the
|
it is run by Evennia just before returning data to/from the
|
||||||
user.
|
user. Depreciated example forms are available by extending
|
||||||
|
the ansi mapping.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from builtins import object, range
|
from builtins import object, range
|
||||||
|
|
@ -143,9 +146,9 @@ class ANSIParser(object):
|
||||||
else:
|
else:
|
||||||
# grayscale values (xterm indexes 0, 232-255, 15) for full spectrum
|
# grayscale values (xterm indexes 0, 232-255, 15) for full spectrum
|
||||||
letter = rgbtag[int(background) + 1]
|
letter = rgbtag[int(background) + 1]
|
||||||
if (letter == 'a'):
|
if letter == 'a':
|
||||||
colval = 16 # pure black @ index 16 (first color cube entry)
|
colval = 16 # pure black @ index 16 (first color cube entry)
|
||||||
elif (letter == 'z'):
|
elif letter == 'z':
|
||||||
colval = 231 # pure white @ index 231 (last color cube entry)
|
colval = 231 # pure white @ index 231 (last color cube entry)
|
||||||
else:
|
else:
|
||||||
# letter in range [b..y] (exactly 24 values!)
|
# letter in range [b..y] (exactly 24 values!)
|
||||||
|
|
@ -161,7 +164,7 @@ class ANSIParser(object):
|
||||||
colval = 16 + (red * 36) + (green * 6) + blue
|
colval = 16 + (red * 36) + (green * 6) + blue
|
||||||
|
|
||||||
return "\033[%s8;5;%sm" % (3 + int(background), colval)
|
return "\033[%s8;5;%sm" % (3 + int(background), colval)
|
||||||
# replaced since some cliens (like Potato) does not accept codes with leading zeroes, see issue #1024.
|
# replaced since some clients (like Potato) does not accept codes with leading zeroes, see issue #1024.
|
||||||
# return "\033[%s8;5;%s%s%sm" % (3 + int(background), colval // 100, (colval % 100) // 10, colval%10)
|
# return "\033[%s8;5;%s%s%sm" % (3 + int(background), colval // 100, (colval % 100) // 10, colval%10)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -366,7 +369,7 @@ class ANSIParser(object):
|
||||||
(r'{[W', ANSI_BACK_WHITE), # light grey background
|
(r'{[W', ANSI_BACK_WHITE), # light grey background
|
||||||
(r'{[X', ANSI_BACK_BLACK), # pure black background
|
(r'{[X', ANSI_BACK_BLACK), # pure black background
|
||||||
|
|
||||||
## alternative |-format
|
# alternative |-format
|
||||||
|
|
||||||
(r'|n', ANSI_NORMAL), # reset
|
(r'|n', ANSI_NORMAL), # reset
|
||||||
(r'|/', ANSI_RETURN), # line break
|
(r'|/', ANSI_RETURN), # line break
|
||||||
|
|
@ -433,8 +436,7 @@ class ANSIParser(object):
|
||||||
(r'{[w', r'{[555'), # white background
|
(r'{[w', r'{[555'), # white background
|
||||||
(r'{[x', r'{[222'), # dark grey background
|
(r'{[x', r'{[222'), # dark grey background
|
||||||
|
|
||||||
## |-style variations
|
# |-style variations
|
||||||
|
|
||||||
(r'|[r', r'|[500'),
|
(r'|[r', r'|[500'),
|
||||||
(r'|[g', r'|[050'),
|
(r'|[g', r'|[050'),
|
||||||
(r'|[y', r'|[550'),
|
(r'|[y', r'|[550'),
|
||||||
|
|
@ -450,11 +452,11 @@ class ANSIParser(object):
|
||||||
xterm256_map = [
|
xterm256_map = [
|
||||||
(r'\{[0-5]{3}', ""), # {123 - foreground colour
|
(r'\{[0-5]{3}', ""), # {123 - foreground colour
|
||||||
(r'\{\[[0-5]{3}', ""), # {[123 - background colour
|
(r'\{\[[0-5]{3}', ""), # {[123 - background colour
|
||||||
## |-style
|
# |-style
|
||||||
(r'\|[0-5]{3}', ""), # |123 - foreground colour
|
(r'\|[0-5]{3}', ""), # |123 - foreground colour
|
||||||
(r'\|\[[0-5]{3}', ""), # |[123 - background colour
|
(r'\|\[[0-5]{3}', ""), # |[123 - background colour
|
||||||
|
|
||||||
## grayscale entries including ansi extremes: {=a .. {=z
|
# grayscale entries including ansi extremes: {=a .. {=z
|
||||||
(r'\{=[a-z]', ""),
|
(r'\{=[a-z]', ""),
|
||||||
(r'\{\[=[a-z]', ""),
|
(r'\{\[=[a-z]', ""),
|
||||||
(r'\|=[a-z]', ""),
|
(r'\|=[a-z]', ""),
|
||||||
|
|
@ -512,6 +514,7 @@ def strip_ansi(string, parser=ANSI_PARSER):
|
||||||
markup.
|
markup.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
string (str): The string to strip.
|
||||||
parser (ansi.AnsiParser, optional): The parser to use.
|
parser (ansi.AnsiParser, optional): The parser to use.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -520,6 +523,7 @@ def strip_ansi(string, parser=ANSI_PARSER):
|
||||||
"""
|
"""
|
||||||
return parser.parse_ansi(string, strip_ansi=True)
|
return parser.parse_ansi(string, strip_ansi=True)
|
||||||
|
|
||||||
|
|
||||||
def strip_raw_ansi(string, parser=ANSI_PARSER):
|
def strip_raw_ansi(string, parser=ANSI_PARSER):
|
||||||
"""
|
"""
|
||||||
Remove raw ansi codes from string. This assumes pure
|
Remove raw ansi codes from string. This assumes pure
|
||||||
|
|
@ -544,7 +548,7 @@ def raw(string):
|
||||||
string (str): The raw, escaped string.
|
string (str): The raw, escaped string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return string.replace('{', '{{')
|
return string.replace('{', '{{').replace('|', '||')
|
||||||
|
|
||||||
|
|
||||||
def group(lst, n):
|
def group(lst, n):
|
||||||
|
|
@ -1129,7 +1133,6 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)):
|
||||||
rstripped = rstripped[::-1]
|
rstripped = rstripped[::-1]
|
||||||
return ANSIString(lstripped + raw[ir1:ir2+1] + rstripped)
|
return ANSIString(lstripped + raw[ir1:ir2+1] + rstripped)
|
||||||
|
|
||||||
|
|
||||||
def lstrip(self, chars=None):
|
def lstrip(self, chars=None):
|
||||||
"""
|
"""
|
||||||
Strip from the left, taking ANSI markers into account.
|
Strip from the left, taking ANSI markers into account.
|
||||||
|
|
|
||||||
|
|
@ -183,12 +183,13 @@ _ENCODINGS = settings.ENCODINGS
|
||||||
_RE_INSERT = re.compile(r"^\#INSERT (.*)", re.MULTILINE)
|
_RE_INSERT = re.compile(r"^\#INSERT (.*)", re.MULTILINE)
|
||||||
_RE_CLEANBLOCK = re.compile(r"^\#.*?$|^\s*$", re.MULTILINE)
|
_RE_CLEANBLOCK = re.compile(r"^\#.*?$|^\s*$", re.MULTILINE)
|
||||||
_RE_CMD_SPLIT = re.compile(r"^\#.*?$", re.MULTILINE)
|
_RE_CMD_SPLIT = re.compile(r"^\#.*?$", re.MULTILINE)
|
||||||
_RE_CODE_OR_HEADER = re.compile(r"(\A|^\#CODE|^\#HEADER).*?$(.*?)(?=^#CODE.*?$|^#HEADER.*?$|\Z)", re.MULTILINE + re.DOTALL)
|
_RE_CODE_OR_HEADER = re.compile(r"(\A|^\#CODE|^\#HEADER).*?$(.*?)(?=^#CODE.*?$|^#HEADER.*?$|\Z)",
|
||||||
|
re.MULTILINE + re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# Helper function
|
# Helper function
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
def read_batchfile(pythonpath, file_ending='.py'):
|
def read_batchfile(pythonpath, file_ending='.py'):
|
||||||
"""
|
"""
|
||||||
|
|
@ -212,8 +213,7 @@ def read_batchfile(pythonpath, file_ending='.py'):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# find all possible absolute paths
|
# find all possible absolute paths
|
||||||
abspaths = utils.pypath_to_realpath(pythonpath,
|
abspaths = utils.pypath_to_realpath(pythonpath, file_ending, settings.BASE_BATCHPROCESS_PATHS)
|
||||||
file_ending, settings.BASE_BATCHPROCESS_PATHS)
|
|
||||||
if not abspaths:
|
if not abspaths:
|
||||||
raise IOError
|
raise IOError
|
||||||
text = None
|
text = None
|
||||||
|
|
@ -237,11 +237,11 @@ def read_batchfile(pythonpath, file_ending='.py'):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Batch-command processor
|
# Batch-command processor
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class BatchCommandProcessor(object):
|
class BatchCommandProcessor(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -271,15 +271,15 @@ class BatchCommandProcessor(object):
|
||||||
text = "".join(read_batchfile(pythonpath, file_ending='.ev'))
|
text = "".join(read_batchfile(pythonpath, file_ending='.ev'))
|
||||||
|
|
||||||
def replace_insert(match):
|
def replace_insert(match):
|
||||||
"Map replace entries"
|
"""Map replace entries"""
|
||||||
return "\n#".join(self.parse_file(match.group(1)))
|
return "\n#".join(self.parse_file(match.group(1)))
|
||||||
|
|
||||||
# insert commands from inserted files
|
# insert commands from inserted files
|
||||||
text = _RE_INSERT.sub(replace_insert, text)
|
text = _RE_INSERT.sub(replace_insert, text)
|
||||||
#text = re.sub(r"^\#INSERT (.*?)", replace_insert, text, flags=re.MULTILINE)
|
# re.sub(r"^\#INSERT (.*?)", replace_insert, text, flags=re.MULTILINE)
|
||||||
# get all commands
|
# get all commands
|
||||||
commands = _RE_CMD_SPLIT.split(text)
|
commands = _RE_CMD_SPLIT.split(text)
|
||||||
#commands = re.split(r"^\#.*?$", text, flags=re.MULTILINE)
|
# re.split(r"^\#.*?$", text, flags=re.MULTILINE)
|
||||||
# remove eventual newline at the end of commands
|
# remove eventual newline at the end of commands
|
||||||
commands = [c.strip('\r\n') for c in commands]
|
commands = [c.strip('\r\n') for c in commands]
|
||||||
commands = [c for c in commands if c]
|
commands = [c for c in commands if c]
|
||||||
|
|
@ -287,19 +287,19 @@ class BatchCommandProcessor(object):
|
||||||
return commands
|
return commands
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Batch-code processor
|
# Batch-code processor
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
def tb_filename(tb):
|
def tb_filename(tb):
|
||||||
"Helper to get filename from traceback"
|
"""Helper to get filename from traceback"""
|
||||||
return tb.tb_frame.f_code.co_filename
|
return tb.tb_frame.f_code.co_filename
|
||||||
|
|
||||||
|
|
||||||
def tb_iter(tb):
|
def tb_iter(tb):
|
||||||
"Traceback iterator."
|
"""Traceback iterator."""
|
||||||
while tb is not None:
|
while tb is not None:
|
||||||
yield tb
|
yield tb
|
||||||
tb = tb.tb_next
|
tb = tb.tb_next
|
||||||
|
|
@ -341,7 +341,7 @@ class BatchCodeProcessor(object):
|
||||||
text = "".join(read_batchfile(pythonpath, file_ending='.py'))
|
text = "".join(read_batchfile(pythonpath, file_ending='.py'))
|
||||||
|
|
||||||
def replace_insert(match):
|
def replace_insert(match):
|
||||||
"Run parse_file on the import before sub:ing it into this file"
|
"""Run parse_file on the import before sub:ing it into this file"""
|
||||||
path = match.group(1)
|
path = match.group(1)
|
||||||
return "# batchcode insert (%s):" % path + "\n".join(self.parse_file(path))
|
return "# batchcode insert (%s):" % path + "\n".join(self.parse_file(path))
|
||||||
|
|
||||||
|
|
@ -365,7 +365,6 @@ class BatchCodeProcessor(object):
|
||||||
codes = ["%s# batchcode code:\n%s" % (header, code) for code in codes]
|
codes = ["%s# batchcode code:\n%s" % (header, code) for code in codes]
|
||||||
return codes
|
return codes
|
||||||
|
|
||||||
|
|
||||||
def code_exec(self, code, extra_environ=None, debug=False):
|
def code_exec(self, code, extra_environ=None, debug=False):
|
||||||
"""
|
"""
|
||||||
Execute a single code block, including imports and appending
|
Execute a single code block, including imports and appending
|
||||||
|
|
@ -406,7 +405,7 @@ class BatchCodeProcessor(object):
|
||||||
err = ""
|
err = ""
|
||||||
for iline, line in enumerate(code.split("\n")):
|
for iline, line in enumerate(code.split("\n")):
|
||||||
if iline == lineno:
|
if iline == lineno:
|
||||||
err += "\n{w%02i{n: %s" % (iline + 1, line)
|
err += "\n|w%02i|n: %s" % (iline + 1, line)
|
||||||
elif lineno - 5 < iline < lineno + 5:
|
elif lineno - 5 < iline < lineno + 5:
|
||||||
err += "\n%02i: %s" % (iline + 1, line)
|
err += "\n%02i: %s" % (iline + 1, line)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ _GA = object.__getattribute__
|
||||||
|
|
||||||
#
|
#
|
||||||
# Game Object creation
|
# Game Object creation
|
||||||
#
|
|
||||||
|
|
||||||
def create_object(typeclass=None, key=None, location=None,
|
|
||||||
home=None, permissions=None, locks=None,
|
def create_object(typeclass=None, key=None, location=None, home=None,
|
||||||
aliases=None, tags=None, destination=None, report_to=None, nohome=False):
|
permissions=None, locks=None, aliases=None, tags=None,
|
||||||
|
destination=None, report_to=None, nohome=False):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Create a new in-game object.
|
Create a new in-game object.
|
||||||
|
|
@ -113,10 +113,9 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
db_destination=destination, db_home=home,
|
db_destination=destination, db_home=home,
|
||||||
db_typeclass_path=typeclass.path)
|
db_typeclass_path=typeclass.path)
|
||||||
# store the call signature for the signal
|
# store the call signature for the signal
|
||||||
new_object._createdict = {"key":key, "location":location, "destination":destination,
|
new_object._createdict = dict(key=key, location=location, destination=destination, home=home,
|
||||||
"home":home, "typeclass":typeclass.path, "permissions":permissions,
|
typeclass=typeclass.path, permissions=permissions, locks=locks,
|
||||||
"locks":locks, "aliases":aliases, "tags": tags,
|
aliases=aliases, tags=tags, report_to=report_to, nohome=nohome)
|
||||||
"report_to":report_to, "nohome":nohome}
|
|
||||||
# this will trigger the save signal which in turn calls the
|
# this will trigger the save signal which in turn calls the
|
||||||
# at_first_save hook on the typeclass, where the _createdict can be
|
# at_first_save hook on the typeclass, where the _createdict can be
|
||||||
# used.
|
# used.
|
||||||
|
|
@ -129,7 +128,6 @@ object = create_object
|
||||||
|
|
||||||
#
|
#
|
||||||
# Script creation
|
# Script creation
|
||||||
#
|
|
||||||
|
|
||||||
def create_script(typeclass=None, key=None, obj=None, player=None, locks=None,
|
def create_script(typeclass=None, key=None, obj=None, player=None, locks=None,
|
||||||
interval=None, start_delay=None, repeats=None,
|
interval=None, start_delay=None, repeats=None,
|
||||||
|
|
@ -194,12 +192,9 @@ def create_script(typeclass=None, key=None, obj=None, player=None, locks=None,
|
||||||
new_script = typeclass(**kwarg)
|
new_script = typeclass(**kwarg)
|
||||||
|
|
||||||
# store the call signature for the signal
|
# store the call signature for the signal
|
||||||
new_script._createdict = {"key":key, "obj":obj, "player":player,
|
new_script._createdict = dict(key=key, obj=obj, player=player, locks=locks, interval=interval,
|
||||||
"locks":locks, "interval":interval,
|
start_delay=start_delay, repeats=repeats, persistent=persistent,
|
||||||
"start_delay":start_delay, "repeats":repeats,
|
autostart=autostart, report_to=report_to)
|
||||||
"persistent":persistent, "autostart":autostart,
|
|
||||||
"report_to":report_to}
|
|
||||||
|
|
||||||
# this will trigger the save signal which in turn calls the
|
# this will trigger the save signal which in turn calls the
|
||||||
# at_first_save hook on the typeclass, where the _createdict
|
# at_first_save hook on the typeclass, where the _createdict
|
||||||
# can be used.
|
# can be used.
|
||||||
|
|
@ -227,6 +222,7 @@ def create_help_entry(key, entrytext, category="General", locks=None, aliases=No
|
||||||
entrytext (str): The body of te help entry
|
entrytext (str): The body of te help entry
|
||||||
category (str, optional): The help category of the entry.
|
category (str, optional): The help category of the entry.
|
||||||
locks (str, optional): A lockstring to restrict access.
|
locks (str, optional): A lockstring to restrict access.
|
||||||
|
aliases (list of str): List of alternative (likely shorter) keynames.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
help (HelpEntry): A newly created help entry.
|
help (HelpEntry): A newly created help entry.
|
||||||
|
|
@ -260,10 +256,8 @@ help_entry = create_help_entry
|
||||||
|
|
||||||
#
|
#
|
||||||
# Comm system methods
|
# Comm system methods
|
||||||
#
|
|
||||||
|
|
||||||
def create_message(senderobj, message, channels=None,
|
def create_message(senderobj, message, channels=None, receivers=None, locks=None, header=None):
|
||||||
receivers=None, locks=None, header=None):
|
|
||||||
"""
|
"""
|
||||||
Create a new communication Msg. Msgs represent a unit of
|
Create a new communication Msg. Msgs represent a unit of
|
||||||
database-persistent communication between entites.
|
database-persistent communication between entites.
|
||||||
|
|
@ -325,7 +319,7 @@ def create_channel(key, aliases=None, desc=None,
|
||||||
key (str): This must be unique.
|
key (str): This must be unique.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
aliases (list): List of alternative (likely shorter) keynames.
|
aliases (list of str): List of alternative (likely shorter) keynames.
|
||||||
desc (str): A description of the channel, for use in listings.
|
desc (str): A description of the channel, for use in listings.
|
||||||
locks (str): Lockstring.
|
locks (str): Lockstring.
|
||||||
keep_log (bool): Log channel throughput.
|
keep_log (bool): Log channel throughput.
|
||||||
|
|
@ -346,8 +340,7 @@ def create_channel(key, aliases=None, desc=None,
|
||||||
new_channel = typeclass(db_key=key)
|
new_channel = typeclass(db_key=key)
|
||||||
|
|
||||||
# store call signature for the signal
|
# store call signature for the signal
|
||||||
new_channel._createdict = {"key":key, "aliases":aliases,
|
new_channel._createdict = dict(key=key, aliases=aliases, desc=desc, locks=locks, keep_log=keep_log)
|
||||||
"desc":desc, "locks":locks, "keep_log":keep_log}
|
|
||||||
|
|
||||||
# this will trigger the save signal which in turn calls the
|
# this will trigger the save signal which in turn calls the
|
||||||
# at_first_save hook on the typeclass, where the _createdict can be
|
# at_first_save hook on the typeclass, where the _createdict can be
|
||||||
|
|
@ -358,11 +351,11 @@ def create_channel(key, aliases=None, desc=None,
|
||||||
channel = create_channel
|
channel = create_channel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Player creation methods
|
# Player creation methods
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def create_player(key, email, password,
|
def create_player(key, email, password,
|
||||||
typeclass=None,
|
typeclass=None,
|
||||||
is_superuser=False,
|
is_superuser=False,
|
||||||
|
|
@ -376,7 +369,7 @@ def create_player(key, email, password,
|
||||||
key (str): The player's name. This should be unique.
|
key (str): The player's name. This should be unique.
|
||||||
email (str): Email on valid addr@addr.domain form. This is
|
email (str): Email on valid addr@addr.domain form. This is
|
||||||
technically required but if set to `None`, an email of
|
technically required but if set to `None`, an email of
|
||||||
`dummy@dummy.com` will be used as a placeholder.
|
`dummy@example.com` will be used as a placeholder.
|
||||||
password (str): Password in cleartext.
|
password (str): Password in cleartext.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
|
|
@ -411,7 +404,7 @@ def create_player(key, email, password,
|
||||||
# correctly when each object is recovered).
|
# correctly when each object is recovered).
|
||||||
|
|
||||||
if not email:
|
if not email:
|
||||||
email = "dummy@dummy.com"
|
email = "dummy@example.com"
|
||||||
if _PlayerDB.objects.filter(username__iexact=key):
|
if _PlayerDB.objects.filter(username__iexact=key):
|
||||||
raise ValueError("A Player with the name '%s' already exists." % key)
|
raise ValueError("A Player with the name '%s' already exists." % key)
|
||||||
|
|
||||||
|
|
@ -426,8 +419,7 @@ def create_player(key, email, password,
|
||||||
is_staff=is_superuser, is_superuser=is_superuser,
|
is_staff=is_superuser, is_superuser=is_superuser,
|
||||||
last_login=now, date_joined=now)
|
last_login=now, date_joined=now)
|
||||||
new_player.set_password(password)
|
new_player.set_password(password)
|
||||||
new_player._createdict = {"locks":locks, "permissions":permissions,
|
new_player._createdict = dict(locks=locks, permissions=permissions, report_to=report_to)
|
||||||
"report_to":report_to}
|
|
||||||
# saving will trigger the signal that calls the
|
# saving will trigger the signal that calls the
|
||||||
# at_first_save hook on the typeclass, where the _createdict
|
# at_first_save hook on the typeclass, where the _createdict
|
||||||
# can be used.
|
# can be used.
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ __all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle",
|
||||||
|
|
||||||
PICKLE_PROTOCOL = 2
|
PICKLE_PROTOCOL = 2
|
||||||
|
|
||||||
|
|
||||||
def _get_mysql_db_version():
|
def _get_mysql_db_version():
|
||||||
"""
|
"""
|
||||||
This is a helper method for specifically getting the version
|
This is a helper method for specifically getting the version
|
||||||
|
|
@ -93,7 +94,7 @@ def _TO_DATESTRING(obj):
|
||||||
|
|
||||||
|
|
||||||
def _init_globals():
|
def _init_globals():
|
||||||
"Lazy importing to avoid circular import issues"
|
"""Lazy importing to avoid circular import issues"""
|
||||||
global _FROM_MODEL_MAP, _TO_MODEL_MAP, _SESSION_HANDLER
|
global _FROM_MODEL_MAP, _TO_MODEL_MAP, _SESSION_HANDLER
|
||||||
if not _FROM_MODEL_MAP:
|
if not _FROM_MODEL_MAP:
|
||||||
_FROM_MODEL_MAP = defaultdict(str)
|
_FROM_MODEL_MAP = defaultdict(str)
|
||||||
|
|
@ -110,7 +111,7 @@ def _init_globals():
|
||||||
|
|
||||||
|
|
||||||
def _save(method):
|
def _save(method):
|
||||||
"method decorator that saves data to Attribute"
|
"""method decorator that saves data to Attribute"""
|
||||||
def save_wrapper(self, *args, **kwargs):
|
def save_wrapper(self, *args, **kwargs):
|
||||||
self.__doc__ = method.__doc__
|
self.__doc__ = method.__doc__
|
||||||
ret = method(self, *args, **kwargs)
|
ret = method(self, *args, **kwargs)
|
||||||
|
|
@ -127,17 +128,17 @@ class _SaverMutable(object):
|
||||||
will not save the updated value to the database.
|
will not save the updated value to the database.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"store all properties for tracking the tree"
|
"""store all properties for tracking the tree"""
|
||||||
self._parent = kwargs.pop("_parent", None)
|
self._parent = kwargs.pop("_parent", None)
|
||||||
self._db_obj = kwargs.pop("_db_obj", None)
|
self._db_obj = kwargs.pop("_db_obj", None)
|
||||||
self._data = None
|
self._data = None
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
"Make sure to evaluate as False if empty"
|
"""Make sure to evaluate as False if empty"""
|
||||||
return bool(self._data)
|
return bool(self._data)
|
||||||
|
|
||||||
def _save_tree(self):
|
def _save_tree(self):
|
||||||
"recursively traverse back up the tree, save when we reach the root"
|
"""recursively traverse back up the tree, save when we reach the root"""
|
||||||
if self._parent:
|
if self._parent:
|
||||||
self._parent._save_tree()
|
self._parent._save_tree()
|
||||||
elif self._db_obj:
|
elif self._db_obj:
|
||||||
|
|
@ -146,9 +147,9 @@ class _SaverMutable(object):
|
||||||
logger.log_err("_SaverMutable %s has no root Attribute to save to." % self)
|
logger.log_err("_SaverMutable %s has no root Attribute to save to." % self)
|
||||||
|
|
||||||
def _convert_mutables(self, data):
|
def _convert_mutables(self, data):
|
||||||
"converts mutables to Saver* variants and assigns ._parent property"
|
"""converts mutables to Saver* variants and assigns ._parent property"""
|
||||||
def process_tree(item, parent):
|
def process_tree(item, parent):
|
||||||
"recursively populate the tree, storing parents"
|
"""recursively populate the tree, storing parents"""
|
||||||
dtype = type(item)
|
dtype = type(item)
|
||||||
if dtype in (basestring, int, float, bool, tuple):
|
if dtype in (basestring, int, float, bool, tuple):
|
||||||
return item
|
return item
|
||||||
|
|
@ -218,7 +219,6 @@ class _SaverList(_SaverMutable, MutableSequence):
|
||||||
return self._data.index(value, *args)
|
return self._data.index(value, *args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class _SaverDict(_SaverMutable, MutableMapping):
|
class _SaverDict(_SaverMutable, MutableMapping):
|
||||||
"""
|
"""
|
||||||
A dict that stores changes to an Attribute when updated
|
A dict that stores changes to an Attribute when updated
|
||||||
|
|
@ -290,8 +290,10 @@ class _SaverDeque(_SaverMutable):
|
||||||
# maxlen property
|
# maxlen property
|
||||||
def _getmaxlen(self):
|
def _getmaxlen(self):
|
||||||
return self._data.maxlen
|
return self._data.maxlen
|
||||||
|
|
||||||
def _setmaxlen(self, value):
|
def _setmaxlen(self, value):
|
||||||
self._data.maxlen = value
|
self._data.maxlen = value
|
||||||
|
|
||||||
def _delmaxlen(self):
|
def _delmaxlen(self):
|
||||||
del self._data.maxlen
|
del self._data.maxlen
|
||||||
maxlen = property(_getmaxlen, _setmaxlen, _delmaxlen)
|
maxlen = property(_getmaxlen, _setmaxlen, _delmaxlen)
|
||||||
|
|
@ -314,7 +316,7 @@ class _SaverDeque(_SaverMutable):
|
||||||
|
|
||||||
#
|
#
|
||||||
# serialization helpers
|
# serialization helpers
|
||||||
#
|
|
||||||
|
|
||||||
def pack_dbobj(item):
|
def pack_dbobj(item):
|
||||||
"""
|
"""
|
||||||
|
|
@ -395,6 +397,7 @@ def pack_session(item):
|
||||||
_GA(item, "conn_time"))
|
_GA(item, "conn_time"))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def unpack_session(item):
|
def unpack_session(item):
|
||||||
"""
|
"""
|
||||||
Check and convert internal representations back to Sessions.
|
Check and convert internal representations back to Sessions.
|
||||||
|
|
@ -419,7 +422,7 @@ def unpack_session(item):
|
||||||
|
|
||||||
#
|
#
|
||||||
# Access methods
|
# Access methods
|
||||||
#
|
|
||||||
|
|
||||||
def to_pickle(data):
|
def to_pickle(data):
|
||||||
"""
|
"""
|
||||||
|
|
@ -437,7 +440,7 @@ def to_pickle(data):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def process_item(item):
|
def process_item(item):
|
||||||
"Recursive processor and identification of data"
|
"""Recursive processor and identification of data"""
|
||||||
dtype = type(item)
|
dtype = type(item)
|
||||||
if dtype in (basestring, int, float, bool):
|
if dtype in (basestring, int, float, bool):
|
||||||
return item
|
return item
|
||||||
|
|
@ -489,7 +492,7 @@ def from_pickle(data, db_obj=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def process_item(item):
|
def process_item(item):
|
||||||
"Recursive processor and identification of data"
|
"""Recursive processor and identification of data"""
|
||||||
dtype = type(item)
|
dtype = type(item)
|
||||||
if dtype in (basestring, int, float, bool):
|
if dtype in (basestring, int, float, bool):
|
||||||
return item
|
return item
|
||||||
|
|
@ -518,7 +521,7 @@ def from_pickle(data, db_obj=None):
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def process_tree(item, parent):
|
def process_tree(item, parent):
|
||||||
"Recursive processor, building a parent-tree from iterable data"
|
"""Recursive processor, building a parent-tree from iterable data"""
|
||||||
dtype = type(item)
|
dtype = type(item)
|
||||||
if dtype in (basestring, int, float, bool):
|
if dtype in (basestring, int, float, bool):
|
||||||
return item
|
return item
|
||||||
|
|
@ -590,20 +593,20 @@ def from_pickle(data, db_obj=None):
|
||||||
|
|
||||||
|
|
||||||
def do_pickle(data):
|
def do_pickle(data):
|
||||||
"Perform pickle to string"
|
"""Perform pickle to string"""
|
||||||
return to_str(dumps(data, protocol=PICKLE_PROTOCOL))
|
return to_str(dumps(data, protocol=PICKLE_PROTOCOL))
|
||||||
|
|
||||||
|
|
||||||
def do_unpickle(data):
|
def do_unpickle(data):
|
||||||
"Retrieve pickle from pickled string"
|
"""Retrieve pickle from pickled string"""
|
||||||
return loads(to_str(data))
|
return loads(to_str(data))
|
||||||
|
|
||||||
|
|
||||||
def dbserialize(data):
|
def dbserialize(data):
|
||||||
"Serialize to pickled form in one step"
|
"""Serialize to pickled form in one step"""
|
||||||
return do_pickle(to_pickle(data))
|
return do_pickle(to_pickle(data))
|
||||||
|
|
||||||
|
|
||||||
def dbunserialize(data, db_obj=None):
|
def dbunserialize(data, db_obj=None):
|
||||||
"Un-serialize in one step. See from_pickle for help db_obj."
|
"""Un-serialize in one step. See from_pickle for help db_obj."""
|
||||||
return from_pickle(do_unpickle(data), db_obj=db_obj)
|
return from_pickle(do_unpickle(data), db_obj=db_obj)
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,13 @@ _RE_GROUP = re.compile(r"\".*?\"|\'.*?\'|\S*")
|
||||||
# use NAWS in the future?
|
# use NAWS in the future?
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# texts
|
# texts
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
_HELP_TEXT = \
|
_HELP_TEXT = """
|
||||||
"""
|
|
||||||
<txt> - any non-command is appended to the end of the buffer.
|
<txt> - any non-command is appended to the end of the buffer.
|
||||||
: <l> - view buffer or only line(s) <l>
|
: <l> - view buffer or only line(s) <l>
|
||||||
:: <l> - raw-view buffer or only line(s) <l>
|
:: <l> - raw-view buffer or only line(s) <l>
|
||||||
|
|
@ -105,31 +104,27 @@ _HELP_TEXT = \
|
||||||
:echo - turn echoing of the input on/off (helpful for some clients)
|
:echo - turn echoing of the input on/off (helpful for some clients)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_HELP_LEGEND = \
|
_HELP_LEGEND = """
|
||||||
"""
|
|
||||||
Legend:
|
Legend:
|
||||||
<l> - line number, like '5' or range, like '3:7'.
|
<l> - line number, like '5' or range, like '3:7'.
|
||||||
<w> - a single word, or multiple words with quotes around them.
|
<w> - a single word, or multiple words with quotes around them.
|
||||||
<txt> - longer string, usually not needing quotes.
|
<txt> - longer string, usually not needing quotes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_HELP_CODE = \
|
_HELP_CODE = """
|
||||||
"""
|
|
||||||
:! - Execute code buffer without saving
|
:! - Execute code buffer without saving
|
||||||
:< - Decrease the level of automatic indentation for the next lines
|
:< - Decrease the level of automatic indentation for the next lines
|
||||||
:> - Increase the level of automatic indentation for the next lines
|
:> - Increase the level of automatic indentation for the next lines
|
||||||
:= - Switch automatic indentation on/off
|
:= - Switch automatic indentation on/off
|
||||||
""".lstrip("\n")
|
""".lstrip("\n")
|
||||||
|
|
||||||
_ERROR_LOADFUNC = \
|
_ERROR_LOADFUNC = """
|
||||||
"""
|
|
||||||
{error}
|
{error}
|
||||||
|
|
||||||
|rBuffer load function error. Could not load initial data.|n
|
|rBuffer load function error. Could not load initial data.|n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ERROR_SAVEFUNC = \
|
_ERROR_SAVEFUNC = """
|
||||||
"""
|
|
||||||
{error}
|
{error}
|
||||||
|
|
||||||
|rSave function returned an error. Buffer not saved.|n
|
|rSave function returned an error. Buffer not saved.|n
|
||||||
|
|
@ -140,15 +135,13 @@ _ERROR_NO_SAVEFUNC = "|rNo save function defined. Buffer cannot be saved.|n"
|
||||||
_MSG_SAVE_NO_CHANGE = "No changes need saving"
|
_MSG_SAVE_NO_CHANGE = "No changes need saving"
|
||||||
_DEFAULT_NO_QUITFUNC = "Exited editor."
|
_DEFAULT_NO_QUITFUNC = "Exited editor."
|
||||||
|
|
||||||
_ERROR_QUITFUNC = \
|
_ERROR_QUITFUNC = """
|
||||||
"""
|
|
||||||
{error}
|
{error}
|
||||||
|
|
||||||
|rQuit function gave an error. Skipping.|n
|
|rQuit function gave an error. Skipping.|n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ERROR_PERSISTENT_SAVING = \
|
_ERROR_PERSISTENT_SAVING = """
|
||||||
"""
|
|
||||||
{error}
|
{error}
|
||||||
|
|
||||||
|rThe editor state could not be saved for persistent mode. Switching
|
|rThe editor state could not be saved for persistent mode. Switching
|
||||||
|
|
@ -167,11 +160,12 @@ _MSG_NO_REDO = "Nothing to redo."
|
||||||
_MSG_UNDO = "Undid one step."
|
_MSG_UNDO = "Undid one step."
|
||||||
_MSG_REDO = "Redid one step."
|
_MSG_REDO = "Redid one step."
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Handle yes/no quit question
|
# Handle yes/no quit question
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class CmdSaveYesNo(Command):
|
class CmdSaveYesNo(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -185,7 +179,7 @@ class CmdSaveYesNo(Command):
|
||||||
help_cateogory = "LineEditor"
|
help_cateogory = "LineEditor"
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"Implement the yes/no choice."
|
"""Implement the yes/no choice."""
|
||||||
# this is only called from inside the lineeditor
|
# this is only called from inside the lineeditor
|
||||||
# so caller.ndb._lineditor must be set.
|
# so caller.ndb._lineditor must be set.
|
||||||
|
|
||||||
|
|
@ -200,21 +194,21 @@ class CmdSaveYesNo(Command):
|
||||||
|
|
||||||
|
|
||||||
class SaveYesNoCmdSet(CmdSet):
|
class SaveYesNoCmdSet(CmdSet):
|
||||||
"Stores the yesno question"
|
"""Stores the yesno question"""
|
||||||
key = "quitsave_yesno"
|
key = "quitsave_yesno"
|
||||||
priority = 1
|
priority = 1
|
||||||
mergetype = "Replace"
|
mergetype = "Replace"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"at cmdset creation"
|
"""at cmdset creation"""
|
||||||
self.add(CmdSaveYesNo())
|
self.add(CmdSaveYesNo())
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Editor commands
|
# Editor commands
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
class CmdEditorBase(Command):
|
class CmdEditorBase(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -239,7 +233,6 @@ class CmdEditorBase(Command):
|
||||||
txt - extra text (string), could be encased in quotes.
|
txt - extra text (string), could be encased in quotes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
linebuffer = []
|
|
||||||
editor = self.caller.ndb._eveditor
|
editor = self.caller.ndb._eveditor
|
||||||
if not editor:
|
if not editor:
|
||||||
# this will completely replace the editor
|
# this will completely replace the editor
|
||||||
|
|
@ -297,11 +290,7 @@ class CmdEditorBase(Command):
|
||||||
arglist = arglist[1:]
|
arglist = arglist[1:]
|
||||||
|
|
||||||
# nicer output formatting of the line range.
|
# nicer output formatting of the line range.
|
||||||
lstr = ""
|
lstr = "line %i" % (lstart + 1) if not linerange or lstart + 1 == lend else "lines %i-%i" % (lstart + 1, lend)
|
||||||
if not linerange or lstart + 1 == lend:
|
|
||||||
lstr = "line %i" % (lstart + 1)
|
|
||||||
else:
|
|
||||||
lstr = "lines %i-%i" % (lstart + 1, lend)
|
|
||||||
|
|
||||||
# arg1 and arg2 is whatever arguments. Line numbers or -ranges are
|
# arg1 and arg2 is whatever arguments. Line numbers or -ranges are
|
||||||
# never included here.
|
# never included here.
|
||||||
|
|
@ -369,25 +358,14 @@ class CmdLineInput(CmdEditorBase):
|
||||||
"""
|
"""
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
editor = caller.ndb._eveditor
|
editor = caller.ndb._eveditor
|
||||||
|
|
||||||
buf = editor.get_buffer()
|
buf = editor.get_buffer()
|
||||||
|
|
||||||
# add a line of text to buffer
|
# add a line of text to buffer
|
||||||
line = self.raw_string.strip("\r\n")
|
line = self.raw_string.strip("\r\n")
|
||||||
if not editor._codefunc:
|
if editor._codefunc and editor._indent >= 0:
|
||||||
if not buf:
|
|
||||||
buf = line
|
|
||||||
else:
|
|
||||||
buf = buf + "\n%s" % line
|
|
||||||
else:
|
|
||||||
# if automatic indentation is active, add spaces
|
# if automatic indentation is active, add spaces
|
||||||
if editor._indent >= 0:
|
|
||||||
line = editor.deduce_indent(line, buf)
|
line = editor.deduce_indent(line, buf)
|
||||||
|
buf = line if not buf else buf + "\n%s" % line
|
||||||
if not buf:
|
|
||||||
buf = line
|
|
||||||
else:
|
|
||||||
buf = buf + "\n%s" % line
|
|
||||||
self.editor.update_buffer(buf)
|
self.editor.update_buffer(buf)
|
||||||
if self.editor._echo_mode:
|
if self.editor._echo_mode:
|
||||||
# need to do it here or we will be off one line
|
# need to do it here or we will be off one line
|
||||||
|
|
@ -398,11 +376,10 @@ class CmdLineInput(CmdEditorBase):
|
||||||
if indent < 0:
|
if indent < 0:
|
||||||
indent = "off"
|
indent = "off"
|
||||||
|
|
||||||
self.caller.msg("{b%02i|{n ({g%s{n) %s" % (
|
self.caller.msg("|b%02i|||n (|g%s|n) %s" % (
|
||||||
cline, indent, line))
|
cline, indent, line))
|
||||||
else:
|
else:
|
||||||
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
|
self.caller.msg("|b%02i|||n %s" % (cline, self.args))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdEditorGroup(CmdEditorBase):
|
class CmdEditorGroup(CmdEditorBase):
|
||||||
|
|
@ -485,7 +462,7 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
# :dd <l> - delete line <l>
|
# :dd <l> - delete line <l>
|
||||||
buf = linebuffer[:lstart] + linebuffer[lend:]
|
buf = linebuffer[:lstart] + linebuffer[lend:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
caller.msg("Deleted %s." % (self.lstr))
|
caller.msg("Deleted %s." % self.lstr)
|
||||||
elif cmd == ":dw":
|
elif cmd == ":dw":
|
||||||
# :dw <w> - delete word in entire buffer
|
# :dw <w> - delete word in entire buffer
|
||||||
# :dw <l> <w> delete word only on line(s) <l>
|
# :dw <l> <w> delete word only on line(s) <l>
|
||||||
|
|
@ -556,7 +533,8 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
if not self.raw_string and not editor._codefunc:
|
if not self.raw_string and not editor._codefunc:
|
||||||
caller.msg("You need to enter text to insert.")
|
caller.msg("You need to enter text to insert.")
|
||||||
else:
|
else:
|
||||||
buf = linebuffer[:lstart] + ["%s%s" % (self.args, line) for line in linebuffer[lstart:lend]] + linebuffer[lend:]
|
buf = linebuffer[:lstart] + ["%s%s" % (self.args, line)
|
||||||
|
for line in linebuffer[lstart:lend]] + linebuffer[lend:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
caller.msg("Inserted text at beginning of %s." % self.lstr)
|
caller.msg("Inserted text at beginning of %s." % self.lstr)
|
||||||
elif cmd == ":A":
|
elif cmd == ":A":
|
||||||
|
|
@ -564,7 +542,8 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
if not self.args:
|
if not self.args:
|
||||||
caller.msg("You need to enter text to append.")
|
caller.msg("You need to enter text to append.")
|
||||||
else:
|
else:
|
||||||
buf = linebuffer[:lstart] + ["%s%s" % (line, self.args) for line in linebuffer[lstart:lend]] + linebuffer[lend:]
|
buf = linebuffer[:lstart] + ["%s%s" % (line, self.args)
|
||||||
|
for line in linebuffer[lstart:lend]] + linebuffer[lend:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
caller.msg("Appended text to end of %s." % self.lstr)
|
caller.msg("Appended text to end of %s." % self.lstr)
|
||||||
elif cmd == ":s":
|
elif cmd == ":s":
|
||||||
|
|
@ -585,7 +564,8 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
regarg = self.arg1.strip("\'").strip('\"')
|
regarg = self.arg1.strip("\'").strip('\"')
|
||||||
if " " in regarg:
|
if " " in regarg:
|
||||||
regarg = regarg.replace(" ", " +")
|
regarg = regarg.replace(" ", " +")
|
||||||
sarea = re.sub(regex % (regarg, regarg, regarg, regarg, regarg), self.arg2.strip("\'").strip('\"'), sarea, re.MULTILINE)
|
sarea = re.sub(regex % (regarg, regarg, regarg, regarg, regarg), self.arg2.strip("\'").strip('\"'),
|
||||||
|
sarea, re.MULTILINE)
|
||||||
buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:]
|
buf = linebuffer[:lstart] + sarea.split("\n") + linebuffer[lend:]
|
||||||
editor.update_buffer(buf)
|
editor.update_buffer(buf)
|
||||||
elif cmd == ":f":
|
elif cmd == ":f":
|
||||||
|
|
@ -693,18 +673,20 @@ class CmdEditorGroup(CmdEditorBase):
|
||||||
|
|
||||||
|
|
||||||
class EvEditorCmdSet(CmdSet):
|
class EvEditorCmdSet(CmdSet):
|
||||||
"CmdSet for the editor commands"
|
"""CmdSet for the editor commands"""
|
||||||
key = "editorcmdset"
|
key = "editorcmdset"
|
||||||
mergetype = "Replace"
|
mergetype = "Replace"
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
self.add(CmdLineInput())
|
self.add(CmdLineInput())
|
||||||
self.add(CmdEditorGroup())
|
self.add(CmdEditorGroup())
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Main Editor object
|
# Main Editor object
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class EvEditor(object):
|
class EvEditor(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -791,11 +773,9 @@ class EvEditor(object):
|
||||||
# save in tuple {kwargs, other options}
|
# save in tuple {kwargs, other options}
|
||||||
try:
|
try:
|
||||||
caller.attributes.add("_eveditor_saved", (
|
caller.attributes.add("_eveditor_saved", (
|
||||||
{"loadfunc":loadfunc, "savefunc": savefunc,
|
dict(loadfunc=loadfunc, savefunc=savefunc, quitfunc=quitfunc,
|
||||||
"quitfunc": quitfunc, "codefunc": codefunc,
|
codefunc=codefunc, key=key, persistent=persistent),
|
||||||
"key": key, "persistent": persistent},
|
dict(_pristine_buffer=self._pristine_buffer, _sep=self._sep)))
|
||||||
{"_pristine_buffer": self._pristine_buffer,
|
|
||||||
"_sep": self._sep}))
|
|
||||||
caller.attributes.add("_eveditor_buffer_temp", (self._buffer, self._undo_buffer))
|
caller.attributes.add("_eveditor_buffer_temp", (self._buffer, self._undo_buffer))
|
||||||
caller.attributes.add("_eveditor_unsaved", False)
|
caller.attributes.add("_eveditor_unsaved", False)
|
||||||
caller.attributes.add("_eveditor_indent", 0)
|
caller.attributes.add("_eveditor_indent", 0)
|
||||||
|
|
@ -933,7 +913,7 @@ class EvEditor(object):
|
||||||
`offset` should define the actual starting line number, to
|
`offset` should define the actual starting line number, to
|
||||||
get the linenum display right.
|
get the linenum display right.
|
||||||
linenums (bool, optional): Show line numbers in buffer.
|
linenums (bool, optional): Show line numbers in buffer.
|
||||||
raw (bool, optional): Tell protocol to not parse
|
options: raw (bool, optional): Tell protocol to not parse
|
||||||
formatting information.
|
formatting information.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -949,10 +929,10 @@ class EvEditor(object):
|
||||||
|
|
||||||
sep = self._sep
|
sep = self._sep
|
||||||
header = "|n" + sep * 10 + "Line Editor [%s]" % self._key + sep * (_DEFAULT_WIDTH-20-len(self._key))
|
header = "|n" + sep * 10 + "Line Editor [%s]" % self._key + sep * (_DEFAULT_WIDTH-20-len(self._key))
|
||||||
footer = "|n" + sep * 10 + "[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars) \
|
footer = "|n" + sep * 10 +\
|
||||||
+ sep * 12 + "(:h for help)" + sep * 28
|
"[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars) + sep * 12 + "(:h for help)" + sep * 28
|
||||||
if linenums:
|
if linenums:
|
||||||
main = "\n".join("{b%02i|{n %s" % (iline + 1 + offset, line) for iline, line in enumerate(lines))
|
main = "\n".join("|b%02i|||n %s" % (iline + 1 + offset, line) for iline, line in enumerate(lines))
|
||||||
else:
|
else:
|
||||||
main = "\n".join(lines)
|
main = "\n".join(lines)
|
||||||
string = "%s\n%s\n%s" % (header, main, footer)
|
string = "%s\n%s\n%s" % (header, main, footer)
|
||||||
|
|
@ -1018,6 +998,7 @@ class EvEditor(object):
|
||||||
self._indent += 1
|
self._indent += 1
|
||||||
if self._persistent:
|
if self._persistent:
|
||||||
self._caller.attributes.add("_eveditor_indent", self._indent)
|
self._caller.attributes.add("_eveditor_indent", self._indent)
|
||||||
|
|
||||||
def swap_autoindent(self):
|
def swap_autoindent(self):
|
||||||
"""Swap automatic indentation on or off."""
|
"""Swap automatic indentation on or off."""
|
||||||
if self._codefunc:
|
if self._codefunc:
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ Example usage:
|
||||||
|
|
||||||
Where `caller` is the Object to use the menu on - it will get a new
|
Where `caller` is the Object to use the menu on - it will get a new
|
||||||
cmdset while using the Menu. The menu_module_path is the python path
|
cmdset while using the Menu. The menu_module_path is the python path
|
||||||
to a python module containing function defintions. By adjusting the
|
to a python module containing function definitions. By adjusting the
|
||||||
keyword options of the Menu() initialization call you can start the
|
keyword options of the Menu() initialization call you can start the
|
||||||
menu at different places in the menu definition file, adjust if the
|
menu at different places in the menu definition file, adjust if the
|
||||||
menu command should overload the normal commands or not, etc.
|
menu command should overload the normal commands or not, etc.
|
||||||
|
|
||||||
The `perstent` keyword will make the menu survive a server reboot.
|
The `persistent` keyword will make the menu survive a server reboot.
|
||||||
It is `False` by default. Note that if using persistent mode, every
|
It is `False` by default. Note that if using persistent mode, every
|
||||||
node and callback in the menu must be possible to be *pickled*, this
|
node and callback in the menu must be possible to be *pickled*, this
|
||||||
excludes e.g. callables that are class methods or functions defined
|
excludes e.g. callables that are class methods or functions defined
|
||||||
|
|
@ -31,7 +31,7 @@ dynamically or as part of another function. In non-persistent mode
|
||||||
no such restrictions exist.
|
no such restrictions exist.
|
||||||
|
|
||||||
The menu is defined in a module (this can be the same module as the
|
The menu is defined in a module (this can be the same module as the
|
||||||
command definition too) with function defintions:
|
command definition too) with function definitions:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
|
|
@ -181,8 +181,7 @@ _HELP_NO_OPTIONS = _("Commands: help, quit")
|
||||||
_HELP_NO_OPTIONS_NO_QUIT = _("Commands: help")
|
_HELP_NO_OPTIONS_NO_QUIT = _("Commands: help")
|
||||||
_HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.")
|
_HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.")
|
||||||
|
|
||||||
_ERROR_PERSISTENT_SAVING = \
|
_ERROR_PERSISTENT_SAVING = """
|
||||||
"""
|
|
||||||
{error}
|
{error}
|
||||||
|
|
||||||
|rThe menu state could not be saved for persistent mode. Switching
|
|rThe menu state could not be saved for persistent mode. Switching
|
||||||
|
|
@ -190,8 +189,7 @@ to non-persistent mode (which means the menu session won't survive
|
||||||
an eventual server reload).|n
|
an eventual server reload).|n
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_TRACE_PERSISTENT_SAVING = \
|
_TRACE_PERSISTENT_SAVING = "EvMenu persistent-mode error. Commonly, this is because one or " \
|
||||||
"EvMenu persistent-mode error. Commonly, this is because one or " \
|
|
||||||
"more of the EvEditor callbacks could not be pickled, for example " \
|
"more of the EvEditor callbacks could not be pickled, for example " \
|
||||||
"because it's a class method or is defined inside another function."
|
"because it's a class method or is defined inside another function."
|
||||||
|
|
||||||
|
|
@ -203,11 +201,12 @@ class EvMenuError(RuntimeError):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Menu command and command set
|
# Menu command and command set
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class CmdEvMenuNode(Command):
|
class CmdEvMenuNode(Command):
|
||||||
"""
|
"""
|
||||||
|
|
@ -227,7 +226,11 @@ class CmdEvMenuNode(Command):
|
||||||
# this will re-start a completely new evmenu call.
|
# this will re-start a completely new evmenu call.
|
||||||
saved_options = caller.attributes.get("_menutree_saved")
|
saved_options = caller.attributes.get("_menutree_saved")
|
||||||
if saved_options:
|
if saved_options:
|
||||||
startnode, startnode_input = caller.attributes.get("_menutree_saved_startnode")
|
startnode_tuple = caller.attributes.get("_menutree_saved_startnode")
|
||||||
|
try:
|
||||||
|
startnode, startnode_input = startnode_tuple
|
||||||
|
except ValueError: # old form of startnode store
|
||||||
|
startnode, startnode_input = startnode_tuple, ""
|
||||||
if startnode:
|
if startnode:
|
||||||
saved_options[2]["startnode"] = startnode
|
saved_options[2]["startnode"] = startnode
|
||||||
saved_options[2]["startnode_input"] = startnode_input
|
saved_options[2]["startnode_input"] = startnode_input
|
||||||
|
|
@ -254,7 +257,7 @@ class CmdEvMenuNode(Command):
|
||||||
menu = caller.ndb._menutree
|
menu = caller.ndb._menutree
|
||||||
if not menu:
|
if not menu:
|
||||||
# can't restore from a session
|
# can't restore from a session
|
||||||
err = "Menu object not found as %s.ndb._menutree!" % (orig_caller)
|
err = "Menu object not found as %s.ndb._menutree!" % orig_caller
|
||||||
orig_caller.msg(err) # don't give the session as a kwarg here, direct to original
|
orig_caller.msg(err) # don't give the session as a kwarg here, direct to original
|
||||||
raise EvMenuError(err)
|
raise EvMenuError(err)
|
||||||
# we must do this after the caller with the menui has been correctly identified since it
|
# we must do this after the caller with the menui has been correctly identified since it
|
||||||
|
|
@ -287,7 +290,8 @@ class EvMenuCmdSet(CmdSet):
|
||||||
#
|
#
|
||||||
# Menu main class
|
# Menu main class
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class EvMenu(object):
|
class EvMenu(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -436,7 +440,17 @@ class EvMenu(object):
|
||||||
"persistent": persistent}
|
"persistent": persistent}
|
||||||
calldict.update(kwargs)
|
calldict.update(kwargs)
|
||||||
try:
|
try:
|
||||||
caller.attributes.add("_menutree_saved", ( self.__class__, (menudata, ), calldict ))
|
caller.attributes.add("_menutree_saved",
|
||||||
|
((menudata, ),
|
||||||
|
{"startnode": startnode,
|
||||||
|
"cmdset_mergetype": cmdset_mergetype,
|
||||||
|
"cmdset_priority": cmdset_priority,
|
||||||
|
"auto_quit": auto_quit, "auto_look": auto_look, "auto_help": auto_help,
|
||||||
|
"cmd_on_exit": cmd_on_exit,
|
||||||
|
"nodetext_formatter": nodetext_formatter,
|
||||||
|
"options_formatter": options_formatter,
|
||||||
|
"node_formatter": node_formatter, "input_parser": input_parser,
|
||||||
|
"persistent": persistent, }))
|
||||||
caller.attributes.add("_menutree_saved_startnode", (startnode, startnode_input))
|
caller.attributes.add("_menutree_saved_startnode", (startnode, startnode_input))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err), session=self._session)
|
caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err), session=self._session)
|
||||||
|
|
@ -505,7 +519,6 @@ class EvMenu(object):
|
||||||
# format the entire node
|
# format the entire node
|
||||||
return self.node_formatter(nodetext, optionstext)
|
return self.node_formatter(nodetext, optionstext)
|
||||||
|
|
||||||
|
|
||||||
def _execute_node(self, nodename, raw_string):
|
def _execute_node(self, nodename, raw_string):
|
||||||
"""
|
"""
|
||||||
Execute a node.
|
Execute a node.
|
||||||
|
|
@ -542,15 +555,12 @@ class EvMenu(object):
|
||||||
raise
|
raise
|
||||||
return nodetext, options
|
return nodetext, options
|
||||||
|
|
||||||
|
|
||||||
def display_nodetext(self):
|
def display_nodetext(self):
|
||||||
self.caller.msg(self.nodetext, session=self._session)
|
self.caller.msg(self.nodetext, session=self._session)
|
||||||
|
|
||||||
|
|
||||||
def display_helptext(self):
|
def display_helptext(self):
|
||||||
self.caller.msg(self.helptext, session=self._session)
|
self.caller.msg(self.helptext, session=self._session)
|
||||||
|
|
||||||
|
|
||||||
def callback_goto(self, callback, goto, raw_string):
|
def callback_goto(self, callback, goto, raw_string):
|
||||||
"""
|
"""
|
||||||
Call callback and goto in sequence.
|
Call callback and goto in sequence.
|
||||||
|
|
@ -860,15 +870,14 @@ class CmdGetInput(Command):
|
||||||
aliases = _CMD_NOINPUT
|
aliases = _CMD_NOINPUT
|
||||||
|
|
||||||
def func(self):
|
def func(self):
|
||||||
"This is called when user enters anything."
|
"""This is called when user enters anything."""
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
callback = caller.ndb._getinput._callback
|
try:
|
||||||
if not callback:
|
getinput = caller.ndb._getinput
|
||||||
# this can be happen if called from a player-command when IC
|
if not getinput and hasattr(caller, "player"):
|
||||||
caller = self.player
|
getinput = caller.player.ndb._getinput
|
||||||
callback = caller.ndb._getinput._callback
|
caller = caller.player
|
||||||
if not callback:
|
callback = getinput._callback
|
||||||
raise RuntimeError("No input callback found.")
|
|
||||||
|
|
||||||
caller.ndb._getinput._session = self.session
|
caller.ndb._getinput._session = self.session
|
||||||
prompt = caller.ndb._getinput._prompt
|
prompt = caller.ndb._getinput._prompt
|
||||||
|
|
@ -880,6 +889,11 @@ class CmdGetInput(Command):
|
||||||
# anything
|
# anything
|
||||||
del caller.ndb._getinput
|
del caller.ndb._getinput
|
||||||
caller.cmdset.remove(InputCmdSet)
|
caller.cmdset.remove(InputCmdSet)
|
||||||
|
except Exception:
|
||||||
|
# make sure to clean up cmdset if something goes wrong
|
||||||
|
caller.msg("|rError in get_input. Choice not confirmed (report to admin)|n")
|
||||||
|
logger.log_trace("Error in get_input")
|
||||||
|
caller.cmdset.remove(InputCmdSet)
|
||||||
|
|
||||||
|
|
||||||
class InputCmdSet(CmdSet):
|
class InputCmdSet(CmdSet):
|
||||||
|
|
@ -894,12 +908,12 @@ class InputCmdSet(CmdSet):
|
||||||
no_channels = False
|
no_channels = False
|
||||||
|
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
"called once at creation"
|
"""called once at creation"""
|
||||||
self.add(CmdGetInput())
|
self.add(CmdGetInput())
|
||||||
|
|
||||||
|
|
||||||
class _Prompt(object):
|
class _Prompt(object):
|
||||||
"Dummy holder"
|
"""Dummy holder"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -958,11 +972,11 @@ def get_input(caller, prompt, callback, session=None):
|
||||||
caller.msg(prompt, session=session)
|
caller.msg(prompt, session=session)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# test menu strucure and testing command
|
# test menu strucure and testing command
|
||||||
#
|
#
|
||||||
#------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
|
|
||||||
def test_start_node(caller):
|
def test_start_node(caller):
|
||||||
menu = caller.ndb._menutree
|
menu = caller.ndb._menutree
|
||||||
|
|
@ -978,17 +992,17 @@ def test_start_node(caller):
|
||||||
The menu was initialized with two variables: %s and %s.
|
The menu was initialized with two variables: %s and %s.
|
||||||
""" % (menu.testval, menu.testval2)
|
""" % (menu.testval, menu.testval2)
|
||||||
|
|
||||||
options = ({"key": ("{yS{net", "s"),
|
options = ({"key": ("|yS|net", "s"),
|
||||||
"desc": "Set an attribute on yourself.",
|
"desc": "Set an attribute on yourself.",
|
||||||
"exec": lambda caller: caller.attributes.add("menuattrtest", "Test value"),
|
"exec": lambda caller: caller.attributes.add("menuattrtest", "Test value"),
|
||||||
"goto": "test_set_node"},
|
"goto": "test_set_node"},
|
||||||
{"key": ("{yL{nook", "l"),
|
{"key": ("|yL|nook", "l"),
|
||||||
"desc": "Look and see a custom message.",
|
"desc": "Look and see a custom message.",
|
||||||
"goto": "test_look_node"},
|
"goto": "test_look_node"},
|
||||||
{"key": ("{yV{niew", "v"),
|
{"key": ("|yV|niew", "v"),
|
||||||
"desc": "View your own name",
|
"desc": "View your own name",
|
||||||
"goto": "test_view_node"},
|
"goto": "test_view_node"},
|
||||||
{"key": ("{yQ{nuit", "quit", "q", "Q"),
|
{"key": ("|yQ|nuit", "quit", "q", "Q"),
|
||||||
"desc": "Quit this menu example.",
|
"desc": "Quit this menu example.",
|
||||||
"goto": "test_end_node"},
|
"goto": "test_end_node"},
|
||||||
{"key": "_default",
|
{"key": "_default",
|
||||||
|
|
@ -998,16 +1012,17 @@ def test_start_node(caller):
|
||||||
|
|
||||||
def test_look_node(caller):
|
def test_look_node(caller):
|
||||||
text = ""
|
text = ""
|
||||||
options = {"key": ("{yL{nook", "l"),
|
options = {"key": ("|yL|nook", "l"),
|
||||||
"desc": "Go back to the previous menu.",
|
"desc": "Go back to the previous menu.",
|
||||||
"goto": "test_start_node"}
|
"goto": "test_start_node"}
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def test_set_node(caller):
|
def test_set_node(caller):
|
||||||
text = ("""
|
text = ("""
|
||||||
The attribute 'menuattrtest' was set to
|
The attribute 'menuattrtest' was set to
|
||||||
|
|
||||||
{w%s{n
|
|w%s|n
|
||||||
|
|
||||||
(check it with examine after quitting the menu).
|
(check it with examine after quitting the menu).
|
||||||
|
|
||||||
|
|
@ -1015,8 +1030,7 @@ def test_set_node(caller):
|
||||||
string "_default", meaning it will catch any input, in this case
|
string "_default", meaning it will catch any input, in this case
|
||||||
to return to the main menu. So you can e.g. press <return> to go
|
to return to the main menu. So you can e.g. press <return> to go
|
||||||
back now.
|
back now.
|
||||||
""" % caller.db.menuattrtest,
|
""" % caller.db.menuattrtest, # optional help text for this node
|
||||||
# optional help text for this node
|
|
||||||
"""
|
"""
|
||||||
This is the help entry for this node. It is created by returning
|
This is the help entry for this node. It is created by returning
|
||||||
the node text as a tuple - the second string in that tuple will be
|
the node text as a tuple - the second string in that tuple will be
|
||||||
|
|
@ -1031,7 +1045,7 @@ def test_set_node(caller):
|
||||||
|
|
||||||
def test_view_node(caller):
|
def test_view_node(caller):
|
||||||
text = """
|
text = """
|
||||||
Your name is {g%s{n!
|
Your name is |g%s|n!
|
||||||
|
|
||||||
click |lclook|lthere|le to trigger a look command under MXP.
|
click |lclook|lthere|le to trigger a look command under MXP.
|
||||||
This node's option has no explicit key (nor the "_default" key
|
This node's option has no explicit key (nor the "_default" key
|
||||||
|
|
@ -1048,7 +1062,7 @@ def test_displayinput_node(caller, raw_string):
|
||||||
text = """
|
text = """
|
||||||
You entered the text:
|
You entered the text:
|
||||||
|
|
||||||
"{w%s{n"
|
"|w%s|n"
|
||||||
|
|
||||||
... which could now be handled or stored here in some way if this
|
... which could now be handled or stored here in some way if this
|
||||||
was not just an example.
|
was not just an example.
|
||||||
|
|
@ -1089,5 +1103,5 @@ class CmdTestMenu(Command):
|
||||||
self.caller.msg("Usage: testmenu menumodule")
|
self.caller.msg("Usage: testmenu menumodule")
|
||||||
return
|
return
|
||||||
# start menu
|
# start menu
|
||||||
EvMenu(self.caller, self.args.strip(), startnode="test_start_node", persistent=True, cmdset_mergetype="Replace",
|
EvMenu(self.caller, self.args.strip(), startnode="test_start_node", persistent=True,
|
||||||
testval="val", testval2="val2")
|
cmdset_mergetype="Replace", testval="val", testval2="val2")
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ class EvMore(object):
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
|
|
||||||
# always limit number of chars to 10 000 per page
|
# always limit number of chars to 10 000 per page
|
||||||
height = min(10000 // width, height)
|
height = min(10000 // max(1, width), height)
|
||||||
|
|
||||||
self._pages = ["\n".join(lines[i:i+height]) for i in range(0, len(lines), height)]
|
self._pages = ["\n".join(lines[i:i+height]) for i in range(0, len(lines), height)]
|
||||||
self._npages = len(self._pages)
|
self._npages = len(self._pages)
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ from evennia.utils.ansi import ANSIString
|
||||||
|
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
||||||
|
|
||||||
def _to_ansi(obj):
|
def _to_ansi(obj):
|
||||||
"""
|
"""
|
||||||
convert to ANSIString.
|
convert to ANSIString.
|
||||||
|
|
@ -142,6 +143,8 @@ def _to_ansi(obj):
|
||||||
|
|
||||||
_unicode = unicode
|
_unicode = unicode
|
||||||
_whitespace = '\t\n\x0b\x0c\r '
|
_whitespace = '\t\n\x0b\x0c\r '
|
||||||
|
|
||||||
|
|
||||||
class ANSITextWrapper(TextWrapper):
|
class ANSITextWrapper(TextWrapper):
|
||||||
"""
|
"""
|
||||||
This is a wrapper work class for handling strings with ANSI tags
|
This is a wrapper work class for handling strings with ANSI tags
|
||||||
|
|
@ -158,8 +161,8 @@ class ANSITextWrapper(TextWrapper):
|
||||||
becomes " foo bar baz".
|
becomes " foo bar baz".
|
||||||
"""
|
"""
|
||||||
return text
|
return text
|
||||||
##TODO: Ignore expand_tabs/replace_whitespace until ANSISTring handles them.
|
# TODO: Ignore expand_tabs/replace_whitespace until ANSIString handles them.
|
||||||
## - don't remove this code. /Griatch
|
# - don't remove this code. /Griatch
|
||||||
# if self.expand_tabs:
|
# if self.expand_tabs:
|
||||||
# text = text.expandtabs()
|
# text = text.expandtabs()
|
||||||
# if self.replace_whitespace:
|
# if self.replace_whitespace:
|
||||||
|
|
@ -169,7 +172,6 @@ class ANSITextWrapper(TextWrapper):
|
||||||
# text = text.translate(self.unicode_whitespace_trans)
|
# text = text.translate(self.unicode_whitespace_trans)
|
||||||
# return text
|
# return text
|
||||||
|
|
||||||
|
|
||||||
def _split(self, text):
|
def _split(self, text):
|
||||||
"""_split(text : string) -> [string]
|
"""_split(text : string) -> [string]
|
||||||
|
|
||||||
|
|
@ -289,6 +291,7 @@ def wrap(text, width=_DEFAULT_WIDTH, **kwargs):
|
||||||
w = ANSITextWrapper(width=width, **kwargs)
|
w = ANSITextWrapper(width=width, **kwargs)
|
||||||
return w.wrap(text)
|
return w.wrap(text)
|
||||||
|
|
||||||
|
|
||||||
def fill(text, width=_DEFAULT_WIDTH, **kwargs):
|
def fill(text, width=_DEFAULT_WIDTH, **kwargs):
|
||||||
"""Fill a single paragraph of text, returning a new string.
|
"""Fill a single paragraph of text, returning a new string.
|
||||||
|
|
||||||
|
|
@ -311,6 +314,7 @@ def fill(text, width=_DEFAULT_WIDTH, **kwargs):
|
||||||
|
|
||||||
# EvCell class (see further down for the EvTable itself)
|
# EvCell class (see further down for the EvTable itself)
|
||||||
|
|
||||||
|
|
||||||
class EvCell(object):
|
class EvCell(object):
|
||||||
"""
|
"""
|
||||||
Holds a single data cell for the table. A cell has a certain width
|
Holds a single data cell for the table. A cell has a certain width
|
||||||
|
|
@ -442,14 +446,14 @@ class EvCell(object):
|
||||||
if "width" in kwargs:
|
if "width" in kwargs:
|
||||||
width = kwargs.pop("width")
|
width = kwargs.pop("width")
|
||||||
self.width = width - self.pad_left - self.pad_right - self.border_left - self.border_right
|
self.width = width - self.pad_left - self.pad_right - self.border_left - self.border_right
|
||||||
if self.width <= 0 and self.raw_width > 0:
|
if self.width <= 0 < self.raw_width:
|
||||||
raise Exception("Cell width too small - no space for data.")
|
raise Exception("Cell width too small - no space for data.")
|
||||||
else:
|
else:
|
||||||
self.width = self.raw_width
|
self.width = self.raw_width
|
||||||
if "height" in kwargs:
|
if "height" in kwargs:
|
||||||
height = kwargs.pop("height")
|
height = kwargs.pop("height")
|
||||||
self.height = height - self.pad_top - self.pad_bottom - self.border_top - self.border_bottom
|
self.height = height - self.pad_top - self.pad_bottom - self.border_top - self.border_bottom
|
||||||
if self.height <= 0 and self.raw_height > 0:
|
if self.height <= 0 < self.raw_height:
|
||||||
raise Exception("Cell height too small - no space for data.")
|
raise Exception("Cell height too small - no space for data.")
|
||||||
else:
|
else:
|
||||||
self.height = self.raw_height
|
self.height = self.raw_height
|
||||||
|
|
@ -512,7 +516,7 @@ class EvCell(object):
|
||||||
if 0 < width < m_len(line):
|
if 0 < width < m_len(line):
|
||||||
# replace_whitespace=False, expand_tabs=False is a
|
# replace_whitespace=False, expand_tabs=False is a
|
||||||
# fix for ANSIString not supporting expand_tabs/translate
|
# fix for ANSIString not supporting expand_tabs/translate
|
||||||
adjusted_data.extend([ANSIString(part + ANSIString("{n"))
|
adjusted_data.extend([ANSIString(part + ANSIString("|n"))
|
||||||
for part in wrap(line, width=width, drop_whitespace=False)])
|
for part in wrap(line, width=width, drop_whitespace=False)])
|
||||||
else:
|
else:
|
||||||
adjusted_data.append(line)
|
adjusted_data.append(line)
|
||||||
|
|
@ -526,7 +530,7 @@ class EvCell(object):
|
||||||
adjusted_data[-1] = adjusted_data[-1][:-2] + ".."
|
adjusted_data[-1] = adjusted_data[-1][:-2] + ".."
|
||||||
elif excess < 0:
|
elif excess < 0:
|
||||||
# too few lines. Fill to height.
|
# too few lines. Fill to height.
|
||||||
adjusted_data.extend(["" for i in range(excess)])
|
adjusted_data.extend(["" for _ in range(excess)])
|
||||||
|
|
||||||
return adjusted_data
|
return adjusted_data
|
||||||
|
|
||||||
|
|
@ -577,10 +581,13 @@ class EvCell(object):
|
||||||
hfill_char = self.hfill_char
|
hfill_char = self.hfill_char
|
||||||
width = self.width
|
width = self.width
|
||||||
if align == "l":
|
if align == "l":
|
||||||
lines= [(line.lstrip(" ") + " " if line.startswith(" ") and not line.startswith(" ") else line) + hfill_char * (width - m_len(line)) for line in data]
|
lines = [(line.lstrip(" ") + " " if line.startswith(" ") and not line.startswith(" ")
|
||||||
|
else line) + hfill_char * (width - m_len(line)) for line in data]
|
||||||
return lines
|
return lines
|
||||||
elif align == "r":
|
elif align == "r":
|
||||||
return [hfill_char * (width - m_len(line)) + (" " + line.rstrip(" ") if line.endswith(" ") and not line.endswith(" ") else line) for line in data]
|
return [hfill_char * (width - m_len(line)) + (" " + line.rstrip(" ")
|
||||||
|
if line.endswith(" ") and not line.endswith(" ")
|
||||||
|
else line) for line in data]
|
||||||
else: # center, 'c'
|
else: # center, 'c'
|
||||||
return [self._center(line, self.width, self.hfill_char) for line in data]
|
return [self._center(line, self.width, self.hfill_char) for line in data]
|
||||||
|
|
||||||
|
|
@ -605,11 +612,11 @@ class EvCell(object):
|
||||||
return data
|
return data
|
||||||
# only care if we need to add new lines
|
# only care if we need to add new lines
|
||||||
if valign == 't':
|
if valign == 't':
|
||||||
return data + [padline for i in range(excess)]
|
return data + [padline for _ in range(excess)]
|
||||||
elif valign == 'b':
|
elif valign == 'b':
|
||||||
return [padline for i in range(excess)] + data
|
return [padline for _ in range(excess)] + data
|
||||||
else: # center
|
else: # center
|
||||||
narrowside = [padline for i in range(excess // 2)]
|
narrowside = [padline for _ in range(excess // 2)]
|
||||||
widerside = narrowside + [padline]
|
widerside = narrowside + [padline]
|
||||||
if excess % 2:
|
if excess % 2:
|
||||||
# uneven padding
|
# uneven padding
|
||||||
|
|
@ -635,8 +642,8 @@ class EvCell(object):
|
||||||
left = self.hpad_char * self.pad_left
|
left = self.hpad_char * self.pad_left
|
||||||
right = self.hpad_char * self.pad_right
|
right = self.hpad_char * self.pad_right
|
||||||
vfill = (self.width + self.pad_left + self.pad_right) * self.vpad_char
|
vfill = (self.width + self.pad_left + self.pad_right) * self.vpad_char
|
||||||
top = [vfill for i in range(self.pad_top)]
|
top = [vfill for _ in range(self.pad_top)]
|
||||||
bottom = [vfill for i in range(self.pad_bottom)]
|
bottom = [vfill for _ in range(self.pad_bottom)]
|
||||||
return top + [left + line + right for line in data] + bottom
|
return top + [left + line + right for line in data] + bottom
|
||||||
|
|
||||||
def _border(self, data):
|
def _border(self, data):
|
||||||
|
|
@ -654,18 +661,17 @@ class EvCell(object):
|
||||||
left = self.border_left_char * self.border_left + ANSIString('|n')
|
left = self.border_left_char * self.border_left + ANSIString('|n')
|
||||||
right = ANSIString('|n') + self.border_right_char * self.border_right
|
right = ANSIString('|n') + self.border_right_char * self.border_right
|
||||||
|
|
||||||
cwidth = self.width + self.pad_left + self.pad_right + \
|
cwidth = self.width + self.pad_left + self.pad_right + max(0, self.border_left-1) + max(0, self.border_right-1)
|
||||||
max(0,self.border_left-1) + max(0, self.border_right-1)
|
|
||||||
|
|
||||||
vfill = self.corner_top_left_char if left else ""
|
vfill = self.corner_top_left_char if left else ""
|
||||||
vfill += cwidth * self.border_top_char
|
vfill += cwidth * self.border_top_char
|
||||||
vfill += self.corner_top_right_char if right else ""
|
vfill += self.corner_top_right_char if right else ""
|
||||||
top = [vfill for i in range(self.border_top)]
|
top = [vfill for _ in range(self.border_top)]
|
||||||
|
|
||||||
vfill = self.corner_bottom_left_char if left else ""
|
vfill = self.corner_bottom_left_char if left else ""
|
||||||
vfill += cwidth * self.border_bottom_char
|
vfill += cwidth * self.border_bottom_char
|
||||||
vfill += self.corner_bottom_right_char if right else ""
|
vfill += self.corner_bottom_right_char if right else ""
|
||||||
bottom = [vfill for i in range(self.border_bottom)]
|
bottom = [vfill for _ in range(self.border_bottom)]
|
||||||
|
|
||||||
return top + [left + line + right for line in data] + bottom
|
return top + [left + line + right for line in data] + bottom
|
||||||
|
|
||||||
|
|
@ -764,22 +770,34 @@ class EvCell(object):
|
||||||
self.vfill_char = vfill_char[0] if vfill_char else self.vfill_char
|
self.vfill_char = vfill_char[0] if vfill_char else self.vfill_char
|
||||||
|
|
||||||
borderwidth = kwargs.get("border_width", None)
|
borderwidth = kwargs.get("border_width", None)
|
||||||
self.border_left = kwargs.pop("border_left", borderwidth if borderwidth is not None else self.border_left)
|
self.border_left = kwargs.pop(
|
||||||
self.border_right = kwargs.pop("border_right", borderwidth if borderwidth is not None else self.border_right)
|
"border_left", borderwidth if borderwidth is not None else self.border_left)
|
||||||
self.border_top = kwargs.pop("border_top", borderwidth if borderwidth is not None else self.border_top)
|
self.border_right = kwargs.pop(
|
||||||
self.border_bottom = kwargs.pop("border_bottom", borderwidth if borderwidth is not None else self.border_bottom)
|
"border_right", borderwidth if borderwidth is not None else self.border_right)
|
||||||
|
self.border_top = kwargs.pop(
|
||||||
|
"border_top", borderwidth if borderwidth is not None else self.border_top)
|
||||||
|
self.border_bottom = kwargs.pop(
|
||||||
|
"border_bottom", borderwidth if borderwidth is not None else self.border_bottom)
|
||||||
|
|
||||||
borderchar = kwargs.get("border_char", None)
|
borderchar = kwargs.get("border_char", None)
|
||||||
self.border_left_char = kwargs.pop("border_left_char", borderchar if borderchar else self.border_left_char)
|
self.border_left_char = kwargs.pop(
|
||||||
self.border_right_char = kwargs.pop("border_right_char", borderchar if borderchar else self.border_right_char)
|
"border_left_char", borderchar if borderchar else self.border_left_char)
|
||||||
self.border_top_char = kwargs.pop("border_topchar", borderchar if borderchar else self.border_top_char)
|
self.border_right_char = kwargs.pop(
|
||||||
self.border_bottom_char = kwargs.pop("border_bottom_char", borderchar if borderchar else self.border_bottom_char)
|
"border_right_char", borderchar if borderchar else self.border_right_char)
|
||||||
|
self.border_top_char = kwargs.pop(
|
||||||
|
"border_topchar", borderchar if borderchar else self.border_top_char)
|
||||||
|
self.border_bottom_char = kwargs.pop(
|
||||||
|
"border_bottom_char", borderchar if borderchar else self.border_bottom_char)
|
||||||
|
|
||||||
corner_char = kwargs.get("corner_char", None)
|
corner_char = kwargs.get("corner_char", None)
|
||||||
self.corner_top_left_char = kwargs.pop("corner_top_left", corner_char if corner_char is not None else self.corner_top_left_char)
|
self.corner_top_left_char = kwargs.pop(
|
||||||
self.corner_top_right_char = kwargs.pop("corner_top_right", corner_char if corner_char is not None else self.corner_top_right_char)
|
"corner_top_left", corner_char if corner_char is not None else self.corner_top_left_char)
|
||||||
self.corner_bottom_left_char = kwargs.pop("corner_bottom_left", corner_char if corner_char is not None else self.corner_bottom_left_char)
|
self.corner_top_right_char = kwargs.pop(
|
||||||
self.corner_bottom_right_char = kwargs.pop("corner_bottom_right", corner_char if corner_char is not None else self.corner_bottom_right_char)
|
"corner_top_right", corner_char if corner_char is not None else self.corner_top_right_char)
|
||||||
|
self.corner_bottom_left_char = kwargs.pop(
|
||||||
|
"corner_bottom_left", corner_char if corner_char is not None else self.corner_bottom_left_char)
|
||||||
|
self.corner_bottom_right_char = kwargs.pop(
|
||||||
|
"corner_bottom_right", corner_char if corner_char is not None else self.corner_bottom_right_char)
|
||||||
|
|
||||||
# this is used by the table to adjust size of cells with borders in the middle
|
# this is used by the table to adjust size of cells with borders in the middle
|
||||||
# of the table
|
# of the table
|
||||||
|
|
@ -793,13 +811,16 @@ class EvCell(object):
|
||||||
# Handle sizes
|
# Handle sizes
|
||||||
if "width" in kwargs:
|
if "width" in kwargs:
|
||||||
width = kwargs.pop("width")
|
width = kwargs.pop("width")
|
||||||
self.width = width - self.pad_left - self.pad_right - self.border_left - self.border_right + self.trim_horizontal
|
self.width = width - self.pad_left - self.pad_right\
|
||||||
if self.width <= 0 and self.raw_width > 0:
|
- self.border_left - self.border_right + self.trim_horizontal
|
||||||
|
# if self.width <= 0 and self.raw_width > 0:
|
||||||
|
if self.width <= 0 < self.raw_width:
|
||||||
raise Exception("Cell width too small, no room for data.")
|
raise Exception("Cell width too small, no room for data.")
|
||||||
if "height" in kwargs:
|
if "height" in kwargs:
|
||||||
height = kwargs.pop("height")
|
height = kwargs.pop("height")
|
||||||
self.height = height - self.pad_top - self.pad_bottom - self.border_top - self.border_bottom + self.trim_vertical
|
self.height = height - self.pad_top - self.pad_bottom\
|
||||||
if self.height <= 0 and self.raw_height > 0:
|
- self.border_top - self.border_bottom + self.trim_vertical
|
||||||
|
if self.height <= 0 < self.raw_height:
|
||||||
raise Exception("Cell height too small, no room for data.")
|
raise Exception("Cell height too small, no room for data.")
|
||||||
|
|
||||||
# reformat (to new sizes, padding, header and borders)
|
# reformat (to new sizes, padding, header and borders)
|
||||||
|
|
@ -868,7 +889,7 @@ class EvColumn(object):
|
||||||
col = self.column
|
col = self.column
|
||||||
kwargs.update(self.options)
|
kwargs.update(self.options)
|
||||||
# use fixed width or adjust to the largest cell
|
# use fixed width or adjust to the largest cell
|
||||||
if not "width" in kwargs:
|
if "width" not in kwargs:
|
||||||
[cell.reformat() for cell in col] # this is necessary to get initial widths of all cells
|
[cell.reformat() for cell in col] # this is necessary to get initial widths of all cells
|
||||||
kwargs["width"] = max(cell.get_width() for cell in col) if col else 0
|
kwargs["width"] = max(cell.get_width() for cell in col) if col else 0
|
||||||
[cell.reformat(**kwargs) for cell in col]
|
[cell.reformat(**kwargs) for cell in col]
|
||||||
|
|
@ -904,7 +925,7 @@ class EvColumn(object):
|
||||||
|
|
||||||
def reformat(self, **kwargs):
|
def reformat(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Change the options for the collumn.
|
Change the options for the column.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
Keywords as per `EvCell.__init__`.
|
Keywords as per `EvCell.__init__`.
|
||||||
|
|
@ -930,19 +951,24 @@ class EvColumn(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<EvColumn\n %s>" % ("\n ".join([repr(cell) for cell in self.column]))
|
return "<EvColumn\n %s>" % ("\n ".join([repr(cell) for cell in self.column]))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.column)
|
return len(self.column)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.column)
|
return iter(self.column)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return self.column[index]
|
return self.column[index]
|
||||||
|
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
self.column[index] = value
|
self.column[index] = value
|
||||||
|
|
||||||
def __delitem__(self, index):
|
def __delitem__(self, index):
|
||||||
del self.column[index]
|
del self.column[index]
|
||||||
|
|
||||||
|
|
||||||
## Main Evtable class
|
# Main Evtable class
|
||||||
|
|
||||||
class EvTable(object):
|
class EvTable(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -998,7 +1024,7 @@ class EvTable(object):
|
||||||
height (int, optional): Fixed height of table. Defaults to being unset. Width is
|
height (int, optional): Fixed height of table. Defaults to being unset. Width is
|
||||||
still given precedence. If given, table cells will crop text rather
|
still given precedence. If given, table cells will crop text rather
|
||||||
than expand vertically.
|
than expand vertically.
|
||||||
evenwidth (bool, optional): Used with the `width` keyword. Adjusts collumns to have as even width as
|
evenwidth (bool, optional): Used with the `width` keyword. Adjusts columns to have as even width as
|
||||||
possible. This often looks best also for mixed-length tables. Default is `False`.
|
possible. This often looks best also for mixed-length tables. Default is `False`.
|
||||||
maxwidth (int, optional): This will set a maximum width
|
maxwidth (int, optional): This will set a maximum width
|
||||||
of the table while allowing it to be smaller. Only if it grows wider than this
|
of the table while allowing it to be smaller. Only if it grows wider than this
|
||||||
|
|
@ -1025,10 +1051,10 @@ class EvTable(object):
|
||||||
excess = len(header) - len(table)
|
excess = len(header) - len(table)
|
||||||
if excess > 0:
|
if excess > 0:
|
||||||
# header bigger than table
|
# header bigger than table
|
||||||
table.extend([] for i in range(excess))
|
table.extend([] for _ in range(excess))
|
||||||
elif excess < 0:
|
elif excess < 0:
|
||||||
# too short header
|
# too short header
|
||||||
header.extend(_to_ansi(["" for i in range(abs(excess))]))
|
header.extend(_to_ansi(["" for _ in range(abs(excess))]))
|
||||||
for ix, heading in enumerate(header):
|
for ix, heading in enumerate(header):
|
||||||
table[ix].insert(0, heading)
|
table[ix].insert(0, heading)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1043,7 +1069,7 @@ class EvTable(object):
|
||||||
border = kwargs.pop("border", "tablecols")
|
border = kwargs.pop("border", "tablecols")
|
||||||
if border is None:
|
if border is None:
|
||||||
border = "none"
|
border = "none"
|
||||||
if not border in ("none", "table", "tablecols",
|
if border not in ("none", "table", "tablecols",
|
||||||
"header", "incols", "cols", "rows", "cells"):
|
"header", "incols", "cols", "rows", "cells"):
|
||||||
raise Exception("Unsupported border type: '%s'" % border)
|
raise Exception("Unsupported border type: '%s'" % border)
|
||||||
self.border = border
|
self.border = border
|
||||||
|
|
@ -1052,10 +1078,14 @@ class EvTable(object):
|
||||||
self.border_width = kwargs.get("border_width", 1)
|
self.border_width = kwargs.get("border_width", 1)
|
||||||
self.corner_char = kwargs.get("corner_char", "+")
|
self.corner_char = kwargs.get("corner_char", "+")
|
||||||
pcorners = kwargs.pop("pretty_corners", False)
|
pcorners = kwargs.pop("pretty_corners", False)
|
||||||
self.corner_top_left_char = _to_ansi(kwargs.pop("corner_top_left_char", '.' if pcorners else self.corner_char))
|
self.corner_top_left_char = _to_ansi(kwargs.pop(
|
||||||
self.corner_top_right_char = _to_ansi(kwargs.pop("corner_top_right_char", '.' if pcorners else self.corner_char))
|
"corner_top_left_char", '.' if pcorners else self.corner_char))
|
||||||
self.corner_bottom_left_char = _to_ansi(kwargs.pop("corner_bottom_left_char", ' ' if pcorners else self.corner_char))
|
self.corner_top_right_char = _to_ansi(kwargs.pop(
|
||||||
self.corner_bottom_right_char = _to_ansi(kwargs.pop("corner_bottom_right_char", ' ' if pcorners else self.corner_char))
|
"corner_top_right_char", '.' if pcorners else self.corner_char))
|
||||||
|
self.corner_bottom_left_char = _to_ansi(kwargs.pop(
|
||||||
|
"corner_bottom_left_char", ' ' if pcorners else self.corner_char))
|
||||||
|
self.corner_bottom_right_char = _to_ansi(kwargs.pop(
|
||||||
|
"corner_bottom_right_char", ' ' if pcorners else self.corner_char))
|
||||||
|
|
||||||
self.width = kwargs.pop("width", None)
|
self.width = kwargs.pop("width", None)
|
||||||
self.height = kwargs.pop("height", None)
|
self.height = kwargs.pop("height", None)
|
||||||
|
|
@ -1114,7 +1144,7 @@ class EvTable(object):
|
||||||
headchar = self.header_line_char
|
headchar = self.header_line_char
|
||||||
|
|
||||||
def corners(ret):
|
def corners(ret):
|
||||||
"Handle corners of table"
|
"""Handle corners of table"""
|
||||||
if ix == 0 and iy == 0:
|
if ix == 0 and iy == 0:
|
||||||
ret["corner_top_left_char"] = self.corner_top_left_char
|
ret["corner_top_left_char"] = self.corner_top_left_char
|
||||||
if ix == nx and iy == 0:
|
if ix == nx and iy == 0:
|
||||||
|
|
@ -1126,47 +1156,47 @@ class EvTable(object):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def left_edge(ret):
|
def left_edge(ret):
|
||||||
"add vertical border along left table edge"
|
"""add vertical border along left table edge"""
|
||||||
if ix == 0:
|
if ix == 0:
|
||||||
ret["border_left"] = bwidth
|
ret["border_left"] = bwidth
|
||||||
# ret["trim_horizontal"] = bwidth
|
# ret["trim_horizontal"] = bwidth
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def top_edge(ret):
|
def top_edge(ret):
|
||||||
"add border along top table edge"
|
"""add border along top table edge"""
|
||||||
if iy == 0:
|
if iy == 0:
|
||||||
ret["border_top"] = bwidth
|
ret["border_top"] = bwidth
|
||||||
# ret["trim_vertical"] = bwidth
|
# ret["trim_vertical"] = bwidth
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def right_edge(ret):
|
def right_edge(ret):
|
||||||
"add vertical border along right table edge"
|
"""add vertical border along right table edge"""
|
||||||
if ix == nx: # and 0 < iy < ny:
|
if ix == nx: # and 0 < iy < ny:
|
||||||
ret["border_right"] = bwidth
|
ret["border_right"] = bwidth
|
||||||
# ret["trim_horizontal"] = 0
|
# ret["trim_horizontal"] = 0
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def bottom_edge(ret):
|
def bottom_edge(ret):
|
||||||
"add border along bottom table edge"
|
"""add border along bottom table edge"""
|
||||||
if iy == ny:
|
if iy == ny:
|
||||||
ret["border_bottom"] = bwidth
|
ret["border_bottom"] = bwidth
|
||||||
# ret["trim_vertical"] = bwidth
|
# ret["trim_vertical"] = bwidth
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def cols(ret):
|
def cols(ret):
|
||||||
"Adding vertical borders inside the table"
|
"""Adding vertical borders inside the table"""
|
||||||
if 0 <= ix < nx:
|
if 0 <= ix < nx:
|
||||||
ret["border_right"] = bwidth
|
ret["border_right"] = bwidth
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def rows(ret):
|
def rows(ret):
|
||||||
"Adding horizontal borders inside the table"
|
"""Adding horizontal borders inside the table"""
|
||||||
if 0 <= iy < ny:
|
if 0 <= iy < ny:
|
||||||
ret["border_bottom"] = bwidth
|
ret["border_bottom"] = bwidth
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def head(ret):
|
def head(ret):
|
||||||
"Add header underline"
|
"""Add header underline"""
|
||||||
if iy == 0:
|
if iy == 0:
|
||||||
# put different bottom line for header
|
# put different bottom line for header
|
||||||
ret["border_bottom"] = bwidth
|
ret["border_bottom"] = bwidth
|
||||||
|
|
@ -1180,11 +1210,11 @@ class EvTable(object):
|
||||||
ret = bottom_edge(right_edge(top_edge(left_edge(corners(ret)))))
|
ret = bottom_edge(right_edge(top_edge(left_edge(corners(ret)))))
|
||||||
if border in ("cols", "tablecols", "cells"):
|
if border in ("cols", "tablecols", "cells"):
|
||||||
ret = cols(right_edge(left_edge(ret)))
|
ret = cols(right_edge(left_edge(ret)))
|
||||||
if border in ("incols"):
|
if border in "incols":
|
||||||
ret = cols(ret)
|
ret = cols(ret)
|
||||||
if border in ("rows", "cells"):
|
if border in ("rows", "cells"):
|
||||||
ret = rows(bottom_edge(top_edge(ret)))
|
ret = rows(bottom_edge(top_edge(ret)))
|
||||||
if header and not border in ("none", None):
|
if header and border not in ("none", None):
|
||||||
ret = head(ret)
|
ret = head(ret)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -1211,6 +1241,8 @@ class EvTable(object):
|
||||||
# actual table. This allows us to add columns/rows
|
# actual table. This allows us to add columns/rows
|
||||||
# and re-balance over and over without issue.
|
# and re-balance over and over without issue.
|
||||||
self.worktable = deepcopy(self.table)
|
self.worktable = deepcopy(self.table)
|
||||||
|
# self._borders()
|
||||||
|
# return
|
||||||
options = copy(self.options)
|
options = copy(self.options)
|
||||||
|
|
||||||
# balance number of rows to make a rectangular table
|
# balance number of rows to make a rectangular table
|
||||||
|
|
@ -1222,7 +1254,7 @@ class EvTable(object):
|
||||||
self.worktable[icol].reformat(**options)
|
self.worktable[icol].reformat(**options)
|
||||||
if nrow < nrowmax:
|
if nrow < nrowmax:
|
||||||
# add more rows to too-short columns
|
# add more rows to too-short columns
|
||||||
empty_rows = ["" for i in range(nrowmax-nrow)]
|
empty_rows = ["" for _ in range(nrowmax-nrow)]
|
||||||
self.worktable[icol].add_rows(*empty_rows)
|
self.worktable[icol].add_rows(*empty_rows)
|
||||||
self.ncols = ncols
|
self.ncols = ncols
|
||||||
self.nrows = nrowmax
|
self.nrows = nrowmax
|
||||||
|
|
@ -1251,16 +1283,16 @@ class EvTable(object):
|
||||||
|
|
||||||
excess = width - cwmin
|
excess = width - cwmin
|
||||||
if self.evenwidth:
|
if self.evenwidth:
|
||||||
# make each collumn of equal width
|
# make each column of equal width
|
||||||
for i in range(excess):
|
for _ in range(excess):
|
||||||
# flood-fill the minimum table starting with the smallest collumns
|
# flood-fill the minimum table starting with the smallest columns
|
||||||
ci = cwidths_min.index(min(cwidths_min))
|
ci = cwidths_min.index(min(cwidths_min))
|
||||||
cwidths_min[ci] += 1
|
cwidths_min[ci] += 1
|
||||||
cwidths = cwidths_min
|
cwidths = cwidths_min
|
||||||
else:
|
else:
|
||||||
# make each collumn expand more proportional to their data size
|
# make each column expand more proportional to their data size
|
||||||
for i in range(excess):
|
for _ in range(excess):
|
||||||
# fill wider collumns first
|
# fill wider columns first
|
||||||
ci = cwidths.index(max(cwidths))
|
ci = cwidths.index(max(cwidths))
|
||||||
cwidths_min[ci] += 1
|
cwidths_min[ci] += 1
|
||||||
cwidths[ci] -= 3
|
cwidths[ci] -= 3
|
||||||
|
|
@ -1280,8 +1312,9 @@ class EvTable(object):
|
||||||
# if we are fixing the table height, it means cells must crop text instead of resizing.
|
# if we are fixing the table height, it means cells must crop text instead of resizing.
|
||||||
if nrowmax:
|
if nrowmax:
|
||||||
|
|
||||||
# get minimum possible cell heights for each collumn
|
# get minimum possible cell heights for each column
|
||||||
cheights_min = [max(cell.get_min_height() for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)]
|
cheights_min = [max(cell.get_min_height()
|
||||||
|
for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)]
|
||||||
chmin = sum(cheights_min)
|
chmin = sum(cheights_min)
|
||||||
|
|
||||||
if chmin > self.height:
|
if chmin > self.height:
|
||||||
|
|
@ -1294,9 +1327,9 @@ class EvTable(object):
|
||||||
|
|
||||||
excess = self.height - chmin
|
excess = self.height - chmin
|
||||||
even = self.height % 2 == 0
|
even = self.height % 2 == 0
|
||||||
for i in range(excess):
|
for position in range(excess):
|
||||||
# expand the cells with the most rows first
|
# expand the cells with the most rows first
|
||||||
if 0 <= i < nrowmax and nrowmax > 1:
|
if 0 <= position < nrowmax and nrowmax > 1:
|
||||||
# avoid adding to header first round (looks bad on very small tables)
|
# avoid adding to header first round (looks bad on very small tables)
|
||||||
ci = cheights[1:].index(max(cheights[1:])) + 1
|
ci = cheights[1:].index(max(cheights[1:])) + 1
|
||||||
else:
|
else:
|
||||||
|
|
@ -1318,7 +1351,7 @@ class EvTable(object):
|
||||||
col.reformat_cell(iy, height=cheights[iy], **options)
|
col.reformat_cell(iy, height=cheights[iy], **options)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = "ix=%s, iy=%s, height=%s: %s" % (ix, iy, cheights[iy], e.message)
|
msg = "ix=%s, iy=%s, height=%s: %s" % (ix, iy, cheights[iy], e.message)
|
||||||
raise Exception ("Error in vertical allign:\n %s" % msg)
|
raise Exception("Error in vertical align:\n %s" % msg)
|
||||||
|
|
||||||
# calculate actual table width/height in characters
|
# calculate actual table width/height in characters
|
||||||
self.cwidth = sum(cwidths)
|
self.cwidth = sum(cwidths)
|
||||||
|
|
@ -1387,12 +1420,12 @@ class EvTable(object):
|
||||||
if excess > 0:
|
if excess > 0:
|
||||||
# we need to add new rows to table
|
# we need to add new rows to table
|
||||||
for col in self.table:
|
for col in self.table:
|
||||||
empty_rows = ["" for i in range(excess)]
|
empty_rows = ["" for _ in range(excess)]
|
||||||
col.add_rows(*empty_rows, **options)
|
col.add_rows(*empty_rows, **options)
|
||||||
self.nrows += excess
|
self.nrows += excess
|
||||||
elif excess < 0:
|
elif excess < 0:
|
||||||
# we need to add new rows to new column
|
# we need to add new rows to new column
|
||||||
empty_rows = ["" for i in range(abs(excess))]
|
empty_rows = ["" for _ in range(abs(excess))]
|
||||||
column.add_rows(*empty_rows, **options)
|
column.add_rows(*empty_rows, **options)
|
||||||
self.nrows -= excess
|
self.nrows -= excess
|
||||||
|
|
||||||
|
|
@ -1444,12 +1477,12 @@ class EvTable(object):
|
||||||
|
|
||||||
if excess > 0:
|
if excess > 0:
|
||||||
# we need to add new empty columns to table
|
# we need to add new empty columns to table
|
||||||
empty_rows = ["" for i in range(htable)]
|
empty_rows = ["" for _ in range(htable)]
|
||||||
self.table.extend([EvColumn(*empty_rows, **options) for i in range(excess)])
|
self.table.extend([EvColumn(*empty_rows, **options) for _ in range(excess)])
|
||||||
self.ncols += excess
|
self.ncols += excess
|
||||||
elif excess < 0:
|
elif excess < 0:
|
||||||
# we need to add more cells to row
|
# we need to add more cells to row
|
||||||
row.extend(["" for i in range(abs(excess))])
|
row.extend(["" for _ in range(abs(excess))])
|
||||||
self.ncols -= excess
|
self.ncols -= excess
|
||||||
|
|
||||||
if ypos is None or ypos > htable - 1:
|
if ypos is None or ypos > htable - 1:
|
||||||
|
|
@ -1523,16 +1556,17 @@ class EvTable(object):
|
||||||
return [line for line in self._generate_lines()]
|
return [line for line in self._generate_lines()]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"print table (this also balances it)"
|
"""print table (this also balances it)"""
|
||||||
return str(unicode(ANSIString("\n").join([line for line in self._generate_lines()])))
|
return str(unicode(ANSIString("\n").join([line for line in self._generate_lines()])))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return unicode(ANSIString("\n").join([line for line in self._generate_lines()]))
|
return unicode(ANSIString("\n").join([line for line in self._generate_lines()]))
|
||||||
|
|
||||||
|
|
||||||
def _test():
|
def _test():
|
||||||
"Test"
|
"""Test"""
|
||||||
table = EvTable("{yHeading1{n", "{gHeading2{n", table=[[1,2,3],[4,5,6],[7,8,9]], border="cells", align="l")
|
table = EvTable("|yHeading1|n", "|gHeading2|n", table=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], border="cells", align="l")
|
||||||
table.add_column("{rThis is long data{n", "{bThis is even longer data{n")
|
table.add_column("|rThis is long data|n", "|bThis is even longer data|n")
|
||||||
table.add_row("This is a single row")
|
table.add_row("This is a single row")
|
||||||
print(unicode(table))
|
print(unicode(table))
|
||||||
table.reformat(width=50)
|
table.reformat(width=50)
|
||||||
|
|
@ -1541,5 +1575,9 @@ def _test():
|
||||||
print(unicode(table))
|
print(unicode(table))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
def _test2():
|
||||||
|
table = EvTable("|yHeading1|n", "|B|[GHeading2|n", "Heading3")
|
||||||
|
for i in range(100):
|
||||||
|
table.add_row("This is col 0, row %i" % i, "|gThis is col 1, row |w%i|n|g.|n" % i, "This is col 2, row %i" % i)
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ _GAME_EPOCH = None
|
||||||
# Helper Script dealing in gametime (created by `schedule` function
|
# Helper Script dealing in gametime (created by `schedule` function
|
||||||
# below).
|
# below).
|
||||||
|
|
||||||
|
|
||||||
class TimeScript(DefaultScript):
|
class TimeScript(DefaultScript):
|
||||||
"""Gametime-sensitive script."""
|
"""Gametime-sensitive script."""
|
||||||
|
|
||||||
|
|
@ -60,6 +61,7 @@ class TimeScript(DefaultScript):
|
||||||
|
|
||||||
# Access functions
|
# Access functions
|
||||||
|
|
||||||
|
|
||||||
def runtime():
|
def runtime():
|
||||||
"""
|
"""
|
||||||
Get the total runtime of the server since first start (minus
|
Get the total runtime of the server since first start (minus
|
||||||
|
|
@ -134,8 +136,9 @@ def gametime(absolute=False):
|
||||||
gtime = epoch + (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR
|
gtime = epoch + (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR
|
||||||
return gtime
|
return gtime
|
||||||
|
|
||||||
def real_seconds_until(sec=None, min=None, hour=None, day=None,
|
|
||||||
month=None, year=None):
|
def real_seconds_until(sec=None, min=None, hour=None,
|
||||||
|
day=None, month=None, year=None):
|
||||||
"""
|
"""
|
||||||
Return the real seconds until game time.
|
Return the real seconds until game time.
|
||||||
|
|
||||||
|
|
@ -187,8 +190,9 @@ def real_seconds_until(sec=None, min=None, hour=None, day=None,
|
||||||
seconds = (projected - current).total_seconds()
|
seconds = (projected - current).total_seconds()
|
||||||
return seconds / TIMEFACTOR
|
return seconds / TIMEFACTOR
|
||||||
|
|
||||||
def schedule(callback, repeat=False, sec=None, min=None, hour=None,
|
|
||||||
day=None, month=None, year=None):
|
def schedule(callback, repeat=False, sec=None, min=None,
|
||||||
|
hour=None, day=None, month=None, year=None):
|
||||||
"""
|
"""
|
||||||
Call a callback at a given in-game time.
|
Call a callback at a given in-game time.
|
||||||
|
|
||||||
|
|
@ -212,8 +216,8 @@ def schedule(callback, repeat=False, sec=None, min=None, hour=None,
|
||||||
schedule(func, min=5, sec=0) # Will call 5 minutes past the next (in-game) hour.
|
schedule(func, min=5, sec=0) # Will call 5 minutes past the next (in-game) hour.
|
||||||
schedule(func, hour=2, min=30, sec=0) # Will call the next (in-game) day at 02:30.
|
schedule(func, hour=2, min=30, sec=0) # Will call the next (in-game) day at 02:30.
|
||||||
"""
|
"""
|
||||||
seconds = real_seconds_until(sec=sec, min=min, hour=hour, day=day,
|
seconds = real_seconds_until(sec=sec, min=min, hour=hour,
|
||||||
month=month, year=year)
|
day=day, month=month, year=year)
|
||||||
script = create_script("evennia.utils.gametime.TimeScript",
|
script = create_script("evennia.utils.gametime.TimeScript",
|
||||||
key="TimeScript", desc="A gametime-sensitive script",
|
key="TimeScript", desc="A gametime-sensitive script",
|
||||||
interval=seconds, start_delay=True,
|
interval=seconds, start_delay=True,
|
||||||
|
|
@ -229,6 +233,7 @@ def schedule(callback, repeat=False, sec=None, min=None, hour=None,
|
||||||
}
|
}
|
||||||
return script
|
return script
|
||||||
|
|
||||||
|
|
||||||
def reset_gametime():
|
def reset_gametime():
|
||||||
"""
|
"""
|
||||||
Resets the game time to make it start from the current time. Note that
|
Resets the game time to make it start from the current time. Note that
|
||||||
|
|
@ -238,5 +243,3 @@ def reset_gametime():
|
||||||
global GAME_TIME_OFFSET
|
global GAME_TIME_OFFSET
|
||||||
GAME_TIME_OFFSET = runtime()
|
GAME_TIME_OFFSET = runtime()
|
||||||
ServerConfig.objects.conf("gametime_offset", GAME_TIME_OFFSET)
|
ServerConfig.objects.conf("gametime_offset", GAME_TIME_OFFSET)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ from twisted.internet.threads import deferToThread
|
||||||
_LOGDIR = None
|
_LOGDIR = None
|
||||||
_TIMEZONE = None
|
_TIMEZONE = None
|
||||||
|
|
||||||
|
|
||||||
def timeformat(when=None):
|
def timeformat(when=None):
|
||||||
"""
|
"""
|
||||||
This helper function will format the current time in the same
|
This helper function will format the current time in the same
|
||||||
|
|
@ -88,7 +89,7 @@ def log_err(errmsg):
|
||||||
Prints/logs an error message to the server log.
|
Prints/logs an error message to the server log.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
errormsg (str): The message to be logged.
|
errmsg (str): The message to be logged.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|
@ -97,7 +98,7 @@ def log_err(errmsg):
|
||||||
errmsg = str(e)
|
errmsg = str(e)
|
||||||
for line in errmsg.splitlines():
|
for line in errmsg.splitlines():
|
||||||
log.msg('[EE] %s' % line)
|
log.msg('[EE] %s' % line)
|
||||||
#log.err('ERROR: %s' % (errormsg,))
|
# log.err('ERROR: %s' % (errmsg,))
|
||||||
log_errmsg = log_err
|
log_errmsg = log_err
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -154,6 +155,7 @@ log_depmsg = log_dep
|
||||||
|
|
||||||
_LOG_FILE_HANDLES = {} # holds open log handles
|
_LOG_FILE_HANDLES = {} # holds open log handles
|
||||||
|
|
||||||
|
|
||||||
def _open_log_file(filename):
|
def _open_log_file(filename):
|
||||||
"""
|
"""
|
||||||
Helper to open the log file (always in the log dir) and cache its
|
Helper to open the log file (always in the log dir) and cache its
|
||||||
|
|
@ -184,13 +186,14 @@ def log_file(msg, filename="game.log"):
|
||||||
Arbitrary file logger using threads.
|
Arbitrary file logger using threads.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
msg (str): String to append to logfile.
|
||||||
filename (str, optional): Defaults to 'game.log'. All logs
|
filename (str, optional): Defaults to 'game.log'. All logs
|
||||||
will appear in the logs directory and log entries will start
|
will appear in the logs directory and log entries will start
|
||||||
on new lines following datetime info.
|
on new lines following datetime info.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def callback(filehandle, msg):
|
def callback(filehandle, msg):
|
||||||
"Writing to file and flushing result"
|
"""Writing to file and flushing result"""
|
||||||
msg = "\n%s [-] %s" % (timeformat(), msg.strip())
|
msg = "\n%s [-] %s" % (timeformat(), msg.strip())
|
||||||
filehandle.write(msg)
|
filehandle.write(msg)
|
||||||
# since we don't close the handle, we need to flush
|
# since we don't close the handle, we need to flush
|
||||||
|
|
@ -199,7 +202,7 @@ def log_file(msg, filename="game.log"):
|
||||||
filehandle.flush()
|
filehandle.flush()
|
||||||
|
|
||||||
def errback(failure):
|
def errback(failure):
|
||||||
"Catching errors to normal log"
|
"""Catching errors to normal log"""
|
||||||
log_trace()
|
log_trace()
|
||||||
|
|
||||||
# save to server/logs/ directory
|
# save to server/logs/ directory
|
||||||
|
|
@ -230,7 +233,7 @@ def tail_log_file(filename, offset, nlines, callback=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def seek_file(filehandle, offset, nlines, callback):
|
def seek_file(filehandle, offset, nlines, callback):
|
||||||
"step backwards in chunks and stop only when we have enough lines"
|
"""step backwards in chunks and stop only when we have enough lines"""
|
||||||
lines_found = []
|
lines_found = []
|
||||||
buffer_size = 4098
|
buffer_size = 4098
|
||||||
block_count = -1
|
block_count = -1
|
||||||
|
|
@ -254,7 +257,7 @@ def tail_log_file(filename, offset, nlines, callback=None):
|
||||||
return lines_found
|
return lines_found
|
||||||
|
|
||||||
def errback(failure):
|
def errback(failure):
|
||||||
"Catching errors to normal log"
|
"""Catching errors to normal log"""
|
||||||
log_trace()
|
log_trace()
|
||||||
|
|
||||||
filehandle = _open_log_file(filename)
|
filehandle = _open_log_file(filename)
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,7 @@ else:
|
||||||
from cgi import escape
|
from cgi import escape
|
||||||
|
|
||||||
# hrule styles
|
# hrule styles
|
||||||
FRAME = 0
|
FRAME, ALL, NONE, HEADER = range(4)
|
||||||
ALL = 1
|
|
||||||
NONE = 2
|
|
||||||
HEADER = 3
|
|
||||||
|
|
||||||
# Table styles
|
# Table styles
|
||||||
DEFAULT = 10
|
DEFAULT = 10
|
||||||
|
|
@ -78,12 +75,13 @@ RANDOM = 20
|
||||||
|
|
||||||
_re = re.compile("\033\[[0-9;]*m")
|
_re = re.compile("\033\[[0-9;]*m")
|
||||||
|
|
||||||
|
|
||||||
def _ansi(method):
|
def _ansi(method):
|
||||||
"decorator for converting ansi in input"
|
"""decorator for converting ansi in input"""
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
def convert(inp):
|
def convert(inp):
|
||||||
if isinstance(inp, basestring):
|
if isinstance(inp, basestring):
|
||||||
return parse_ansi("{n%s{n" % inp)
|
return parse_ansi("|n%s|n" % inp)
|
||||||
elif hasattr(inp, '__iter__'):
|
elif hasattr(inp, '__iter__'):
|
||||||
li = []
|
li = []
|
||||||
for element in inp:
|
for element in inp:
|
||||||
|
|
@ -100,15 +98,16 @@ def _ansi(method):
|
||||||
return method(self, *args, **kwargs)
|
return method(self, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def _get_size(text):
|
def _get_size(text):
|
||||||
lines = text.split("\n")
|
lines = text.split("\n")
|
||||||
height = len(lines)
|
height = len(lines)
|
||||||
width = max([_str_block_width(line) for line in lines])
|
width = max([_str_block_width(line) for line in lines])
|
||||||
return (width, height)
|
return width, height
|
||||||
|
|
||||||
|
|
||||||
class PrettyTable(object):
|
class PrettyTable(object):
|
||||||
|
|
||||||
|
|
||||||
@_ansi
|
@_ansi
|
||||||
def __init__(self, field_names=None, **kwargs):
|
def __init__(self, field_names=None, **kwargs):
|
||||||
|
|
||||||
|
|
@ -153,9 +152,12 @@ class PrettyTable(object):
|
||||||
self._widths = []
|
self._widths = []
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
self._options = "start end fields header border sortby reversesort sort_key attributes format hrules vrules".split()
|
self._options = "start end fields header border sortby reversesort" \
|
||||||
self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split())
|
" sort_key attributes format hrules vrules".split()
|
||||||
self._options.extend("vertical_char horizontal_char junction_char header_style valign xhtml print_empty".split())
|
self._options.extend("int_format float_format padding_width "
|
||||||
|
"left_padding_width right_padding_width".split())
|
||||||
|
self._options.extend("vertical_char horizontal_char junction_char"
|
||||||
|
" header_style valign xhtml print_empty".split())
|
||||||
for option in self._options:
|
for option in self._options:
|
||||||
if option in kwargs:
|
if option in kwargs:
|
||||||
self._validate_option(option, kwargs[option])
|
self._validate_option(option, kwargs[option])
|
||||||
|
|
@ -283,31 +285,32 @@ class PrettyTable(object):
|
||||||
# Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings
|
# Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings
|
||||||
|
|
||||||
def _validate_option(self, option, val):
|
def _validate_option(self, option, val):
|
||||||
if option in ("field_names"):
|
if option in "field_names":
|
||||||
self._validate_field_names(val)
|
self._validate_field_names(val)
|
||||||
elif option in ("start", "end", "max_width", "padding_width", "left_padding_width", "right_padding_width", "format"):
|
elif option in ("start", "end", "max_width", "padding_width",
|
||||||
|
"left_padding_width", "right_padding_width", "format"):
|
||||||
self._validate_nonnegative_int(option, val)
|
self._validate_nonnegative_int(option, val)
|
||||||
elif option in ("sortby"):
|
elif option in "sortby":
|
||||||
self._validate_field_name(option, val)
|
self._validate_field_name(option, val)
|
||||||
elif option in ("sort_key"):
|
elif option in "sort_key":
|
||||||
self._validate_function(option, val)
|
self._validate_function(option, val)
|
||||||
elif option in ("hrules"):
|
elif option in "hrules":
|
||||||
self._validate_hrules(option, val)
|
self._validate_hrules(option, val)
|
||||||
elif option in ("vrules"):
|
elif option in "vrules":
|
||||||
self._validate_vrules(option, val)
|
self._validate_vrules(option, val)
|
||||||
elif option in ("fields"):
|
elif option in "fields":
|
||||||
self._validate_all_field_names(option, val)
|
self._validate_all_field_names(option, val)
|
||||||
elif option in ("header", "border", "reversesort", "xhtml", "print_empty"):
|
elif option in ("header", "border", "reversesort", "xhtml", "print_empty"):
|
||||||
self._validate_true_or_false(option, val)
|
self._validate_true_or_false(option, val)
|
||||||
elif option in ("header_style"):
|
elif option in "header_style":
|
||||||
self._validate_header_style(val)
|
self._validate_header_style(val)
|
||||||
elif option in ("int_format"):
|
elif option in "int_format":
|
||||||
self._validate_int_format(option, val)
|
self._validate_int_format(option, val)
|
||||||
elif option in ("float_format"):
|
elif option in "float_format":
|
||||||
self._validate_float_format(option, val)
|
self._validate_float_format(option, val)
|
||||||
elif option in ("vertical_char", "horizontal_char", "junction_char"):
|
elif option in ("vertical_char", "horizontal_char", "junction_char"):
|
||||||
self._validate_single_char(option, val)
|
self._validate_single_char(option, val)
|
||||||
elif option in ("attributes"):
|
elif option in "attributes":
|
||||||
self._validate_attributes(option, val)
|
self._validate_attributes(option, val)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unrecognised option: %s!" % option)
|
raise Exception("Unrecognised option: %s!" % option)
|
||||||
|
|
@ -318,12 +321,14 @@ class PrettyTable(object):
|
||||||
try:
|
try:
|
||||||
assert len(val) == len(self._field_names)
|
assert len(val) == len(self._field_names)
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._field_names)))
|
raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)"
|
||||||
|
% (len(val), len(self._field_names)))
|
||||||
if self._rows:
|
if self._rows:
|
||||||
try:
|
try:
|
||||||
assert len(val) == len(self._rows[0])
|
assert len(val) == len(self._rows[0])
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._rows[0])))
|
raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)"
|
||||||
|
% (len(val), len(self._rows[0])))
|
||||||
# Check for uniqueness
|
# Check for uniqueness
|
||||||
try:
|
try:
|
||||||
assert len(val) == len(set(val))
|
assert len(val) == len(set(val))
|
||||||
|
|
@ -436,6 +441,7 @@ class PrettyTable(object):
|
||||||
|
|
||||||
fields - list or tuple of field names"""
|
fields - list or tuple of field names"""
|
||||||
return self._field_names
|
return self._field_names
|
||||||
|
|
||||||
def _set_field_names(self, val):
|
def _set_field_names(self, val):
|
||||||
val = [self._unicode(x) for x in val]
|
val = [self._unicode(x) for x in val]
|
||||||
self._validate_option("field_names", val)
|
self._validate_option("field_names", val)
|
||||||
|
|
@ -461,30 +467,37 @@ class PrettyTable(object):
|
||||||
else:
|
else:
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._valign[field] = "t"
|
self._valign[field] = "t"
|
||||||
|
|
||||||
field_names = property(_get_field_names, _set_field_names)
|
field_names = property(_get_field_names, _set_field_names)
|
||||||
|
|
||||||
def _get_align(self):
|
def _get_align(self):
|
||||||
return self._align
|
return self._align
|
||||||
|
|
||||||
def _set_align(self, val):
|
def _set_align(self, val):
|
||||||
self._validate_align(val)
|
self._validate_align(val)
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._align[field] = val
|
self._align[field] = val
|
||||||
|
|
||||||
align = property(_get_align, _set_align)
|
align = property(_get_align, _set_align)
|
||||||
|
|
||||||
def _get_valign(self):
|
def _get_valign(self):
|
||||||
return self._valign
|
return self._valign
|
||||||
|
|
||||||
def _set_valign(self, val):
|
def _set_valign(self, val):
|
||||||
self._validate_valign(val)
|
self._validate_valign(val)
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._valign[field] = val
|
self._valign[field] = val
|
||||||
|
|
||||||
valign = property(_get_valign, _set_valign)
|
valign = property(_get_valign, _set_valign)
|
||||||
|
|
||||||
def _get_max_width(self):
|
def _get_max_width(self):
|
||||||
return self._max_width
|
return self._max_width
|
||||||
|
|
||||||
def _set_max_width(self, val):
|
def _set_max_width(self, val):
|
||||||
self._validate_option("max_width", val)
|
self._validate_option("max_width", val)
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._max_width[field] = val
|
self._max_width[field] = val
|
||||||
|
|
||||||
max_width = property(_get_max_width, _set_max_width)
|
max_width = property(_get_max_width, _set_max_width)
|
||||||
|
|
||||||
def _get_fields(self):
|
def _get_fields(self):
|
||||||
|
|
@ -494,9 +507,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
fields - list or tuple of field names to include in displays"""
|
fields - list or tuple of field names to include in displays"""
|
||||||
return self._fields
|
return self._fields
|
||||||
|
|
||||||
def _set_fields(self, val):
|
def _set_fields(self, val):
|
||||||
self._validate_option("fields", val)
|
self._validate_option("fields", val)
|
||||||
self._fields = val
|
self._fields = val
|
||||||
|
|
||||||
fields = property(_get_fields, _set_fields)
|
fields = property(_get_fields, _set_fields)
|
||||||
|
|
||||||
def _get_start(self):
|
def _get_start(self):
|
||||||
|
|
@ -510,6 +525,7 @@ class PrettyTable(object):
|
||||||
def _set_start(self, val):
|
def _set_start(self, val):
|
||||||
self._validate_option("start", val)
|
self._validate_option("start", val)
|
||||||
self._start = val
|
self._start = val
|
||||||
|
|
||||||
start = property(_get_start, _set_start)
|
start = property(_get_start, _set_start)
|
||||||
|
|
||||||
def _get_end(self):
|
def _get_end(self):
|
||||||
|
|
@ -519,9 +535,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
end - index of last data row to include in output PLUS ONE (list slice style)"""
|
end - index of last data row to include in output PLUS ONE (list slice style)"""
|
||||||
return self._end
|
return self._end
|
||||||
|
|
||||||
def _set_end(self, val):
|
def _set_end(self, val):
|
||||||
self._validate_option("end", val)
|
self._validate_option("end", val)
|
||||||
self._end = val
|
self._end = val
|
||||||
|
|
||||||
end = property(_get_end, _set_end)
|
end = property(_get_end, _set_end)
|
||||||
|
|
||||||
def _get_sortby(self):
|
def _get_sortby(self):
|
||||||
|
|
@ -531,9 +549,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
sortby - field name to sort by"""
|
sortby - field name to sort by"""
|
||||||
return self._sortby
|
return self._sortby
|
||||||
|
|
||||||
def _set_sortby(self, val):
|
def _set_sortby(self, val):
|
||||||
self._validate_option("sortby", val)
|
self._validate_option("sortby", val)
|
||||||
self._sortby = val
|
self._sortby = val
|
||||||
|
|
||||||
sortby = property(_get_sortby, _set_sortby)
|
sortby = property(_get_sortby, _set_sortby)
|
||||||
|
|
||||||
def _get_reversesort(self):
|
def _get_reversesort(self):
|
||||||
|
|
@ -543,9 +563,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
reveresort - set to True to sort by descending order, or False to sort by ascending order"""
|
reveresort - set to True to sort by descending order, or False to sort by ascending order"""
|
||||||
return self._reversesort
|
return self._reversesort
|
||||||
|
|
||||||
def _set_reversesort(self, val):
|
def _set_reversesort(self, val):
|
||||||
self._validate_option("reversesort", val)
|
self._validate_option("reversesort", val)
|
||||||
self._reversesort = val
|
self._reversesort = val
|
||||||
|
|
||||||
reversesort = property(_get_reversesort, _set_reversesort)
|
reversesort = property(_get_reversesort, _set_reversesort)
|
||||||
|
|
||||||
def _get_sort_key(self):
|
def _get_sort_key(self):
|
||||||
|
|
@ -555,9 +577,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
sort_key - a function which takes one argument and returns something to be sorted"""
|
sort_key - a function which takes one argument and returns something to be sorted"""
|
||||||
return self._sort_key
|
return self._sort_key
|
||||||
|
|
||||||
def _set_sort_key(self, val):
|
def _set_sort_key(self, val):
|
||||||
self._validate_option("sort_key", val)
|
self._validate_option("sort_key", val)
|
||||||
self._sort_key = val
|
self._sort_key = val
|
||||||
|
|
||||||
sort_key = property(_get_sort_key, _set_sort_key)
|
sort_key = property(_get_sort_key, _set_sort_key)
|
||||||
|
|
||||||
def _get_header(self):
|
def _get_header(self):
|
||||||
|
|
@ -567,9 +591,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
header - print a header showing field names (True or False)"""
|
header - print a header showing field names (True or False)"""
|
||||||
return self._header
|
return self._header
|
||||||
|
|
||||||
def _set_header(self, val):
|
def _set_header(self, val):
|
||||||
self._validate_option("header", val)
|
self._validate_option("header", val)
|
||||||
self._header = val
|
self._header = val
|
||||||
|
|
||||||
header = property(_get_header, _set_header)
|
header = property(_get_header, _set_header)
|
||||||
|
|
||||||
def _get_header_style(self):
|
def _get_header_style(self):
|
||||||
|
|
@ -579,9 +605,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)"""
|
header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)"""
|
||||||
return self._header_style
|
return self._header_style
|
||||||
|
|
||||||
def _set_header_style(self, val):
|
def _set_header_style(self, val):
|
||||||
self._validate_header_style(val)
|
self._validate_header_style(val)
|
||||||
self._header_style = val
|
self._header_style = val
|
||||||
|
|
||||||
header_style = property(_get_header_style, _set_header_style)
|
header_style = property(_get_header_style, _set_header_style)
|
||||||
|
|
||||||
def _get_border(self):
|
def _get_border(self):
|
||||||
|
|
@ -591,9 +619,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
border - print a border around the table (True or False)"""
|
border - print a border around the table (True or False)"""
|
||||||
return self._border
|
return self._border
|
||||||
|
|
||||||
def _set_border(self, val):
|
def _set_border(self, val):
|
||||||
self._validate_option("border", val)
|
self._validate_option("border", val)
|
||||||
self._border = val
|
self._border = val
|
||||||
|
|
||||||
border = property(_get_border, _set_border)
|
border = property(_get_border, _set_border)
|
||||||
|
|
||||||
def _get_hrules(self):
|
def _get_hrules(self):
|
||||||
|
|
@ -603,9 +633,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE"""
|
hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE"""
|
||||||
return self._hrules
|
return self._hrules
|
||||||
|
|
||||||
def _set_hrules(self, val):
|
def _set_hrules(self, val):
|
||||||
self._validate_option("hrules", val)
|
self._validate_option("hrules", val)
|
||||||
self._hrules = val
|
self._hrules = val
|
||||||
|
|
||||||
hrules = property(_get_hrules, _set_hrules)
|
hrules = property(_get_hrules, _set_hrules)
|
||||||
|
|
||||||
def _get_vrules(self):
|
def _get_vrules(self):
|
||||||
|
|
@ -615,9 +647,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
vrules - vertical rules style. Allowed values: FRAME, ALL, NONE"""
|
vrules - vertical rules style. Allowed values: FRAME, ALL, NONE"""
|
||||||
return self._vrules
|
return self._vrules
|
||||||
|
|
||||||
def _set_vrules(self, val):
|
def _set_vrules(self, val):
|
||||||
self._validate_option("vrules", val)
|
self._validate_option("vrules", val)
|
||||||
self._vrules = val
|
self._vrules = val
|
||||||
|
|
||||||
vrules = property(_get_vrules, _set_vrules)
|
vrules = property(_get_vrules, _set_vrules)
|
||||||
|
|
||||||
def _get_int_format(self):
|
def _get_int_format(self):
|
||||||
|
|
@ -626,10 +660,12 @@ class PrettyTable(object):
|
||||||
|
|
||||||
int_format - integer format string"""
|
int_format - integer format string"""
|
||||||
return self._int_format
|
return self._int_format
|
||||||
|
|
||||||
def _set_int_format(self, val):
|
def _set_int_format(self, val):
|
||||||
# self._validate_option("int_format", val)
|
# self._validate_option("int_format", val)
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._int_format[field] = val
|
self._int_format[field] = val
|
||||||
|
|
||||||
int_format = property(_get_int_format, _set_int_format)
|
int_format = property(_get_int_format, _set_int_format)
|
||||||
|
|
||||||
def _get_float_format(self):
|
def _get_float_format(self):
|
||||||
|
|
@ -638,10 +674,12 @@ class PrettyTable(object):
|
||||||
|
|
||||||
float_format - floating point format string"""
|
float_format - floating point format string"""
|
||||||
return self._float_format
|
return self._float_format
|
||||||
|
|
||||||
def _set_float_format(self, val):
|
def _set_float_format(self, val):
|
||||||
# self._validate_option("float_format", val)
|
# self._validate_option("float_format", val)
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
self._float_format[field] = val
|
self._float_format[field] = val
|
||||||
|
|
||||||
float_format = property(_get_float_format, _set_float_format)
|
float_format = property(_get_float_format, _set_float_format)
|
||||||
|
|
||||||
def _get_padding_width(self):
|
def _get_padding_width(self):
|
||||||
|
|
@ -651,9 +689,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
padding_width - number of spaces, must be a positive integer"""
|
padding_width - number of spaces, must be a positive integer"""
|
||||||
return self._padding_width
|
return self._padding_width
|
||||||
|
|
||||||
def _set_padding_width(self, val):
|
def _set_padding_width(self, val):
|
||||||
self._validate_option("padding_width", val)
|
self._validate_option("padding_width", val)
|
||||||
self._padding_width = val
|
self._padding_width = val
|
||||||
|
|
||||||
padding_width = property(_get_padding_width, _set_padding_width)
|
padding_width = property(_get_padding_width, _set_padding_width)
|
||||||
|
|
||||||
def _get_left_padding_width(self):
|
def _get_left_padding_width(self):
|
||||||
|
|
@ -663,9 +703,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
left_padding - number of spaces, must be a positive integer"""
|
left_padding - number of spaces, must be a positive integer"""
|
||||||
return self._left_padding_width
|
return self._left_padding_width
|
||||||
|
|
||||||
def _set_left_padding_width(self, val):
|
def _set_left_padding_width(self, val):
|
||||||
self._validate_option("left_padding_width", val)
|
self._validate_option("left_padding_width", val)
|
||||||
self._left_padding_width = val
|
self._left_padding_width = val
|
||||||
|
|
||||||
left_padding_width = property(_get_left_padding_width, _set_left_padding_width)
|
left_padding_width = property(_get_left_padding_width, _set_left_padding_width)
|
||||||
|
|
||||||
def _get_right_padding_width(self):
|
def _get_right_padding_width(self):
|
||||||
|
|
@ -675,9 +717,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
right_padding - number of spaces, must be a positive integer"""
|
right_padding - number of spaces, must be a positive integer"""
|
||||||
return self._right_padding_width
|
return self._right_padding_width
|
||||||
|
|
||||||
def _set_right_padding_width(self, val):
|
def _set_right_padding_width(self, val):
|
||||||
self._validate_option("right_padding_width", val)
|
self._validate_option("right_padding_width", val)
|
||||||
self._right_padding_width = val
|
self._right_padding_width = val
|
||||||
|
|
||||||
right_padding_width = property(_get_right_padding_width, _set_right_padding_width)
|
right_padding_width = property(_get_right_padding_width, _set_right_padding_width)
|
||||||
|
|
||||||
def _get_vertical_char(self):
|
def _get_vertical_char(self):
|
||||||
|
|
@ -687,10 +731,12 @@ class PrettyTable(object):
|
||||||
|
|
||||||
vertical_char - single character string used to draw vertical lines"""
|
vertical_char - single character string used to draw vertical lines"""
|
||||||
return self._vertical_char
|
return self._vertical_char
|
||||||
|
|
||||||
def _set_vertical_char(self, val):
|
def _set_vertical_char(self, val):
|
||||||
val = self._unicode(val)
|
val = self._unicode(val)
|
||||||
self._validate_option("vertical_char", val)
|
self._validate_option("vertical_char", val)
|
||||||
self._vertical_char = val
|
self._vertical_char = val
|
||||||
|
|
||||||
vertical_char = property(_get_vertical_char, _set_vertical_char)
|
vertical_char = property(_get_vertical_char, _set_vertical_char)
|
||||||
|
|
||||||
def _get_horizontal_char(self):
|
def _get_horizontal_char(self):
|
||||||
|
|
@ -700,10 +746,12 @@ class PrettyTable(object):
|
||||||
|
|
||||||
horizontal_char - single character string used to draw horizontal lines"""
|
horizontal_char - single character string used to draw horizontal lines"""
|
||||||
return self._horizontal_char
|
return self._horizontal_char
|
||||||
|
|
||||||
def _set_horizontal_char(self, val):
|
def _set_horizontal_char(self, val):
|
||||||
val = self._unicode(val)
|
val = self._unicode(val)
|
||||||
self._validate_option("horizontal_char", val)
|
self._validate_option("horizontal_char", val)
|
||||||
self._horizontal_char = val
|
self._horizontal_char = val
|
||||||
|
|
||||||
horizontal_char = property(_get_horizontal_char, _set_horizontal_char)
|
horizontal_char = property(_get_horizontal_char, _set_horizontal_char)
|
||||||
|
|
||||||
def _get_junction_char(self):
|
def _get_junction_char(self):
|
||||||
|
|
@ -713,10 +761,12 @@ class PrettyTable(object):
|
||||||
|
|
||||||
junction_char - single character string used to draw line junctions"""
|
junction_char - single character string used to draw line junctions"""
|
||||||
return self._junction_char
|
return self._junction_char
|
||||||
|
|
||||||
def _set_junction_char(self, val):
|
def _set_junction_char(self, val):
|
||||||
val = self._unicode(val)
|
val = self._unicode(val)
|
||||||
self._validate_option("vertical_char", val)
|
self._validate_option("vertical_char", val)
|
||||||
self._junction_char = val
|
self._junction_char = val
|
||||||
|
|
||||||
junction_char = property(_get_junction_char, _set_junction_char)
|
junction_char = property(_get_junction_char, _set_junction_char)
|
||||||
|
|
||||||
def _get_format(self):
|
def _get_format(self):
|
||||||
|
|
@ -726,9 +776,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
format - True or False"""
|
format - True or False"""
|
||||||
return self._format
|
return self._format
|
||||||
|
|
||||||
def _set_format(self, val):
|
def _set_format(self, val):
|
||||||
self._validate_option("format", val)
|
self._validate_option("format", val)
|
||||||
self._format = val
|
self._format = val
|
||||||
|
|
||||||
format = property(_get_format, _set_format)
|
format = property(_get_format, _set_format)
|
||||||
|
|
||||||
def _get_print_empty(self):
|
def _get_print_empty(self):
|
||||||
|
|
@ -738,9 +790,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
print_empty - True or False"""
|
print_empty - True or False"""
|
||||||
return self._print_empty
|
return self._print_empty
|
||||||
|
|
||||||
def _set_print_empty(self, val):
|
def _set_print_empty(self, val):
|
||||||
self._validate_option("print_empty", val)
|
self._validate_option("print_empty", val)
|
||||||
self._print_empty = val
|
self._print_empty = val
|
||||||
|
|
||||||
print_empty = property(_get_print_empty, _set_print_empty)
|
print_empty = property(_get_print_empty, _set_print_empty)
|
||||||
|
|
||||||
def _get_attributes(self):
|
def _get_attributes(self):
|
||||||
|
|
@ -750,9 +804,11 @@ class PrettyTable(object):
|
||||||
|
|
||||||
attributes - dictionary of attributes"""
|
attributes - dictionary of attributes"""
|
||||||
return self._attributes
|
return self._attributes
|
||||||
|
|
||||||
def _set_attributes(self, val):
|
def _set_attributes(self, val):
|
||||||
self._validate_option("attributes", val)
|
self._validate_option("attributes", val)
|
||||||
self._attributes = val
|
self._attributes = val
|
||||||
|
|
||||||
attributes = property(_get_attributes, _set_attributes)
|
attributes = property(_get_attributes, _set_attributes)
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
|
|
@ -846,7 +902,8 @@ class PrettyTable(object):
|
||||||
has fields"""
|
has fields"""
|
||||||
|
|
||||||
if self._field_names and len(row) != len(self._field_names):
|
if self._field_names and len(row) != len(self._field_names):
|
||||||
raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names)))
|
raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)"
|
||||||
|
% (len(row), len(self._field_names)))
|
||||||
if not self._field_names:
|
if not self._field_names:
|
||||||
self.field_names = [("Field %d" % (n+1)) for n in range(0, len(row))]
|
self.field_names = [("Field %d" % (n+1)) for n in range(0, len(row))]
|
||||||
self._rows.append(list(row))
|
self._rows.append(list(row))
|
||||||
|
|
@ -860,7 +917,7 @@ class PrettyTable(object):
|
||||||
row_index - The index of the row you want to delete. Indexing starts at 0."""
|
row_index - The index of the row you want to delete. Indexing starts at 0."""
|
||||||
|
|
||||||
if row_index > len(self._rows)-1:
|
if row_index > len(self._rows)-1:
|
||||||
raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows)))
|
raise Exception("Can't delete row at index %d, table only has %d rows!" % (row_index, len(self._rows)))
|
||||||
del self._rows[row_index]
|
del self._rows[row_index]
|
||||||
|
|
||||||
@_ansi
|
@_ansi
|
||||||
|
|
@ -1145,14 +1202,14 @@ class PrettyTable(object):
|
||||||
|
|
||||||
valign = self._valign[field]
|
valign = self._valign[field]
|
||||||
lines = value.split("\n")
|
lines = value.split("\n")
|
||||||
dHeight = row_height - len(lines)
|
dheight = row_height - len(lines)
|
||||||
if dHeight:
|
if dheight:
|
||||||
if valign == "m":
|
if valign == "m":
|
||||||
lines = [""] * (dHeight // 2) + lines + [""] * (dHeight - (dHeight // 2))
|
lines = [""] * (dheight // 2) + lines + [""] * (dheight - (dheight // 2))
|
||||||
elif valign == "b":
|
elif valign == "b":
|
||||||
lines = [""] * dHeight + lines
|
lines = [""] * dheight + lines
|
||||||
else:
|
else:
|
||||||
lines = lines + [""] * dHeight
|
lines += [""] * dheight
|
||||||
|
|
||||||
y = 0
|
y = 0
|
||||||
for l in lines:
|
for l in lines:
|
||||||
|
|
@ -1227,8 +1284,7 @@ class PrettyTable(object):
|
||||||
else:
|
else:
|
||||||
linebreak = "<br>"
|
linebreak = "<br>"
|
||||||
|
|
||||||
open_tag = []
|
open_tag = ["<table"]
|
||||||
open_tag.append("<table")
|
|
||||||
if options["attributes"]:
|
if options["attributes"]:
|
||||||
for attr_name in options["attributes"]:
|
for attr_name in options["attributes"]:
|
||||||
open_tag.append(" %s=\"%s\"" % (attr_name, options["attributes"][attr_name]))
|
open_tag.append(" %s=\"%s\"" % (attr_name, options["attributes"][attr_name]))
|
||||||
|
|
@ -1268,8 +1324,7 @@ class PrettyTable(object):
|
||||||
else:
|
else:
|
||||||
linebreak = "<br>"
|
linebreak = "<br>"
|
||||||
|
|
||||||
open_tag = []
|
open_tag = ["<table"]
|
||||||
open_tag.append("<table")
|
|
||||||
if options["border"]:
|
if options["border"]:
|
||||||
if options["hrules"] == ALL and options["vrules"] == ALL:
|
if options["hrules"] == ALL and options["vrules"] == ALL:
|
||||||
open_tag.append(" frame=\"box\" rules=\"all\"")
|
open_tag.append(" frame=\"box\" rules=\"all\"")
|
||||||
|
|
@ -1297,7 +1352,9 @@ class PrettyTable(object):
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
if options["fields"] and field not in options["fields"]:
|
if options["fields"] and field not in options["fields"]:
|
||||||
continue
|
continue
|
||||||
lines.append(" <th style=\"padding-left: %dem; padding-right: %dem; text-align: center\">%s</th>" % (lpad, rpad, escape(field).replace("\n", linebreak)))
|
lines.append(" <th style=\"padding-left: %dem; padding-right: "
|
||||||
|
"%dem; text-align: center\">%s</th>"
|
||||||
|
% (lpad, rpad, escape(field).replace("\n", linebreak)))
|
||||||
lines.append(" </tr>")
|
lines.append(" </tr>")
|
||||||
|
|
||||||
# Data
|
# Data
|
||||||
|
|
@ -1306,14 +1363,16 @@ class PrettyTable(object):
|
||||||
aligns = []
|
aligns = []
|
||||||
valigns = []
|
valigns = []
|
||||||
for field in self._field_names:
|
for field in self._field_names:
|
||||||
aligns.append({ "l" : "left", "r" : "right", "c" : "center" }[self._align[field]])
|
aligns.append(dict(l="left", r="right", c="center")[self._align[field]])
|
||||||
valigns.append({"t" : "top", "m" : "middle", "b" : "bottom"}[self._valign[field]])
|
valigns.append(dict(t="top", m="middle", b="bottom")[self._valign[field]])
|
||||||
for row in formatted_rows:
|
for row in formatted_rows:
|
||||||
lines.append(" <tr>")
|
lines.append(" <tr>")
|
||||||
for field, datum, align, valign in zip(self._field_names, row, aligns, valigns):
|
for field, datum, align, valign in zip(self._field_names, row, aligns, valigns):
|
||||||
if options["fields"] and field not in options["fields"]:
|
if options["fields"] and field not in options["fields"]:
|
||||||
continue
|
continue
|
||||||
lines.append(" <td style=\"padding-left: %dem; padding-right: %dem; text-align: %s; vertical-align: %s\">%s</td>" % (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak)))
|
lines.append(" <td style=\"padding-left: %dem; padding-right: %dem; "
|
||||||
|
"text-align: %s; vertical-align: %s\">%s</td>"
|
||||||
|
% (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak)))
|
||||||
lines.append(" </tr>")
|
lines.append(" </tr>")
|
||||||
lines.append("</table>")
|
lines.append("</table>")
|
||||||
|
|
||||||
|
|
@ -1323,6 +1382,7 @@ class PrettyTable(object):
|
||||||
# UNICODE WIDTH FUNCTIONS #
|
# UNICODE WIDTH FUNCTIONS #
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
|
|
||||||
def _char_block_width(char):
|
def _char_block_width(char):
|
||||||
# Basic Latin, which is probably the most common case
|
# Basic Latin, which is probably the most common case
|
||||||
# if char in xrange(0x0021, 0x007e):
|
# if char in xrange(0x0021, 0x007e):
|
||||||
|
|
@ -1356,6 +1416,7 @@ def _char_block_width(char):
|
||||||
# Take a guess
|
# Take a guess
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def _str_block_width(val):
|
def _str_block_width(val):
|
||||||
|
|
||||||
return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val))))
|
return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val))))
|
||||||
|
|
@ -1364,6 +1425,7 @@ def _str_block_width(val):
|
||||||
# TABLE FACTORIES #
|
# TABLE FACTORIES #
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
|
|
||||||
def from_csv(fp, field_names=None, **kwargs):
|
def from_csv(fp, field_names=None, **kwargs):
|
||||||
|
|
||||||
dialect = csv.Sniffer().sniff(fp.read(1024))
|
dialect = csv.Sniffer().sniff(fp.read(1024))
|
||||||
|
|
@ -1381,6 +1443,7 @@ def from_csv(fp, field_names = None, **kwargs):
|
||||||
|
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
def from_db_cursor(cursor, **kwargs):
|
def from_db_cursor(cursor, **kwargs):
|
||||||
|
|
||||||
if cursor.description:
|
if cursor.description:
|
||||||
|
|
@ -1390,6 +1453,7 @@ def from_db_cursor(cursor, **kwargs):
|
||||||
table.add_row(row)
|
table.add_row(row)
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
class TableHandler(HTMLParser):
|
class TableHandler(HTMLParser):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
@ -1425,7 +1489,6 @@ class TableHandler(HTMLParser):
|
||||||
self.last_content = " "
|
self.last_content = " "
|
||||||
self.active = None
|
self.active = None
|
||||||
|
|
||||||
|
|
||||||
def handle_data(self, data):
|
def handle_data(self, data):
|
||||||
self.last_content += data
|
self.last_content += data
|
||||||
|
|
||||||
|
|
@ -1437,10 +1500,10 @@ class TableHandler(HTMLParser):
|
||||||
for row in self.rows:
|
for row in self.rows:
|
||||||
if len(row[0]) < self.max_row_width:
|
if len(row[0]) < self.max_row_width:
|
||||||
appends = self.max_row_width - len(row[0])
|
appends = self.max_row_width - len(row[0])
|
||||||
for i in range(1,appends):
|
for _ in range(1, appends):
|
||||||
row[0].append("-")
|
row[0].append("-")
|
||||||
|
|
||||||
if row[1] == True:
|
if row[1] is True:
|
||||||
self.make_fields_unique(row[0])
|
self.make_fields_unique(row[0])
|
||||||
table.field_names = row[0]
|
table.field_names = row[0]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1456,6 +1519,7 @@ class TableHandler(HTMLParser):
|
||||||
if fields[i] == fields[j]:
|
if fields[i] == fields[j]:
|
||||||
fields[j] += "'"
|
fields[j] += "'"
|
||||||
|
|
||||||
|
|
||||||
def from_html(html_code, **kwargs):
|
def from_html(html_code, **kwargs):
|
||||||
"""
|
"""
|
||||||
Generates a list of PrettyTables from a string of HTML code. Each <table> in
|
Generates a list of PrettyTables from a string of HTML code. Each <table> in
|
||||||
|
|
@ -1466,6 +1530,7 @@ def from_html(html_code, **kwargs):
|
||||||
parser.feed(html_code)
|
parser.feed(html_code)
|
||||||
return parser.tables
|
return parser.tables
|
||||||
|
|
||||||
|
|
||||||
def from_html_one(html_code, **kwargs):
|
def from_html_one(html_code, **kwargs):
|
||||||
"""
|
"""
|
||||||
Generates a PrettyTables from a string of HTML code which contains only a
|
Generates a PrettyTables from a string of HTML code which contains only a
|
||||||
|
|
@ -1483,6 +1548,7 @@ def from_html_one(html_code, **kwargs):
|
||||||
# MAIN (TEST FUNCTION) #
|
# MAIN (TEST FUNCTION) #
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
|
x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_c
|
||||||
Tag = ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
Tag = ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Search manager-wrappers
|
# Search manager-wrappers
|
||||||
#------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
#
|
#
|
||||||
# Search objects as a character
|
# Search objects as a character
|
||||||
|
|
@ -199,10 +199,16 @@ help_entries = search_help
|
||||||
|
|
||||||
def search_object_attribute(key=None, category=None, value=None, strvalue=None):
|
def search_object_attribute(key=None, category=None, value=None, strvalue=None):
|
||||||
return ObjectDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
return ObjectDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
||||||
|
|
||||||
|
|
||||||
def search_player_attribute(key=None, category=None, value=None, strvalue=None):
|
def search_player_attribute(key=None, category=None, value=None, strvalue=None):
|
||||||
return PlayerDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
return PlayerDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
||||||
|
|
||||||
|
|
||||||
def search_script_attribute(key=None, category=None, value=None, strvalue=None):
|
def search_script_attribute(key=None, category=None, value=None, strvalue=None):
|
||||||
return ScriptDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
return ScriptDB.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
||||||
|
|
||||||
|
|
||||||
def search_channel_attribute(key=None, category=None, value=None, strvalue=None):
|
def search_channel_attribute(key=None, category=None, value=None, strvalue=None):
|
||||||
return Channel.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
return Channel.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue)
|
||||||
|
|
||||||
|
|
@ -218,6 +224,8 @@ search_attribute_object = ObjectDB.objects.get_attribute
|
||||||
|
|
||||||
# Note that this returns the object attached to the tag, not the tag
|
# Note that this returns the object attached to the tag, not the tag
|
||||||
# object itself (this is usually what you want)
|
# object itself (this is usually what you want)
|
||||||
|
|
||||||
|
|
||||||
def search_object_by_tag(key=None, category=None):
|
def search_object_by_tag(key=None, category=None):
|
||||||
"""
|
"""
|
||||||
Find object based on tag or category.
|
Find object based on tag or category.
|
||||||
|
|
@ -236,6 +244,8 @@ def search_object_by_tag(key=None, category=None):
|
||||||
"""
|
"""
|
||||||
return ObjectDB.objects.get_by_tag(key=key, category=category)
|
return ObjectDB.objects.get_by_tag(key=key, category=category)
|
||||||
search_tag = search_object_by_tag # this is the most common case
|
search_tag = search_object_by_tag # this is the most common case
|
||||||
|
|
||||||
|
|
||||||
def search_player_tag(key=None, category=None):
|
def search_player_tag(key=None, category=None):
|
||||||
"""
|
"""
|
||||||
Find player based on tag or category.
|
Find player based on tag or category.
|
||||||
|
|
@ -253,6 +263,8 @@ def search_player_tag(key=None, category=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return PlayerDB.objects.get_by_tag(key=key, category=category)
|
return PlayerDB.objects.get_by_tag(key=key, category=category)
|
||||||
|
|
||||||
|
|
||||||
def search_script_tag(key=None, category=None):
|
def search_script_tag(key=None, category=None):
|
||||||
"""
|
"""
|
||||||
Find script based on tag or category.
|
Find script based on tag or category.
|
||||||
|
|
@ -270,6 +282,8 @@ def search_script_tag(key=None, category=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ScriptDB.objects.get_by_tag(key=key, category=category)
|
return ScriptDB.objects.get_by_tag(key=key, category=category)
|
||||||
|
|
||||||
|
|
||||||
def search_channel_tag(key=None, category=None):
|
def search_channel_tag(key=None, category=None):
|
||||||
"""
|
"""
|
||||||
Find channel based on tag or category.
|
Find channel based on tag or category.
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ def _get_prototype(dic, prot, protparents):
|
||||||
prot.pop("prototype", None) # we don't need this anymore
|
prot.pop("prototype", None) # we don't need this anymore
|
||||||
return prot
|
return prot
|
||||||
|
|
||||||
|
|
||||||
def _batch_create_object(*objparams):
|
def _batch_create_object(*objparams):
|
||||||
"""
|
"""
|
||||||
This is a cut-down version of the create_object() function,
|
This is a cut-down version of the create_object() function,
|
||||||
|
|
@ -141,7 +142,7 @@ def _batch_create_object(*objparams):
|
||||||
so make sure the spawned Typeclass works before using this!
|
so make sure the spawned Typeclass works before using this!
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
objsparams (any): Aach argument should be a tuple of arguments
|
objsparams (any): Each argument should be a tuple of arguments
|
||||||
for the respective creation/add handlers in the following
|
for the respective creation/add handlers in the following
|
||||||
order: (create, permissions, locks, aliases, nattributes,
|
order: (create, permissions, locks, aliases, nattributes,
|
||||||
attributes)
|
attributes)
|
||||||
|
|
@ -153,7 +154,8 @@ def _batch_create_object(*objparams):
|
||||||
# bulk create all objects in one go
|
# bulk create all objects in one go
|
||||||
|
|
||||||
# unfortunately this doesn't work since bulk_create doesn't creates pks;
|
# unfortunately this doesn't work since bulk_create doesn't creates pks;
|
||||||
# the result are double objects at the next stage
|
# the result would be duplicate objects at the next stage, so we comment
|
||||||
|
# it out for now:
|
||||||
# dbobjs = _ObjectDB.objects.bulk_create(dbobjs)
|
# dbobjs = _ObjectDB.objects.bulk_create(dbobjs)
|
||||||
|
|
||||||
dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams]
|
dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams]
|
||||||
|
|
@ -201,8 +203,8 @@ def spawn(*prototypes, **kwargs):
|
||||||
if not protmodules and hasattr(settings, "PROTOTYPE_MODULES"):
|
if not protmodules and hasattr(settings, "PROTOTYPE_MODULES"):
|
||||||
protmodules = make_iter(settings.PROTOTYPE_MODULES)
|
protmodules = make_iter(settings.PROTOTYPE_MODULES)
|
||||||
for prototype_module in protmodules:
|
for prototype_module in protmodules:
|
||||||
protparents.update(dict((key, val)
|
protparents.update(dict((key, val) for key, val in
|
||||||
for key, val in all_from_module(prototype_module).items() if isinstance(val, dict)))
|
all_from_module(prototype_module).items() if isinstance(val, dict)))
|
||||||
# overload module's protparents with specifically given protparents
|
# overload module's protparents with specifically given protparents
|
||||||
protparents.update(kwargs.get("prototype_parents", {}))
|
protparents.update(kwargs.get("prototype_parents", {}))
|
||||||
for key, prototype in protparents.items():
|
for key, prototype in protparents.items():
|
||||||
|
|
@ -300,4 +302,6 @@ if __name__ == "__main__":
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# test
|
# test
|
||||||
print([o.key for o in spawn(protparents["GOBLIN"], protparents["GOBLIN_ARCHWIZARD"], prototype_parents=protparents)])
|
print([o.key for o in spawn(protparents["GOBLIN"],
|
||||||
|
protparents["GOBLIN_ARCHWIZARD"],
|
||||||
|
prototype_parents=protparents)])
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,11 @@ class ANSIStringTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
Make sure the ANSIString is always constructed correctly.
|
Make sure the ANSIString is always constructed correctly.
|
||||||
"""
|
"""
|
||||||
clean = u'This isA{r testTest'
|
clean = u'This isA|r testTest'
|
||||||
encoded = u'\x1b[1m\x1b[32mThis is\x1b[1m\x1b[31mA{r test\x1b[0mTest\x1b[0m'
|
encoded = u'\x1b[1m\x1b[32mThis is\x1b[1m\x1b[31mA|r test\x1b[0mTest\x1b[0m'
|
||||||
target = ANSIString(r'{gThis is{rA{{r test{nTest{n')
|
target = ANSIString(r'|gThis is|rA||r test|nTest|n')
|
||||||
char_table = [9, 10, 11, 12, 13, 14, 15, 25, 26, 27, 28, 29, 30, 31,
|
char_table = [9, 10, 11, 12, 13, 14, 15, 25, 26, 27, 28, 29, 30, 31, 32, 37, 38, 39, 40]
|
||||||
32, 37, 38, 39, 40]
|
code_table = [0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 17, 18, 19, 20, 21, 22, 23, 24, 33, 34, 35, 36, 41, 42, 43, 44]
|
||||||
code_table = [0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 17, 18, 19, 20, 21, 22,
|
|
||||||
23, 24, 33, 34, 35, 36, 41, 42, 43, 44]
|
|
||||||
self.checker(target, encoded, clean)
|
self.checker(target, encoded, clean)
|
||||||
self.table_check(target, char_table, code_table)
|
self.table_check(target, char_table, code_table)
|
||||||
self.checker(ANSIString(target), encoded, clean)
|
self.checker(ANSIString(target), encoded, clean)
|
||||||
|
|
@ -54,7 +52,7 @@ class ANSIStringTestCase(TestCase):
|
||||||
Verifies that slicing an ANSIString results in expected color code
|
Verifies that slicing an ANSIString results in expected color code
|
||||||
distribution.
|
distribution.
|
||||||
"""
|
"""
|
||||||
target = ANSIString(r'{gTest{rTest{n')
|
target = ANSIString(r'|gTest|rTest|n')
|
||||||
result = target[:3]
|
result = target[:3]
|
||||||
self.checker(result, u'\x1b[1m\x1b[32mTes', u'Tes')
|
self.checker(result, u'\x1b[1m\x1b[32mTes', u'Tes')
|
||||||
result = target[:4]
|
result = target[:4]
|
||||||
|
|
@ -80,7 +78,7 @@ class ANSIStringTestCase(TestCase):
|
||||||
Verifies that re.split and .split behave similarly and that color
|
Verifies that re.split and .split behave similarly and that color
|
||||||
codes end up where they should.
|
codes end up where they should.
|
||||||
"""
|
"""
|
||||||
target = ANSIString("{gThis is {nA split string{g")
|
target = ANSIString("|gThis is |nA split string|g")
|
||||||
first = (u'\x1b[1m\x1b[32mThis is \x1b[0m', u'This is ')
|
first = (u'\x1b[1m\x1b[32mThis is \x1b[0m', u'This is ')
|
||||||
second = (u'\x1b[1m\x1b[32m\x1b[0m split string\x1b[1m\x1b[32m',
|
second = (u'\x1b[1m\x1b[32m\x1b[0m split string\x1b[1m\x1b[32m',
|
||||||
u' split string')
|
u' split string')
|
||||||
|
|
@ -96,9 +94,9 @@ class ANSIStringTestCase(TestCase):
|
||||||
Verify that joining a set of ANSIStrings works.
|
Verify that joining a set of ANSIStrings works.
|
||||||
"""
|
"""
|
||||||
# This isn't the desired behavior, but the expected one. Python
|
# This isn't the desired behavior, but the expected one. Python
|
||||||
# concatinates the in-memory representation with the built-in string's
|
# concatenates the in-memory representation with the built-in string's
|
||||||
# join.
|
# join.
|
||||||
l = [ANSIString("{gTest{r") for s in range(0, 3)]
|
l = [ANSIString("|gTest|r") for _ in range(0, 3)]
|
||||||
# Force the generator to be evaluated.
|
# Force the generator to be evaluated.
|
||||||
result = "".join(l)
|
result = "".join(l)
|
||||||
self.assertEqual(unicode(result), u'TestTestTest')
|
self.assertEqual(unicode(result), u'TestTestTest')
|
||||||
|
|
@ -112,14 +110,14 @@ class ANSIStringTestCase(TestCase):
|
||||||
Make sure that length reporting on ANSIStrings does not include
|
Make sure that length reporting on ANSIStrings does not include
|
||||||
ANSI codes.
|
ANSI codes.
|
||||||
"""
|
"""
|
||||||
self.assertEqual(len(ANSIString('{gTest{n')), 4)
|
self.assertEqual(len(ANSIString('|gTest|n')), 4)
|
||||||
|
|
||||||
def test_capitalize(self):
|
def test_capitalize(self):
|
||||||
"""
|
"""
|
||||||
Make sure that capitalization works. This is the simplest of the
|
Make sure that capitalization works. This is the simplest of the
|
||||||
_transform functions.
|
_transform functions.
|
||||||
"""
|
"""
|
||||||
target = ANSIString('{gtest{n')
|
target = ANSIString('|gtest|n')
|
||||||
result = u'\x1b[1m\x1b[32mTest\x1b[0m'
|
result = u'\x1b[1m\x1b[32mTest\x1b[0m'
|
||||||
self.checker(target.capitalize(), result, u'Test')
|
self.checker(target.capitalize(), result, u'Test')
|
||||||
|
|
||||||
|
|
@ -127,8 +125,8 @@ class ANSIStringTestCase(TestCase):
|
||||||
"""
|
"""
|
||||||
Make sure MXP tags are not treated like ANSI codes, but normal text.
|
Make sure MXP tags are not treated like ANSI codes, but normal text.
|
||||||
"""
|
"""
|
||||||
mxp1 = "{lclook{ltat{le"
|
mxp1 = "|lclook|ltat|le"
|
||||||
mxp2 = "Start to {lclook here{ltclick somewhere here{le first"
|
mxp2 = "Start to |lclook here|ltclick somewhere here|le first"
|
||||||
self.assertEqual(15, len(ANSIString(mxp1)))
|
self.assertEqual(15, len(ANSIString(mxp1)))
|
||||||
self.assertEqual(53, len(ANSIString(mxp2)))
|
self.assertEqual(53, len(ANSIString(mxp2)))
|
||||||
# These would indicate an issue with the tables.
|
# These would indicate an issue with the tables.
|
||||||
|
|
@ -139,17 +137,15 @@ class ANSIStringTestCase(TestCase):
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
"""
|
"""
|
||||||
Verify concatination works correctly.
|
Verify concatenation works correctly.
|
||||||
"""
|
"""
|
||||||
a = ANSIString("{gTest")
|
a = ANSIString("|gTest")
|
||||||
b = ANSIString("{cString{n")
|
b = ANSIString("|cString|n")
|
||||||
c = a + b
|
c = a + b
|
||||||
result = u'\x1b[1m\x1b[32mTest\x1b[1m\x1b[36mString\x1b[0m'
|
result = u'\x1b[1m\x1b[32mTest\x1b[1m\x1b[36mString\x1b[0m'
|
||||||
self.checker(c, result, u'TestString')
|
self.checker(c, result, u'TestString')
|
||||||
char_table = [9, 10, 11, 12, 22, 23, 24, 25, 26, 27]
|
char_table = [9, 10, 11, 12, 22, 23, 24, 25, 26, 27]
|
||||||
code_table = [
|
code_table = [0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28, 29, 30, 31]
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 28, 29, 30, 31
|
|
||||||
]
|
|
||||||
self.table_check(c, char_table, code_table)
|
self.table_check(c, char_table, code_table)
|
||||||
|
|
||||||
def test_strip(self):
|
def test_strip(self):
|
||||||
|
|
@ -231,10 +227,10 @@ class TestMLen(TestCase):
|
||||||
self.assertEqual(utils.m_len('|lclook|ltat|le'), 2)
|
self.assertEqual(utils.m_len('|lclook|ltat|le'), 2)
|
||||||
|
|
||||||
def test_mxp_ansi_string(self):
|
def test_mxp_ansi_string(self):
|
||||||
self.assertEqual(utils.m_len(ANSIString('|lcl|gook|ltat|le{n')), 2)
|
self.assertEqual(utils.m_len(ANSIString('|lcl|gook|ltat|le|n')), 2)
|
||||||
|
|
||||||
def test_non_mxp_ansi_string(self):
|
def test_non_mxp_ansi_string(self):
|
||||||
self.assertEqual(utils.m_len(ANSIString('{gHello{n')), 5)
|
self.assertEqual(utils.m_len(ANSIString('{gHello{n')), 5) # TODO - cause this to fail by default.
|
||||||
self.assertEqual(utils.m_len(ANSIString('|gHello|n')), 5)
|
self.assertEqual(utils.m_len(ANSIString('|gHello|n')), 5)
|
||||||
|
|
||||||
def test_list(self):
|
def test_list(self):
|
||||||
|
|
@ -246,6 +242,7 @@ class TestMLen(TestCase):
|
||||||
|
|
||||||
from .text2html import TextToHTMLparser
|
from .text2html import TextToHTMLparser
|
||||||
|
|
||||||
|
|
||||||
class TestTextToHTMLparser(TestCase):
|
class TestTextToHTMLparser(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.parser = TextToHTMLparser()
|
self.parser = TextToHTMLparser()
|
||||||
|
|
@ -279,19 +276,24 @@ class TestTextToHTMLparser(TestCase):
|
||||||
|
|
||||||
def test_url_chars_colon(self):
|
def test_url_chars_colon(self):
|
||||||
self.assertEqual(self.parser.convert_urls('https://example.com:8000/login/'),
|
self.assertEqual(self.parser.convert_urls('https://example.com:8000/login/'),
|
||||||
'<a href="https://example.com:8000/login/" target="_blank">https://example.com:8000/login/</a>')
|
'<a href="https://example.com:8000/login/" target="_blank">'
|
||||||
|
'https://example.com:8000/login/</a>')
|
||||||
|
|
||||||
def test_url_chars_querystring(self):
|
def test_url_chars_querystring(self):
|
||||||
self.assertEqual(self.parser.convert_urls('https://example.com/submitform?field1=val1+val3&field2=val2'),
|
self.assertEqual(self.parser.convert_urls('https://example.com/submitform?field1=val1+val3&field2=val2'),
|
||||||
'<a href="https://example.com/submitform?field1=val1+val3&field2=val2" target="_blank">https://example.com/submitform?field1=val1+val3&field2=val2</a>')
|
'<a href="https://example.com/submitform?field1=val1+val3&field2=val2" target="_blank">'
|
||||||
|
'https://example.com/submitform?field1=val1+val3&field2=val2</a>')
|
||||||
|
|
||||||
def test_url_chars_anchor(self):
|
def test_url_chars_anchor(self):
|
||||||
self.assertEqual(self.parser.convert_urls('http://www.example.com/menu#section_1'),
|
self.assertEqual(self.parser.convert_urls('http://www.example.com/menu#section_1'),
|
||||||
'<a href="http://www.example.com/menu#section_1" target="_blank">http://www.example.com/menu#section_1</a>')
|
'<a href="http://www.example.com/menu#section_1" target="_blank">'
|
||||||
|
'http://www.example.com/menu#section_1</a>')
|
||||||
|
|
||||||
def test_url_chars_exclam(self):
|
def test_url_chars_exclam(self):
|
||||||
self.assertEqual(self.parser.convert_urls('https://groups.google.com/forum/?fromgroups#!categories/evennia/ainneve'),
|
self.assertEqual(self.parser.convert_urls('https://groups.google.com/forum/'
|
||||||
'<a href="https://groups.google.com/forum/?fromgroups#!categories/evennia/ainneve" target="_blank">https://groups.google.com/forum/?fromgroups#!categories/evennia/ainneve</a>')
|
'?fromgroups#!categories/evennia/ainneve'),
|
||||||
|
'<a href="https://groups.google.com/forum/?fromgroups#!categories/evennia/ainneve"'
|
||||||
|
' target="_blank">https://groups.google.com/forum/?fromgroups#!categories/evennia/ainneve</a>')
|
||||||
|
|
||||||
def test_url_edge_leadingw(self):
|
def test_url_edge_leadingw(self):
|
||||||
self.assertEqual(self.parser.convert_urls('wwww.example.com'),
|
self.assertEqual(self.parser.convert_urls('wwww.example.com'),
|
||||||
|
|
@ -311,7 +313,8 @@ class TestTextToHTMLparser(TestCase):
|
||||||
|
|
||||||
def test_url_edge_multiline(self):
|
def test_url_edge_multiline(self):
|
||||||
self.assertEqual(self.parser.convert_urls(' * http://example.com/info\n * bullet'),
|
self.assertEqual(self.parser.convert_urls(' * http://example.com/info\n * bullet'),
|
||||||
' * <a href="http://example.com/info" target="_blank">http://example.com/info</a>\n * bullet')
|
' * <a href="http://example.com/info" target="_blank">'
|
||||||
|
'http://example.com/info</a>\n * bullet')
|
||||||
|
|
||||||
def test_url_edge_following_htmlentity(self):
|
def test_url_edge_following_htmlentity(self):
|
||||||
self.assertEqual(self.parser.convert_urls('http://example.com/info<span>'),
|
self.assertEqual(self.parser.convert_urls('http://example.com/info<span>'),
|
||||||
|
|
@ -319,7 +322,8 @@ class TestTextToHTMLparser(TestCase):
|
||||||
|
|
||||||
def test_url_edge_surrounded_spans(self):
|
def test_url_edge_surrounded_spans(self):
|
||||||
self.assertEqual(self.parser.convert_urls('</span>http://example.com/<span class="red">'),
|
self.assertEqual(self.parser.convert_urls('</span>http://example.com/<span class="red">'),
|
||||||
'</span><a href="http://example.com/" target="_blank">http://example.com/</a><span class="red">')
|
'</span><a href="http://example.com/" target="_blank">'
|
||||||
|
'http://example.com/</a><span class="red">')
|
||||||
|
|
||||||
from evennia.utils import evmenu
|
from evennia.utils import evmenu
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
@ -338,8 +342,9 @@ class TestEvMenu(TestCase):
|
||||||
|
|
||||||
from evennia.utils import inlinefuncs
|
from evennia.utils import inlinefuncs
|
||||||
|
|
||||||
|
|
||||||
class TestInlineFuncs(TestCase):
|
class TestInlineFuncs(TestCase):
|
||||||
"Test the nested inlinefunc module"
|
"""Test the nested inlinefunc module"""
|
||||||
def test_nofunc(self):
|
def test_nofunc(self):
|
||||||
self.assertEqual(inlinefuncs.parse_inlinefunc(
|
self.assertEqual(inlinefuncs.parse_inlinefunc(
|
||||||
"as$382ewrw w we w werw,|44943}"),
|
"as$382ewrw w we w werw,|44943}"),
|
||||||
|
|
@ -372,12 +377,146 @@ class TestInlineFuncs(TestCase):
|
||||||
|
|
||||||
from evennia.utils import evform
|
from evennia.utils import evform
|
||||||
|
|
||||||
|
|
||||||
class TestEvForm(TestCase):
|
class TestEvForm(TestCase):
|
||||||
def test_form(self):
|
def test_form(self):
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertEqual(evform._test(),
|
self.assertEqual(evform._test(),
|
||||||
u'.------------------------------------------------.\n| |\n| Name: \x1b[0m\x1b[1m\x1b[32mTom\x1b[1m\x1b[32m \x1b[1m\x1b[32mthe\x1b[1m\x1b[32m \x1b[0m \x1b[0m Player: \x1b[0m\x1b[1m\x1b[33mGriatch \x1b[0m\x1b[0m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[0m\x1b[0m |\n| \x1b[0m\x1b[1m\x1b[32mBouncer\x1b[0m \x1b[0m |\n| |\n >----------------------------------------------<\n| |\n| Desc: \x1b[0mA sturdy \x1b[0m \x1b[0m STR: \x1b[0m12 \x1b[0m\x1b[0m\x1b[0m\x1b[0m DEX: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| \x1b[0mfellow\x1b[0m \x1b[0m INT: \x1b[0m5 \x1b[0m\x1b[0m\x1b[0m\x1b[0m STA: \x1b[0m18 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| \x1b[0m \x1b[0m LUC: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m MAG: \x1b[0m3 \x1b[0m\x1b[0m\x1b[0m |\n| |\n >----------.-----------------------------------<\n| | |\n| \x1b[0mHP\x1b[0m|\x1b[0mMV \x1b[0m|\x1b[0mMP\x1b[0m | \x1b[0mSkill \x1b[0m|\x1b[0mValue \x1b[0m|\x1b[0mExp \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| ~~+~~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~+~~~~~~~~~~~ |\n| \x1b[0m**\x1b[0m|\x1b[0m***\x1b[0m\x1b[0m|\x1b[0m**\x1b[0m\x1b[0m | \x1b[0mShooting \x1b[0m|\x1b[0m12 \x1b[0m|\x1b[0m550/1200 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| \x1b[0m \x1b[0m|\x1b[0m**\x1b[0m \x1b[0m|\x1b[0m*\x1b[0m \x1b[0m | \x1b[0mHerbalism \x1b[0m|\x1b[0m14 \x1b[0m|\x1b[0m990/1400 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| \x1b[0m \x1b[0m|\x1b[0m \x1b[0m|\x1b[0m \x1b[0m | \x1b[0mSmithing \x1b[0m|\x1b[0m9 \x1b[0m|\x1b[0m205/900 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n| | |\n -----------`-------------------------------------\n')
|
u'.------------------------------------------------.\n'
|
||||||
|
u'| |\n'
|
||||||
|
u'| Name: \x1b[0m\x1b[1m\x1b[32mTom\x1b[1m\x1b[32m \x1b'
|
||||||
|
u'[1m\x1b[32mthe\x1b[1m\x1b[32m \x1b[0m \x1b[0m '
|
||||||
|
u'Player: \x1b[0m\x1b[1m\x1b[33mGriatch '
|
||||||
|
u'\x1b[0m\x1b[0m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[1m\x1b[32m\x1b[0m\x1b[0m '
|
||||||
|
u'|\n'
|
||||||
|
u'| \x1b[0m\x1b[1m\x1b[32mBouncer\x1b[0m \x1b[0m |\n'
|
||||||
|
u'| |\n'
|
||||||
|
u' >----------------------------------------------<\n'
|
||||||
|
u'| |\n'
|
||||||
|
u'| Desc: \x1b[0mA sturdy \x1b[0m \x1b[0m'
|
||||||
|
u' STR: \x1b[0m12 \x1b[0m\x1b[0m\x1b[0m\x1b[0m'
|
||||||
|
u' DEX: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| \x1b[0mfellow\x1b[0m \x1b[0m'
|
||||||
|
u' INT: \x1b[0m5 \x1b[0m\x1b[0m\x1b[0m\x1b[0m'
|
||||||
|
u' STA: \x1b[0m18 \x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| \x1b[0m \x1b[0m'
|
||||||
|
u' LUC: \x1b[0m10 \x1b[0m\x1b[0m\x1b[0m'
|
||||||
|
u' MAG: \x1b[0m3 \x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| |\n'
|
||||||
|
u' >----------.-----------------------------------<\n'
|
||||||
|
u'| | |\n'
|
||||||
|
u'| \x1b[0mHP\x1b[0m|\x1b[0mMV \x1b[0m|\x1b[0mMP\x1b[0m '
|
||||||
|
u'| \x1b[0mSkill \x1b[0m|\x1b[0mValue \x1b[0m'
|
||||||
|
u'|\x1b[0mExp \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| ~~+~~~+~~ | ~~~~~~~~~~~+~~~~~~~~~~+~~~~~~~~~~~ |\n'
|
||||||
|
u'| \x1b[0m**\x1b[0m|\x1b[0m***\x1b[0m\x1b[0m|\x1b[0m**\x1b[0m\x1b[0m '
|
||||||
|
u'| \x1b[0mShooting \x1b[0m|\x1b[0m12 \x1b[0m'
|
||||||
|
u'|\x1b[0m550/1200 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| \x1b[0m \x1b[0m|\x1b[0m**\x1b[0m \x1b[0m|\x1b[0m*\x1b[0m \x1b[0m '
|
||||||
|
u'| \x1b[0mHerbalism \x1b[0m|\x1b[0m14 \x1b[0m'
|
||||||
|
u'|\x1b[0m990/1400 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| \x1b[0m \x1b[0m|\x1b[0m \x1b[0m|\x1b[0m \x1b[0m '
|
||||||
|
u'| \x1b[0mSmithing \x1b[0m|\x1b[0m9 \x1b[0m'
|
||||||
|
u'|\x1b[0m205/900 \x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m\x1b[0m |\n'
|
||||||
|
u'| | |\n'
|
||||||
|
u' -----------`-------------------------------------\n')
|
||||||
|
|
||||||
def test_ansi_escape(self):
|
def test_ansi_escape(self):
|
||||||
# note that in a msg() call, the result would be the correct |-----,
|
# note that in a msg() call, the result would be the correct |-----,
|
||||||
# in a print, ansi only gets called once, so ||----- is the result
|
# in a print, ansi only gets called once, so ||----- is the result
|
||||||
self.assertEqual(unicode(evform.EvForm(form={"FORM":"\n||-----"})), "||-----")
|
self.assertEqual(unicode(evform.EvForm(form={"FORM":"\n||-----"})), "||-----")
|
||||||
|
|
||||||
|
class TestTimeformat(TestCase):
|
||||||
|
"""
|
||||||
|
Default function header from utils.py:
|
||||||
|
time_format(seconds, style=0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_style_0(self):
|
||||||
|
"""Test the style 0 of time_format."""
|
||||||
|
self.assertEqual(utils.time_format(0, 0), "00:00")
|
||||||
|
self.assertEqual(utils.time_format(28, 0), "00:00")
|
||||||
|
self.assertEqual(utils.time_format(92, 0), "00:01")
|
||||||
|
self.assertEqual(utils.time_format(300, 0), "00:05")
|
||||||
|
self.assertEqual(utils.time_format(660, 0), "00:11")
|
||||||
|
self.assertEqual(utils.time_format(3600, 0), "01:00")
|
||||||
|
self.assertEqual(utils.time_format(3725, 0), "01:02")
|
||||||
|
self.assertEqual(utils.time_format(86350, 0), "23:59")
|
||||||
|
self.assertEqual(utils.time_format(86800, 0), "1d 00:06")
|
||||||
|
self.assertEqual(utils.time_format(130800, 0), "1d 12:20")
|
||||||
|
self.assertEqual(utils.time_format(530800, 0), "6d 03:26")
|
||||||
|
|
||||||
|
def test_style_1(self):
|
||||||
|
"""Test the style 1 of time_format."""
|
||||||
|
self.assertEqual(utils.time_format(0, 1), "0s")
|
||||||
|
self.assertEqual(utils.time_format(28, 1), "28s")
|
||||||
|
self.assertEqual(utils.time_format(92, 1), "1m")
|
||||||
|
self.assertEqual(utils.time_format(300, 1), "5m")
|
||||||
|
self.assertEqual(utils.time_format(660, 1), "11m")
|
||||||
|
self.assertEqual(utils.time_format(3600, 1), "1h")
|
||||||
|
self.assertEqual(utils.time_format(3725, 1), "1h")
|
||||||
|
self.assertEqual(utils.time_format(86350, 1), "23h")
|
||||||
|
self.assertEqual(utils.time_format(86800, 1), "1d")
|
||||||
|
self.assertEqual(utils.time_format(130800, 1), "1d")
|
||||||
|
self.assertEqual(utils.time_format(530800, 1), "6d")
|
||||||
|
|
||||||
|
def test_style_2(self):
|
||||||
|
"""Test the style 2 of time_format."""
|
||||||
|
self.assertEqual(utils.time_format(0, 2), "0 minutes")
|
||||||
|
self.assertEqual(utils.time_format(28, 2), "0 minutes")
|
||||||
|
self.assertEqual(utils.time_format(92, 2), "1 minute")
|
||||||
|
self.assertEqual(utils.time_format(300, 2), "5 minutes")
|
||||||
|
self.assertEqual(utils.time_format(660, 2), "11 minutes")
|
||||||
|
self.assertEqual(utils.time_format(3600, 2), "1 hour, 0 minutes")
|
||||||
|
self.assertEqual(utils.time_format(3725, 2), "1 hour, 2 minutes")
|
||||||
|
self.assertEqual(utils.time_format(86350, 2), "23 hours, 59 minutes")
|
||||||
|
self.assertEqual(utils.time_format(86800, 2),
|
||||||
|
"1 day, 0 hours, 6 minutes")
|
||||||
|
self.assertEqual(utils.time_format(130800, 2),
|
||||||
|
"1 day, 12 hours, 20 minutes")
|
||||||
|
self.assertEqual(utils.time_format(530800, 2),
|
||||||
|
"6 days, 3 hours, 26 minutes")
|
||||||
|
|
||||||
|
def test_style_3(self):
|
||||||
|
"""Test the style 3 of time_format."""
|
||||||
|
self.assertEqual(utils.time_format(0, 3), "")
|
||||||
|
self.assertEqual(utils.time_format(28, 3), "28 seconds")
|
||||||
|
self.assertEqual(utils.time_format(92, 3), "1 minute 32 seconds")
|
||||||
|
self.assertEqual(utils.time_format(300, 3), "5 minutes 0 seconds")
|
||||||
|
self.assertEqual(utils.time_format(660, 3), "11 minutes 0 seconds")
|
||||||
|
self.assertEqual(utils.time_format(3600, 3),
|
||||||
|
"1 hour, 0 minutes")
|
||||||
|
self.assertEqual(utils.time_format(3725, 3),
|
||||||
|
"1 hour, 2 minutes 5 seconds")
|
||||||
|
self.assertEqual(utils.time_format(86350, 3),
|
||||||
|
"23 hours, 59 minutes 10 seconds")
|
||||||
|
self.assertEqual(utils.time_format(86800, 3),
|
||||||
|
"1 day, 0 hours, 6 minutes 40 seconds")
|
||||||
|
self.assertEqual(utils.time_format(130800, 3),
|
||||||
|
"1 day, 12 hours, 20 minutes 0 seconds")
|
||||||
|
self.assertEqual(utils.time_format(530800, 3),
|
||||||
|
"6 days, 3 hours, 26 minutes 40 seconds")
|
||||||
|
|
||||||
|
def test_style_4(self):
|
||||||
|
"""Test the style 4 of time_format."""
|
||||||
|
self.assertEqual(utils.time_format(0, 4), "0 seconds")
|
||||||
|
self.assertEqual(utils.time_format(28, 4), "28 seconds")
|
||||||
|
self.assertEqual(utils.time_format(92, 4), "a minute")
|
||||||
|
self.assertEqual(utils.time_format(300, 4), "5 minutes")
|
||||||
|
self.assertEqual(utils.time_format(660, 4), "11 minutes")
|
||||||
|
self.assertEqual(utils.time_format(3600, 4), "an hour")
|
||||||
|
self.assertEqual(utils.time_format(3725, 4), "an hour")
|
||||||
|
self.assertEqual(utils.time_format(86350, 4), "23 hours")
|
||||||
|
self.assertEqual(utils.time_format(86800, 4), "a day")
|
||||||
|
self.assertEqual(utils.time_format(130800, 4), "a day")
|
||||||
|
self.assertEqual(utils.time_format(530800, 4), "6 days")
|
||||||
|
self.assertEqual(utils.time_format(3030800, 4), "a month")
|
||||||
|
self.assertEqual(utils.time_format(7030800, 4), "2 months")
|
||||||
|
self.assertEqual(utils.time_format(40030800, 4), "a year")
|
||||||
|
self.assertEqual(utils.time_format(90030800, 4), "2 years")
|
||||||
|
|
||||||
|
def test_unknown_format(self):
|
||||||
|
"""Test that unknown formats raise exceptions."""
|
||||||
|
self.assertRaises(ValueError, utils.time_format, 0, 5)
|
||||||
|
self.assertRaises(ValueError, utils.time_format, 0, "u")
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ _DA = object.__delattr__
|
||||||
|
|
||||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||||
|
|
||||||
|
|
||||||
def is_iter(iterable):
|
def is_iter(iterable):
|
||||||
"""
|
"""
|
||||||
Checks if an object behaves iterably.
|
Checks if an object behaves iterably.
|
||||||
|
|
@ -62,6 +63,7 @@ def is_iter(iterable):
|
||||||
"""
|
"""
|
||||||
return hasattr(iterable, '__iter__')
|
return hasattr(iterable, '__iter__')
|
||||||
|
|
||||||
|
|
||||||
def make_iter(obj):
|
def make_iter(obj):
|
||||||
"""
|
"""
|
||||||
Makes sure that the object is always iterable.
|
Makes sure that the object is always iterable.
|
||||||
|
|
@ -250,7 +252,6 @@ def justify(text, width=_DEFAULT_WIDTH, align="f", indent=0):
|
||||||
wlen += word[1]
|
wlen += word[1]
|
||||||
ngaps += 1
|
ngaps += 1
|
||||||
|
|
||||||
|
|
||||||
if line: # catch any line left behind
|
if line: # catch any line left behind
|
||||||
lines.append(_process_line(line))
|
lines.append(_process_line(line))
|
||||||
indentstring = " " * indent
|
indentstring = " " * indent
|
||||||
|
|
@ -344,6 +345,7 @@ def time_format(seconds, style=0):
|
||||||
1. "1d"
|
1. "1d"
|
||||||
2. "1 day, 8 hours, 30 minutes"
|
2. "1 day, 8 hours, 30 minutes"
|
||||||
3. "1 day, 8 hours, 30 minutes, 10 seconds"
|
3. "1 day, 8 hours, 30 minutes, 10 seconds"
|
||||||
|
4. highest unit (like "3 years" or "8 months" or "1 second")
|
||||||
Returns:
|
Returns:
|
||||||
timeformatted (str): A pretty time string.
|
timeformatted (str): A pretty time string.
|
||||||
"""
|
"""
|
||||||
|
|
@ -432,6 +434,38 @@ def time_format(seconds, style=0):
|
||||||
else:
|
else:
|
||||||
seconds_str = '%i seconds ' % seconds
|
seconds_str = '%i seconds ' % seconds
|
||||||
retval = '%s%s%s%s' % (days_str, hours_str, minutes_str, seconds_str)
|
retval = '%s%s%s%s' % (days_str, hours_str, minutes_str, seconds_str)
|
||||||
|
elif style == 4:
|
||||||
|
"""
|
||||||
|
Only return the highest unit.
|
||||||
|
"""
|
||||||
|
if days >= 730: # Several years
|
||||||
|
return "{} years".format(days // 365)
|
||||||
|
elif days >= 365: # One year
|
||||||
|
return "a year"
|
||||||
|
elif days >= 62: # Several months
|
||||||
|
return "{} months".format(days // 31)
|
||||||
|
elif days >= 31: # One month
|
||||||
|
return "a month"
|
||||||
|
elif days >= 2: # Several days
|
||||||
|
return "{} days".format(days)
|
||||||
|
elif days > 0:
|
||||||
|
return "a day"
|
||||||
|
elif hours >= 2: # Several hours
|
||||||
|
return "{} hours".format(hours)
|
||||||
|
elif hours > 0: # One hour
|
||||||
|
return "an hour"
|
||||||
|
elif minutes >= 2: # Several minutes
|
||||||
|
return "{} minutes".format(minutes)
|
||||||
|
elif minutes > 0: # One minute
|
||||||
|
return "a minute"
|
||||||
|
elif seconds >= 2: # Several seconds
|
||||||
|
return "{} seconds".format(seconds)
|
||||||
|
elif seconds == 1:
|
||||||
|
return "a second"
|
||||||
|
else:
|
||||||
|
return "0 seconds"
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown style for time format: %s" % style)
|
||||||
|
|
||||||
return retval.strip()
|
return retval.strip()
|
||||||
|
|
||||||
|
|
@ -616,6 +650,7 @@ dbid_to_obj = dbref_to_obj
|
||||||
_UNICODE_MAP = {"EM DASH": "-", "FIGURE DASH": "-", "EN DASH": "-", "HORIZONTAL BAR": "-",
|
_UNICODE_MAP = {"EM DASH": "-", "FIGURE DASH": "-", "EN DASH": "-", "HORIZONTAL BAR": "-",
|
||||||
"HORIZONTAL ELLIPSIS": "...", "RIGHT SINGLE QUOTATION MARK": "'"}
|
"HORIZONTAL ELLIPSIS": "...", "RIGHT SINGLE QUOTATION MARK": "'"}
|
||||||
|
|
||||||
|
|
||||||
def latinify(unicode_string, default='?', pure_ascii=False):
|
def latinify(unicode_string, default='?', pure_ascii=False):
|
||||||
"""
|
"""
|
||||||
Convert a unicode string to "safe" ascii/latin-1 characters.
|
Convert a unicode string to "safe" ascii/latin-1 characters.
|
||||||
|
|
@ -643,7 +678,7 @@ def latinify(unicode_string, default='?', pure_ascii=False):
|
||||||
# point name; e.g., since `name(u'á') == 'LATIN SMALL
|
# point name; e.g., since `name(u'á') == 'LATIN SMALL
|
||||||
# LETTER A WITH ACUTE'` translate `á` to `a`. However, in
|
# LETTER A WITH ACUTE'` translate `á` to `a`. However, in
|
||||||
# some cases the unicode name is still "LATIN LETTER"
|
# some cases the unicode name is still "LATIN LETTER"
|
||||||
# although no direct equivalent in the Latin alphabeth
|
# although no direct equivalent in the Latin alphabet
|
||||||
# exists (e.g., Þ, "LATIN CAPITAL LETTER THORN") -- we can
|
# exists (e.g., Þ, "LATIN CAPITAL LETTER THORN") -- we can
|
||||||
# avoid these cases by checking that the letter name is
|
# avoid these cases by checking that the letter name is
|
||||||
# composed of one letter only.
|
# composed of one letter only.
|
||||||
|
|
@ -910,6 +945,8 @@ def delay(timedelay, callback, *args, **kwargs):
|
||||||
|
|
||||||
_TYPECLASSMODELS = None
|
_TYPECLASSMODELS = None
|
||||||
_OBJECTMODELS = None
|
_OBJECTMODELS = None
|
||||||
|
|
||||||
|
|
||||||
def clean_object_caches(obj):
|
def clean_object_caches(obj):
|
||||||
"""
|
"""
|
||||||
Clean all object caches on the given object.
|
Clean all object caches on the given object.
|
||||||
|
|
@ -1146,8 +1183,6 @@ def all_from_module(module):
|
||||||
# module if available (try to avoid not imports)
|
# module if available (try to avoid not imports)
|
||||||
members = getmembers(mod, predicate=lambda obj: getmodule(obj) in (mod, None))
|
members = getmembers(mod, predicate=lambda obj: getmodule(obj) in (mod, None))
|
||||||
return dict((key, val) for key, val in members if not key.startswith("_"))
|
return dict((key, val) for key, val in members if not key.startswith("_"))
|
||||||
#return dict((key, val) for key, val in mod.__dict__.items()
|
|
||||||
# if not (key.startswith("_") or ismodule(val)))
|
|
||||||
|
|
||||||
|
|
||||||
def callables_from_module(module):
|
def callables_from_module(module):
|
||||||
|
|
@ -1348,6 +1383,7 @@ def class_from_module(path, defaultpaths=None):
|
||||||
# alias
|
# alias
|
||||||
object_from_module = class_from_module
|
object_from_module = class_from_module
|
||||||
|
|
||||||
|
|
||||||
def init_new_player(player):
|
def init_new_player(player):
|
||||||
"""
|
"""
|
||||||
Deprecated.
|
Deprecated.
|
||||||
|
|
@ -1488,7 +1524,7 @@ def format_table(table, extra_space=1):
|
||||||
for ir, row in enumarate(ftable):
|
for ir, row in enumarate(ftable):
|
||||||
if ir == 0:
|
if ir == 0:
|
||||||
# make first row white
|
# make first row white
|
||||||
string += "\n{w" + ""join(row) + "{n"
|
string += "\n|w" + ""join(row) + "|n"
|
||||||
else:
|
else:
|
||||||
string += "\n" + "".join(row)
|
string += "\n" + "".join(row)
|
||||||
print string
|
print string
|
||||||
|
|
@ -1539,6 +1575,8 @@ def get_evennia_pids():
|
||||||
|
|
||||||
from gc import get_referents
|
from gc import get_referents
|
||||||
from sys import getsizeof
|
from sys import getsizeof
|
||||||
|
|
||||||
|
|
||||||
def deepsize(obj, max_depth=4):
|
def deepsize(obj, max_depth=4):
|
||||||
"""
|
"""
|
||||||
Get not only size of the given object, but also the size of
|
Get not only size of the given object, but also the size of
|
||||||
|
|
@ -1562,21 +1600,22 @@ def deepsize(obj, max_depth=4):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def _recurse(o, dct, depth):
|
def _recurse(o, dct, depth):
|
||||||
if max_depth >= 0 and depth > max_depth:
|
if 0 <= max_depth < depth:
|
||||||
return
|
return
|
||||||
for ref in get_referents(o):
|
for ref in get_referents(o):
|
||||||
idr = id(ref)
|
idr = id(ref)
|
||||||
if not idr in dct:
|
if idr not in dct:
|
||||||
dct[idr] = (ref, getsizeof(ref, default=0))
|
dct[idr] = (ref, getsizeof(ref, default=0))
|
||||||
_recurse(ref, dct, depth+1)
|
_recurse(ref, dct, depth+1)
|
||||||
sizedict = {}
|
sizedict = {}
|
||||||
_recurse(obj, sizedict, 0)
|
_recurse(obj, sizedict, 0)
|
||||||
#count = len(sizedict) + 1
|
|
||||||
size = getsizeof(obj) + sum([p[1] for p in sizedict.values()])
|
size = getsizeof(obj) + sum([p[1] for p in sizedict.values()])
|
||||||
return size
|
return size
|
||||||
|
|
||||||
# lazy load handler
|
# lazy load handler
|
||||||
_missing = object()
|
_missing = object()
|
||||||
|
|
||||||
|
|
||||||
class lazy_property(object):
|
class lazy_property(object):
|
||||||
"""
|
"""
|
||||||
Delays loading of property until first access. Credit goes to the
|
Delays loading of property until first access. Credit goes to the
|
||||||
|
|
@ -1597,14 +1636,14 @@ class lazy_property(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, func, name=None, doc=None):
|
def __init__(self, func, name=None, doc=None):
|
||||||
"Store all properties for now"
|
"""Store all properties for now"""
|
||||||
self.__name__ = name or func.__name__
|
self.__name__ = name or func.__name__
|
||||||
self.__module__ = func.__module__
|
self.__module__ = func.__module__
|
||||||
self.__doc__ = doc or func.__doc__
|
self.__doc__ = doc or func.__doc__
|
||||||
self.func = func
|
self.func = func
|
||||||
|
|
||||||
def __get__(self, obj, type=None):
|
def __get__(self, obj, type=None):
|
||||||
"Triggers initialization"
|
"""Triggers initialization"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return self
|
return self
|
||||||
value = obj.__dict__.get(self.__name__, _missing)
|
value = obj.__dict__.get(self.__name__, _missing)
|
||||||
|
|
@ -1615,6 +1654,8 @@ class lazy_property(object):
|
||||||
|
|
||||||
_STRIP_ANSI = None
|
_STRIP_ANSI = None
|
||||||
_RE_CONTROL_CHAR = re.compile('[%s]' % re.escape(''.join([unichr(c) for c in range(0, 32)]))) # + range(127,160)])))
|
_RE_CONTROL_CHAR = re.compile('[%s]' % re.escape(''.join([unichr(c) for c in range(0, 32)]))) # + range(127,160)])))
|
||||||
|
|
||||||
|
|
||||||
def strip_control_sequences(string):
|
def strip_control_sequences(string):
|
||||||
"""
|
"""
|
||||||
Remove non-print text sequences.
|
Remove non-print text sequences.
|
||||||
|
|
@ -1675,12 +1716,12 @@ def m_len(target):
|
||||||
return len(ANSI_PARSER.strip_mxp(target))
|
return len(ANSI_PARSER.strip_mxp(target))
|
||||||
return len(target)
|
return len(target)
|
||||||
|
|
||||||
#------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Search handler function
|
# Search handler function
|
||||||
#------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Replace this hook function by changing settings.SEARCH_AT_RESULT.
|
# Replace this hook function by changing settings.SEARCH_AT_RESULT.
|
||||||
#
|
|
||||||
|
|
||||||
def at_search_result(matches, caller, query="", quiet=False, **kwargs):
|
def at_search_result(matches, caller, query="", quiet=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1774,6 +1815,7 @@ class LimitedSizeOrderedDict(OrderedDict):
|
||||||
super(LimitedSizeOrderedDict, self).update(*args, **kwargs)
|
super(LimitedSizeOrderedDict, self).update(*args, **kwargs)
|
||||||
self._check_size()
|
self._check_size()
|
||||||
|
|
||||||
|
|
||||||
def get_game_dir_path():
|
def get_game_dir_path():
|
||||||
"""
|
"""
|
||||||
This is called by settings_default in order to determine the path
|
This is called by settings_default in order to determine the path
|
||||||
|
|
@ -1784,7 +1826,7 @@ def get_game_dir_path():
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# current working directory, assumed to be somewhere inside gamedir.
|
# current working directory, assumed to be somewhere inside gamedir.
|
||||||
for i in range(10):
|
for _ in range(10):
|
||||||
gpath = os.getcwd()
|
gpath = os.getcwd()
|
||||||
if "server" in os.listdir(gpath):
|
if "server" in os.listdir(gpath):
|
||||||
if os.path.isfile(os.path.join("server", "conf", "settings.py")):
|
if os.path.isfile(os.path.join("server", "conf", "settings.py")):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue