Changed how the Typeclass system returns errors. Instead of echoing typeclass erros to the MUD-info channel (which is not only not only very spammy for everyone but also very hard to make clean so as to avoid recursion at a stage of typeclass failing), the system instead stores a property on itself called 'typeclass_last_errmsg' that holds eventual errors. This means that the task of reporting errors does not fall on the typeclass system itself but on the calling methods, as it should be. So src.utils.create.create_* functions now takes a new optional keyword "report_to" that holds an object to receive errors. If this keyword is given, the function msg():es that object with the error and returns None as before. If report_to is not set however, the create_* methods now return an Exception containing the error text. All default commands have been changed to accomodate for this behaviour, which allows for much more control over errors.

Also, the default ADMIN_MEDIA static files changed location in Django 1.4. The initial_setup function now accounts for this.
This commit is contained in:
Griatch 2012-04-21 16:15:37 +02:00
parent 63329f5420
commit 8c3b49e704
15 changed files with 838 additions and 785 deletions

View file

@ -16,13 +16,13 @@ from src.commands.default.muxcommand import MuxCommand
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
# limit members for API inclusion
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelPlayer", "CmdEmit", "CmdNewPassword",
__all__ = ("CmdBoot", "CmdBan", "CmdUnban", "CmdDelPlayer", "CmdEmit", "CmdNewPassword",
"CmdPerm", "CmdWall")
class CmdBoot(MuxCommand):
"""
@boot
@boot
Usage
@boot[/switches] <player obj> [: reason]
@ -30,11 +30,11 @@ class CmdBoot(MuxCommand):
Switches:
quiet - Silently boot without informing player
port - boot by port number instead of name or dbref
Boot a player object from the server. If a reason is
supplied it will be echoed to the user unless /quiet is set.
supplied it will be echoed to the user unless /quiet is set.
"""
key = "@boot"
locks = "cmd:perm(boot) or perm(Wizards)"
help_category = "Admin"
@ -43,7 +43,7 @@ class CmdBoot(MuxCommand):
"Implementing the function"
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @boot[/switches] <player> [:reason]")
return
@ -67,12 +67,12 @@ class CmdBoot(MuxCommand):
# Boot by player object
pobj = caller.search("*%s" % args, global_search=True, player=True)
if not pobj:
return
return
if pobj.character.has_player:
if not pobj.access(caller, 'boot'):
string = "You don't have the permission to boot %s."
pobj.msg(string)
return
return
# we have a bootable object with a connected user
matches = SESSIONS.sessions_from_player(pobj)
for match in matches:
@ -87,7 +87,7 @@ class CmdBoot(MuxCommand):
# Carry out the booting of the sessions in the boot list.
feedback = None
feedback = None
if not 'quiet' in self.switches:
feedback = "You have been disconnected by %s.\n" % caller.name
if reason:
@ -108,16 +108,16 @@ def list_bans(banlist):
Helper function to display a list of active bans. Input argument
is the banlist read into the two commands @ban and @undban below.
"""
if not banlist:
if not banlist:
return "No active bans were found."
table = [["id"], ["name/ip"], ["date"], ["reason"]]
table[0].extend([str(i+1) for i in range(len(banlist))])
for ban in banlist:
if ban[0]:
for ban in banlist:
if ban[0]:
table[1].append(ban[0])
else:
table[1].append(ban[1])
else:
table[1].append(ban[1])
table[2].extend([ban[3] for ban in banlist])
table[3].extend([ban[4] for ban in banlist])
ftable = utils.format_table(table, 4)
@ -128,8 +128,8 @@ def list_bans(banlist):
srow = "{w%s{n" % srow.rstrip()
else:
srow = "\n" + "{w%s{n" % row[0] + "".join(row[1:])
string += srow.rstrip()
return string
string += srow.rstrip()
return string
class CmdBan(MuxCommand):
"""
@ -137,34 +137,34 @@ class CmdBan(MuxCommand):
Usage:
@ban [<name or ip> [: reason]]
Without any arguments, shows numbered list of active bans.
Without any arguments, shows numbered list of active bans.
This command bans a user from accessing the game. Supply an
optional reason to be able to later remember why the ban was put in
place
It is often to
prefer over deleting a player with @delplayer. If banned by name,
that player account can no longer be logged into.
IP (Internet Protocol) address banning allows to block all access
from a specific address or subnet. Use the asterisk (*) as a
wildcard.
wildcard.
Examples:
Examples:
@ban thomas - ban account 'thomas'
@ban/ip 134.233.2.111 - ban specific ip address
@ban/ip 134.233.2.* - ban all in a subnet
@ban/ip 134.233.*.* - even wider ban
@ban/ip 134.233.2.* - ban all in a subnet
@ban/ip 134.233.*.* - even wider ban
A single IP filter is easy to circumvent by changing the computer
(also, some ISPs assign only temporary IPs to their users in the
first placer. Widening the IP block filter with wildcards might be
tempting, but remember that blocking too much may accidentally
also block innocent users connecting from the same country and
region.
"""
key = "@ban"
aliases = ["@bans"]
@ -173,25 +173,25 @@ class CmdBan(MuxCommand):
def func(self):
"""
Bans are stored in a serverconf db object as a list of
dictionaries:
Bans are stored in a serverconf db object as a list of
dictionaries:
[ (name, ip, ipregex, date, reason),
(name, ip, ipregex, date, reason),... ]
where name and ip are set by the user and are shown in
lists. ipregex is a converted form of ip where the * is
replaced by an appropriate regex pattern for fast
matching. date is the time stamp the ban was instigated and
'reason' is any optional info given to the command. Unset
'reason' is any optional info given to the command. Unset
values in each tuple is set to the empty string.
"""
"""
banlist = ServerConfig.objects.conf('server_bans')
if not banlist:
banlist = []
if not self.args or (self.switches
and not any(switch in ('ip', 'name') for switch in self.switches)):
if not self.args or (self.switches
and not any(switch in ('ip', 'name') for switch in self.switches)):
self.caller.msg(list_bans(banlist))
return
return
now = time.ctime()
reason = ""
@ -199,13 +199,13 @@ class CmdBan(MuxCommand):
ban, reason = self.args.rsplit(':',1)
else:
ban = self.args
ban = ban.lower()
ban = ban.lower()
ipban = IPREGEX.findall(ban)
if not ipban:
# store as name
# store as name
typ = "Name"
bantup = (ban, "", "", now, reason)
else:
bantup = (ban, "", "", now, reason)
else:
# an ip address.
typ = "IP"
ban = ipban[0]
@ -215,16 +215,16 @@ class CmdBan(MuxCommand):
print "regex:",ipregex
ipregex = re.compile(r"%s" % ipregex)
bantup = ("", ban, ipregex, now, reason)
# save updated banlist
# save updated banlist
banlist.append(bantup)
ServerConfig.objects.conf('server_bans', banlist)
ServerConfig.objects.conf('server_bans', banlist)
self.caller.msg("%s-Ban {w%s{x was added." % (typ, ban))
class CmdUnban(MuxCommand):
class CmdUnban(MuxCommand):
"""
remove a ban
Usage:
Usage:
@unban <banid>
This will clear a player name/ip ban previously set with the @ban
@ -236,26 +236,26 @@ class CmdUnban(MuxCommand):
key = "@unban"
locks = "cmd:perm(unban) or perm(Immortals)"
help_category="Admin"
def func(self):
"Implement unbanning"
banlist = ServerConfig.objects.conf('server_bans')
if not self.args:
if not self.args:
self.caller.msg(list_bans(banlist))
return
return
try:
try:
num = int(self.args)
except Exception:
self.caller.msg("You must supply a valid ban id to clear.")
return
return
if not banlist:
if not banlist:
self.caller.msg("There are no bans to clear.")
elif not (0 < num < len(banlist) + 1):
self.caller.msg("Ban id {w%s{x was not found." % self.args)
self.caller.msg("Ban id {w%s{x was not found." % self.args)
else:
# all is ok, clear ban
ban = banlist[num-1]
@ -263,20 +263,20 @@ class CmdUnban(MuxCommand):
ServerConfig.objects.conf('server_bans', banlist)
self.caller.msg("Cleared ban %s: %s" % (num, " ".join([s for s in ban[:2]])))
class CmdDelPlayer(MuxCommand):
"""
delplayer - delete player from server
Usage:
@delplayer[/switch] <name> [: reason]
Switch:
delobj - also delete the player's currently
assigned in-game object.
assigned in-game object.
Completely deletes a user from the server database,
making their nick and e-mail again available.
making their nick and e-mail again available.
"""
key = "@delplayer"
@ -287,10 +287,10 @@ class CmdDelPlayer(MuxCommand):
"Implements the command."
caller = self.caller
args = self.args
args = self.args
if hasattr(caller, 'player'):
caller = caller.player
caller = caller.player
if not args:
caller.msg("Usage: @delplayer[/delobj] <player/user name or #id> [: reason]")
@ -309,26 +309,26 @@ class CmdDelPlayer(MuxCommand):
except ValueError:
pass
if not players:
if not players:
# try to find a user instead of a Player
try:
user = User.objects.get(id=args)
except Exception:
except Exception:
try:
user = User.objects.get(username__iexact=args)
user = User.objects.get(username__iexact=args)
except Exception:
string = "No Player nor User found matching '%s'." % args
caller.msg(string)
return
return
try:
player = user.get_profile()
except Exception:
player = None
player = None
if player and not player.access(caller, 'delete'):
string = "You don't have the permissions to delete this player."
caller.msg(string)
return
return
string = ""
name = user.username
@ -340,13 +340,13 @@ class CmdDelPlayer(MuxCommand):
else:
string += "The User %s was deleted. It had no Player associated with it." % name
caller.msg(string)
return
return
elif utils.is_iter(players):
string = "There were multiple matches:"
for player in players:
string += "\n %s %s" % (player.id, player.key)
return
string += "\n %s %s" % (player.id, player.key)
return
else:
# one single match
@ -357,10 +357,10 @@ class CmdDelPlayer(MuxCommand):
if not player.access(caller, 'delete'):
string = "You don't have the permissions to delete that player."
caller.msg(string)
return
return
uname = user.username
# boot the player then delete
# boot the player then delete
if character and character.has_player:
caller.msg("Booting and informing player ...")
string = "\nYour account '%s' is being *permanently* deleted.\n" % uname
@ -368,30 +368,30 @@ class CmdDelPlayer(MuxCommand):
string += " Reason given:\n '%s'" % reason
character.msg(string)
caller.execute_cmd("@boot %s" % uname)
player.delete()
user.delete()
user.delete()
caller.msg("Player %s was successfully deleted." % uname)
class CmdEmit(MuxCommand):
class CmdEmit(MuxCommand):
"""
@emit
Usage:
@emit[/switches] [<obj>, <obj>, ... =] <message>
@remit [<obj>, <obj>, ... =] <message>
@pemit [<obj>, <obj>, ... =] <message>
@remit [<obj>, <obj>, ... =] <message>
@pemit [<obj>, <obj>, ... =] <message>
Switches:
room : limit emits to rooms only (default)
players : limit emits to players only
players : limit emits to players only
contents : send to the contents of matched objects too
Emits a message to the selected objects or to
your immediate surroundings. If the object is a room,
send to its contents. @remit and @pemit are just
limited forms of @emit, for sending to rooms and
send to its contents. @remit and @pemit are just
limited forms of @emit, for sending to rooms and
to players respectively.
"""
key = "@emit"
@ -401,7 +401,7 @@ class CmdEmit(MuxCommand):
def func(self):
"Implement the command"
caller = self.caller
args = self.args
@ -411,12 +411,12 @@ class CmdEmit(MuxCommand):
string += "\n@remit [<obj>, <obj>, ... =] <message>"
string += "\n@pemit [<obj>, <obj>, ... =] <message>"
caller.msg(string)
return
return
rooms_only = 'rooms' in self.switches
players_only = 'players' in self.switches
send_to_contents = 'contents' in self.switches
# we check which command was used to force the switches
if self.cmdstring == '@remit':
rooms_only = True
@ -429,12 +429,12 @@ class CmdEmit(MuxCommand):
else:
message = self.rhs
objnames = self.lhslist
# send to all objects
for objname in objnames:
obj = caller.search(objname, global_search=True)
if not obj:
return
return
if rooms_only and not obj.location == None:
caller.msg("%s is not a room. Ignored." % objname)
continue
@ -463,7 +463,7 @@ class CmdNewPassword(MuxCommand):
Set a player's password.
"""
key = "@userpassword"
locks = "cmd:perm(newpassword) or perm(Wizards)"
help_category = "Admin"
@ -475,12 +475,12 @@ class CmdNewPassword(MuxCommand):
if not self.rhs:
caller.msg("Usage: @userpassword <user obj> = <new password>")
return
# the player search also matches 'me' etc.
player = caller.search("*%s" % self.lhs, global_search=True, player=True)
return
# the player search also matches 'me' etc.
player = caller.search("*%s" % self.lhs, global_search=True, player=True)
if not player:
return
return
player.user.set_password(self.rhs)
player.user.save()
caller.msg("%s - new password set to '%s'." % (player.name, self.rhs))
@ -495,12 +495,12 @@ class CmdPerm(MuxCommand):
Usage:
@perm[/switch] <object> [= <permission>[,<permission>,...]]
@perm[/switch] *<player> [= <permission>[,<permission>,...]]
Switches:
del : delete the given permission from <object> or <player>.
player : set permission on a player (same as adding * to name)
This command sets/clears individual permission strings on an object
This command sets/clears individual permission strings on an object
or player. If no permission is given, list all permissions on <object>.
"""
key = "@perm"
@ -515,19 +515,19 @@ class CmdPerm(MuxCommand):
switches = self.switches
lhs, rhs = self.lhs, self.rhs
if not self.args:
string = "Usage: @perm[/switch] object [ = permission, permission, ...]"
if not self.args:
string = "Usage: @perm[/switch] object [ = permission, permission, ...]"
caller.msg(string)
return
playermode = 'player' in self.switches or lhs.startswith('*')
# locate the object
# locate the object
obj = caller.search(lhs, global_search=True, player=playermode)
if not obj:
return
if not rhs:
return
if not rhs:
if not obj.access(caller, 'examine'):
caller.msg("You are not allowed to examine this object.")
return
@ -539,15 +539,15 @@ class CmdPerm(MuxCommand):
string += ", ".join(obj.permissions)
if hasattr(obj, 'player') and hasattr(obj.player, 'is_superuser') and obj.player.is_superuser:
string += "\n(... but this object is currently controlled by a SUPERUSER! "
string += "All access checks are passed automatically.)"
string += "All access checks are passed automatically.)"
caller.msg(string)
return
return
# we supplied an argument on the form obj = perm
if not obj.access(caller, 'control'):
caller.msg("You are not allowed to edit this object's permissions.")
return
return
cstring = ""
tstring = ""
@ -561,24 +561,24 @@ class CmdPerm(MuxCommand):
continue
permissions = obj.permissions
del permissions[index]
obj.permissions = permissions
obj.permissions = permissions
cstring += "\nPermission '%s' was removed from %s." % (perm, obj.name)
tstring += "\n%s revokes the permission '%s' from you." % (caller.name, perm)
else:
# add a new permission
# add a new permission
permissions = obj.permissions
caller.permissions
caller.permissions
for perm in self.rhslist:
# don't allow to set a permission higher in the hierarchy than the one the
# caller has (to prevent self-escalation)
if perm.lower() in PERMISSION_HIERARCHY and not obj.locks.check_lockstring(caller, "dummy:perm(%s)" % perm):
caller.msg("You cannot assign a permission higher than the one you have yourself.")
return
return
if perm in permissions:
cstring += "\nPermission '%s' is already defined on %s." % (rhs, obj.name)
@ -586,7 +586,7 @@ class CmdPerm(MuxCommand):
permissions.append(perm)
obj.permissions = permissions
cstring += "\nPermission '%s' given to %s." % (rhs, obj.name)
tstring += "\n%s gives you the permission '%s'." % (caller.name, rhs)
tstring += "\n%s gives you the permission '%s'." % (caller.name, rhs)
caller.msg(cstring.strip())
if tstring:
obj.msg(tstring.strip())
@ -597,7 +597,7 @@ class CmdWall(MuxCommand):
Usage:
@wall <message>
Announces a message to all connected players.
"""
key = "@wall"

View file

@ -1,16 +1,16 @@
"""
Batch processors
These commands implements the 'batch-command' and 'batch-code'
processors, using the functionality in src.utils.batchprocessors.
They allow for offline world-building.
These commands implements the 'batch-command' and 'batch-code'
processors, using the functionality in src.utils.batchprocessors.
They allow for offline world-building.
Batch-command is the simpler system. This reads a file (*.ev)
containing a list of in-game commands and executes them in sequence as
if they had been entered in the game (including permission checks
etc).
Example batch-command file: game/gamesrc/commands/examples/example_batch_cmd.ev
Example batch-command file: game/gamesrc/commands/examples/example_batch_cmd.ev
Batch-code is a full-fledged python code interpreter that reads blocks
of python code (*.py) and executes them in sequence. This allows for
@ -18,7 +18,7 @@ much more power than Batch-command, but requires knowing Python and
the Evennia API. It is also a severe security risk and should
therefore always be limited to superusers only.
Example batch-code file: game/gamesrc/commands/examples/example_batch_code.py
Example batch-code file: game/gamesrc/commands/examples/example_batch_code.py
"""
from traceback import format_exc
@ -42,10 +42,10 @@ UTF8_ERROR = \
fine and fully supported! But for Evennia to know how to decode such
characters in a universal way, the batchfile must be saved with the
international 'UTF-8' encoding. This file is not.
Please re-save the batchfile with the UTF-8 encoding (refer to the
documentation of your text editor on how to do this, or switch to a
better featured one) and try again.
documentation of your text editor on how to do this, or switch to a
better featured one) and try again.
The (first) error was found with a character on line %s in the file.
"""
@ -60,16 +60,16 @@ def format_header(caller, entry):
Formats a header
"""
width = HEADER_WIDTH - 10
entry = entry.strip()
entry = entry.strip()
header = utils.crop(entry, width=width)
ptr = caller.ndb.batch_stackptr + 1
stacklen = len(caller.ndb.batch_stack)
ptr = caller.ndb.batch_stackptr + 1
stacklen = len(caller.ndb.batch_stack)
header = "{w%02i/%02i{G: %s{n" % (ptr, stacklen, header)
# add extra space to the side for padding.
header = "%s%s" % (header, " "*(width - len(header)))
header = header.replace('\n', '\\n')
return header
return header
def format_code(entry):
"""
@ -89,7 +89,7 @@ def batch_cmd_exec(caller):
command = stack[ptr]
caller.msg(format_header(caller, command))
try:
caller.execute_cmd(command)
caller.execute_cmd(command)
except Exception:
caller.msg(format_code(format_exc()))
return False
@ -98,24 +98,24 @@ def batch_cmd_exec(caller):
def batch_code_exec(caller):
"""
Helper function for executing a single batch-code entry
"""
"""
ptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
debug = caller.ndb.batch_debug
codedict = stack[ptr]
caller.msg(format_header(caller, codedict['code']))
err = BATCHCODE.code_exec(codedict,
extra_environ={"caller":caller}, debug=debug)
err = BATCHCODE.code_exec(codedict,
extra_environ={"caller":caller}, debug=debug)
if err:
caller.msg(format_code(err))
return False
return True
return True
def step_pointer(caller, step=1):
"""
Step in stack, returning the item located.
stackptr - current position in stack
stack - the stack of units
step - how many steps to move from stackptr
@ -139,10 +139,10 @@ def show_curr(caller, showall=False):
if stackptr >= len(stack):
caller.ndb.batch_stackptr = len(stack) - 1
show_curr(caller, showall)
return
return
entry = stack[stackptr]
entry = stack[stackptr]
if type(entry) == dict:
# this is a batch-code entry
string = format_header(caller, entry['code'])
@ -160,7 +160,7 @@ def show_curr(caller, showall=False):
def purge_processor(caller):
"""
This purges all effects running
on the caller.
on the caller.
"""
try:
del caller.ndb.batch_stack
@ -169,17 +169,17 @@ def purge_processor(caller):
del caller.ndb.batch_batchmode
except:
pass
# clear everything but the default cmdset.
caller.cmdset.delete(BatchSafeCmdSet)
caller.cmdset.clear()
# clear everything but the default cmdset.
caller.cmdset.delete(BatchSafeCmdSet)
caller.cmdset.clear()
caller.scripts.validate() # this will purge interactive mode
#------------------------------------------------------------
# main access commands
# main access commands
#------------------------------------------------------------
class CmdBatchCommands(MuxCommand):
"""
"""
Build from batch-command file
Usage:
@ -188,9 +188,9 @@ class CmdBatchCommands(MuxCommand):
Switch:
interactive - this mode will offer more control when
executing the batch file, like stepping,
skipping, reloading etc.
skipping, reloading etc.
Runs batches of commands from a batch-cmd text file (*.ev).
Runs batches of commands from a batch-cmd text file (*.ev).
"""
key = "@batchcommands"
@ -200,23 +200,23 @@ class CmdBatchCommands(MuxCommand):
def func(self):
"Starts the processor."
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @batchcommands[/interactive] <path.to.file>")
return
return
python_path = self.args
#parse indata file
#parse indata file
try:
commands = BATCHCMD.parse_file(python_path)
except UnicodeDecodeError, err:
lnum = err.linenum
caller.msg(UTF8_ERROR % (python_path, lnum))
return
return
if not commands:
string = "'%s' not found.\nYou have to supply the python path "
@ -225,37 +225,37 @@ class CmdBatchCommands(MuxCommand):
return
switches = self.switches
# Store work data in cache
# Store work data in cache
caller.ndb.batch_stack = commands
caller.ndb.batch_stackptr = 0
caller.ndb.batch_pythonpath = python_path
caller.ndb.batch_batchmode = "batch_commands"
caller.cmdset.add(BatchSafeCmdSet)
caller.cmdset.add(BatchSafeCmdSet)
if 'inter' in switches or 'interactive' in switches:
# Allow more control over how batch file is executed
# Set interactive state directly
# Set interactive state directly
caller.cmdset.add(BatchInteractiveCmdSet)
caller.msg("\nBatch-command processor - Interactive mode for %s ..." % python_path)
show_curr(caller)
else:
caller.msg("Running Batch-command processor - Automatic mode for %s ..." % python_path)
# add the 'safety' cmdset in case the batch processing adds cmdsets to us
for inum in range(len(commands)):
# loop through the batch file
# loop through the batch file
if not batch_cmd_exec(caller):
return
return
step_pointer(caller, 1)
# clean out the safety cmdset and clean out all other temporary attrs.
string = " Batchfile '%s' applied." % python_path
# clean out the safety cmdset and clean out all other temporary attrs.
string = " Batchfile '%s' applied." % python_path
caller.msg("{G%s" % string)
purge_processor(caller)
class CmdBatchCode(MuxCommand):
"""
"""
Build from batch-code file
Usage:
@ -264,29 +264,29 @@ class CmdBatchCode(MuxCommand):
Switch:
interactive - this mode will offer more control when
executing the batch file, like stepping,
skipping, reloading etc.
skipping, reloading etc.
debug - auto-delete all objects that has been marked as
deletable in the script file (see example files for
syntax). This is useful so as to to not leave multiple
object copies behind when testing out the script.
Runs batches of commands from a batch-code text file (*.py).
Runs batches of commands from a batch-code text file (*.py).
"""
key = "@batchcode"
aliases = ["@batchcodes"]
locks = "cmd:superuser()"
locks = "cmd:superuser()"
help_category = "Building"
def func(self):
"Starts the processor."
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @batchcode[/interactive/debug] <path.to.file>")
return
return
python_path = self.args
#parse indata file
@ -295,7 +295,7 @@ class CmdBatchCode(MuxCommand):
except UnicodeDecodeError, err:
lnum = err.linenum
caller.msg(UTF8_ERROR % (python_path, lnum))
return
return
if not codes:
string = "'%s' not found.\nYou have to supply the python path "
@ -314,12 +314,12 @@ class CmdBatchCode(MuxCommand):
caller.ndb.batch_pythonpath = python_path
caller.ndb.batch_batchmode = "batch_code"
caller.ndb.batch_debug = debug
caller.cmdset.add(BatchSafeCmdSet)
caller.cmdset.add(BatchSafeCmdSet)
if 'inter' in switches or 'interactive'in switches:
# Allow more control over how batch file is executed
# Set interactive state directly
# Set interactive state directly
caller.cmdset.add(BatchInteractiveCmdSet)
caller.msg("\nBatch-code processor - Interactive mode for %s ..." % python_path)
@ -328,11 +328,11 @@ class CmdBatchCode(MuxCommand):
caller.msg("Running Batch-code processor - Automatic mode for %s ..." % python_path)
# add the 'safety' cmdset in case the batch processing adds cmdsets to us
for inum in range(len(codes)):
# loop through the batch file
# loop through the batch file
if not batch_code_exec(caller):
return
return
step_pointer(caller, 1)
string = " Batchfile '%s' applied." % python_path
string = " Batchfile '%s' applied." % python_path
caller.msg("{G%s" % string)
purge_processor(caller)
@ -346,41 +346,41 @@ class CmdStateAbort(MuxCommand):
@abort
This is a safety feature. It force-ejects us out of the processor and to
the default cmdset, regardless of what current cmdset the processor might
the default cmdset, regardless of what current cmdset the processor might
have put us in (e.g. when testing buggy scripts etc).
"""
key = "@abort"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
"Exit back to default."
purge_processor(self.caller)
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
class CmdStateLL(MuxCommand):
"""
ll
Look at the full source for the current
command definition.
command definition.
"""
key = "ll"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
def func(self):
show_curr(self.caller, showall=True)
class CmdStatePP(MuxCommand):
"""
pp
Process the currently shown command definition.
"""
key = "pp"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
"""
@ -392,17 +392,17 @@ class CmdStatePP(MuxCommand):
else:
batch_cmd_exec(caller)
class CmdStateRR(MuxCommand):
"""
rr
Reload the batch file, keeping the current
position in it.
position in it.
"""
key = "rr"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -410,7 +410,7 @@ class CmdStateRR(MuxCommand):
new_data = BATCHCODE.parse_file(caller.ndb.batch_pythonpath)
else:
new_data = BATCHCMD.parse_file(caller.ndb.batch_pythonpath)
caller.ndb.batch_stack = new_data
caller.ndb.batch_stack = new_data
caller.msg(format_code("File reloaded. Staying on same command."))
show_curr(caller)
@ -423,7 +423,7 @@ class CmdStateRRR(MuxCommand):
"""
key = "rrr"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -443,7 +443,7 @@ class CmdStateNN(MuxCommand):
"""
key = "nn"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -464,7 +464,7 @@ class CmdStateNL(MuxCommand):
"""
key = "nl"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -485,7 +485,7 @@ class CmdStateBB(MuxCommand):
"""
key = "bb"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -493,7 +493,7 @@ class CmdStateBB(MuxCommand):
if arg and arg.isdigit():
step = -int(self.args)
else:
step = -1
step = -1
step_pointer(caller, step)
show_curr(caller)
@ -506,7 +506,7 @@ class CmdStateBL(MuxCommand):
"""
key = "bl"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -514,9 +514,9 @@ class CmdStateBL(MuxCommand):
if arg and arg.isdigit():
step = -int(self.args)
else:
step = -1
step = -1
step_pointer(caller, step)
show_curr(caller, showall=True)
show_curr(caller, showall=True)
class CmdStateSS(MuxCommand):
"""
@ -528,7 +528,7 @@ class CmdStateSS(MuxCommand):
"""
key = "ss"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -536,14 +536,14 @@ class CmdStateSS(MuxCommand):
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
step = 1
for istep in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
batch_cmd_exec(caller)
step_pointer(caller, 1)
step_pointer(caller, 1)
show_curr(caller)
class CmdStateSL(MuxCommand):
@ -552,11 +552,11 @@ class CmdStateSL(MuxCommand):
Process current command, then step to the next
one, viewing its full source. If steps is given,
process this many commands.
process this many commands.
"""
key = "sl"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -564,7 +564,7 @@ class CmdStateSL(MuxCommand):
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
step = 1
for istep in range(step):
if caller.ndb.batch_batchmode == "batch_code":
@ -583,7 +583,7 @@ class CmdStateCC(MuxCommand):
"""
key = "cc"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -604,7 +604,7 @@ class CmdStateCC(MuxCommand):
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
caller.msg(format_code("Finished processing batch file."))
class CmdStateJJ(MuxCommand):
"""
j <command number>
@ -613,7 +613,7 @@ class CmdStateJJ(MuxCommand):
"""
key = "j"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -622,9 +622,9 @@ class CmdStateJJ(MuxCommand):
number = int(self.args)-1
else:
caller.msg(format_code("You must give a number index."))
return
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
step = number - ptr
step_pointer(caller, step)
show_curr(caller)
@ -636,7 +636,7 @@ class CmdStateJL(MuxCommand):
"""
key = "jl"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
caller = self.caller
@ -645,43 +645,43 @@ class CmdStateJL(MuxCommand):
number = int(self.args)-1
else:
caller.msg(format_code("You must give a number index."))
return
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
step = number - ptr
step_pointer(caller, step)
show_curr(caller, showall=True)
class CmdStateQQ(MuxCommand):
"""
qq
qq
Quit the batchprocessor.
"""
key = "qq"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
purge_processor(self.caller)
self.caller.msg("Aborted interactive batch mode.")
class CmdStateHH(MuxCommand):
"Help command"
key = "hh"
help_category = "BatchProcess"
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands)"
def func(self):
string = """
Interactive batch processing commands:
nn [steps] - next command (no processing)
nl [steps] - next & look
nl [steps] - next & look
bb [steps] - back to previous command (no processing)
bl [steps] - back & look
bl [steps] - back & look
jj <N> - jump to command nr N (no processing)
jl <N> - jump & look
jl <N> - jump & look
pp - process currently shown command (no step)
ss [steps] - process & step
sl [steps] - process & step & look
@ -693,9 +693,9 @@ class CmdStateHH(MuxCommand):
cc - continue processing to end, then quit.
qq - quit (abort all remaining commands)
@abort - this is a safety command that always is available
@abort - this is a safety command that always is available
regardless of what cmdsets gets added to us during
batch-command processing. It immediately shuts down
batch-command processing. It immediately shuts down
the processor and returns us to the default cmdset.
"""
self.caller.msg(string)

View file

@ -399,9 +399,8 @@ class CmdCreate(ObjManipCommand):
# object typeclass will automatically be used)
lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
obj = create.create_object(typeclass, name, caller,
home=caller, aliases=aliases, locks=lockstring)
home=caller, aliases=aliases, locks=lockstring, report_to=caller)
if not obj:
string = "Error when creating object."
continue
if aliases:
string = "You create a new %s: %s (aliases: %s)."
@ -416,8 +415,8 @@ class CmdCreate(ObjManipCommand):
if caller.location:
obj.home = caller.location
obj.move_to(caller.location, quiet=True)
if string:
caller.msg(string)
if string:
caller.msg(string)
class CmdDebug(MuxCommand):
@ -630,7 +629,7 @@ class CmdDig(ObjManipCommand):
lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref)
new_room = create.create_object(typeclass, room["name"],
aliases=room["aliases"])
aliases=room["aliases"], report_to=caller)
new_room.locks.add(lockstring)
alias_string = ""
if new_room.aliases:
@ -659,7 +658,7 @@ class CmdDig(ObjManipCommand):
new_to_exit = create.create_object(typeclass, to_exit["name"], location,
aliases=to_exit["aliases"],
locks=lockstring, destination=new_room)
locks=lockstring, destination=new_room, report_to=caller)
alias_string = ""
if new_to_exit.aliases:
alias_string = " (%s)" % ", ".join(new_to_exit.aliases)
@ -684,7 +683,7 @@ class CmdDig(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS
new_back_exit = create.create_object(typeclass, back_exit["name"],
new_room, aliases=back_exit["aliases"],
locks=lockstring, destination=location)
locks=lockstring, destination=location, report_to=caller)
alias_string = ""
if new_back_exit.aliases:
alias_string = " (%s)" % ", ".join(new_back_exit.aliases)
@ -1083,7 +1082,7 @@ class CmdOpen(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS
exit_obj = create.create_object(typeclass, key=exit_name,
location=location,
aliases=exit_aliases)
aliases=exit_aliases, report_to=caller)
if exit_obj:
# storing a destination is what makes it an exit!
exit_obj.destination = destination
@ -1283,9 +1282,9 @@ class CmdTypeclass(MuxCommand):
@typeclass - set object typeclass
Usage:
@typclass[/switch] <object> [= <typeclass path>]
@type ''
@parent ''
@typclass[/switch] <object> [= <typeclass.path>]
@type ''
@parent ''
Switch:
reset - clean out *all* the attributes on the object -
@ -1295,12 +1294,18 @@ class CmdTypeclass(MuxCommand):
Example:
@type button = examples.red_button.RedButton
Sets an object's typeclass. The typeclass must be identified
by its location using python dot-notation pointing to the correct
module and class. If no typeclass is given (or a wrong typeclass
is given), the object will be set to the default typeclass.
The location of the typeclass module is searched from
the default typeclass directory, as defined in the server settings.
View or set an object's typeclass. If setting, the creation hooks
of the new typeclass will be run on the object. If you have
clashing properties on the old class, use /reset. By default you
are protected from changing to a typeclass of the same name as the
one you already have, use /force to override this protection.
The given typeclass must be identified by its location using
python dot-notation pointing to the correct module and class. If
no typeclass is given (or a wrong typeclass is given). Errors in
the path or new typeclass will lead to the old typeclass being
kept. The location of the typeclass module is searched from the
default typeclass directory, as defined in the server settings.
"""
@ -1327,7 +1332,7 @@ class CmdTypeclass(MuxCommand):
# we did not supply a new typeclass, view the
# current one instead.
if hasattr(obj, "typeclass"):
string = "%s's current typeclass is '%s'." % (obj.name, obj.typeclass.typename)
string = "%s's current typeclass is '%s' (%s)." % (obj.name, obj.typeclass.typename, obj.typeclass.path)
else:
string = "%s is not a typed object." % obj.name
caller.msg(string)
@ -1344,25 +1349,31 @@ class CmdTypeclass(MuxCommand):
caller.msg("This object cannot have a type at all!")
return
if obj.is_typeclass(typeclass) and not 'force' in self.switches:
string = "%s already has the typeclass '%s'." % (obj.name, typeclass)
is_same = obj.is_typeclass(typeclass)
if is_same and not 'force' in self.switches:
string = "%s already has the typeclass '%s'. Use /force to override." % (obj.name, typeclass)
else:
reset = "reset" in self.switches
old_typeclass_name = obj.typeclass.typename
old_typeclass_path = obj.typeclass.path
ok = obj.swap_typeclass(typeclass, clean_attributes=reset)
if ok:
string = "%s's type is now %s (instead of %s).\n" % (obj.name,
obj.typeclass.typename,
old_typeclass_name)
if reset:
string += "All attributes where reset."
if is_same:
string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path)
else:
string += "Note that the new class type could have overwritten "
string += "same-named attributes on the existing object."
string = "%s's changed typeclass from %s to %s.\n" % (obj.name,
old_typeclass_path,
obj.typeclass.path)
string += "Creation hooks were run."
if reset:
string += " All old attributes where deleted before the swap."
else:
string += " Note that the typeclassed object could have ended up with a mixture of old"
string += "\nand new attributes. Use /reset to remove old attributes if you don't want this."
else:
string = "Could not swap '%s' (%s) to typeclass '%s'." % (obj.name,
old_typeclass_name,
typeclass)
string = obj.typeclass_last_errmsg
string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name,
old_typeclass_path,
typeclass)
caller.msg(string)

View file

@ -2,9 +2,9 @@
Comsystem command module.
Comm commands are OOC commands and intended to be made available to
the Player at all times (they go into the PlayerCmdSet). So we
the Player at all times (they go into the PlayerCmdSet). So we
make sure to homogenize self.caller to always be the player object
for easy handling.
for easy handling.
"""
from django.conf import settings
@ -12,7 +12,7 @@ from src.comms.models import Channel, Msg, PlayerChannelConnection, ExternalChan
from src.comms import irc, imc2, rss
from src.comms.channelhandler import CHANNELHANDLER
from src.utils import create, utils
from src.commands.default.muxcommand import MuxCommand
from src.commands.default.muxcommand import MuxCommand
# limit symbol import for API
__all__ = ("CommCommand", "CmdAddCom", "CmdDelCom", "CmdAllCom",
@ -45,10 +45,10 @@ def find_channel(caller, channelname, silent=False, noaliases=False):
class CommCommand(MuxCommand):
"""
This is a parent for comm-commands. Since
These commands are to be available to the
Player, we make sure to homogenize the caller
here, so it's always seen as a player to the
command body.
These commands are to be available to the
Player, we make sure to homogenize the caller
here, so it's always seen as a player to the
command body.
"""
def parse(self):
@ -60,14 +60,14 @@ class CommCommand(MuxCommand):
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# an object. Convert it to its player.
self.caller = self.caller.player
class CmdAddCom(MuxCommand):
"""
addcom - subscribe to a channel with optional alias
Usage:
addcom [alias=] <channel>
Joins a given channel. If alias is given, this will allow you to
refer to the channel by this alias rather than the full channel
name. Subsequent calls of this command can be used to add multiple
@ -81,7 +81,7 @@ class CmdAddCom(MuxCommand):
def func(self):
"Implement the command"
caller = self.caller
args = self.args
player = caller
@ -92,7 +92,7 @@ class CmdAddCom(MuxCommand):
if self.rhs:
# rhs holds the channelname
channelname = self.rhs
channelname = self.rhs
alias = self.lhs
else:
channelname = self.args
@ -101,12 +101,12 @@ class CmdAddCom(MuxCommand):
channel = find_channel(caller, channelname)
if not channel:
# we use the custom search method to handle errors.
return
return
# check permissions
if not channel.access(player, 'listen'):
caller.msg("%s: You are not allowed to listen to this channel." % channel.key)
return
return
string = ""
if not channel.has_connection(player):
@ -114,16 +114,16 @@ class CmdAddCom(MuxCommand):
if not channel.connect_to(player):
# if this would have returned True, the player is connected
caller.msg("%s: You are not allowed to join this channel." % channel.key)
return
return
else:
string += "You now listen to the channel %s. " % channel.key
else:
string += "You are already connected to channel %s." % channel.key
if alias:
# create a nick and add it to the caller.
caller.nicks.add(alias, channel.key, nick_type="channel")
string += " You can now refer to the channel %s with the alias '%s'."
string += " You can now refer to the channel %s with the alias '%s'."
caller.msg(string % (channel.key, alias))
else:
string += " No alias added."
@ -155,26 +155,26 @@ class CmdDelCom(MuxCommand):
if not self.args:
caller.msg("Usage: delcom <alias or channel>")
return
return
ostring = self.args.lower()
channel = find_channel(caller, ostring, silent=True, noaliases=True)
if channel:
# we have given a channel name - unsubscribe
if not channel.has_connection(player):
caller.msg("You are not listening to that channel.")
return
return
chkey = channel.key.lower()
# find all nicks linked to this channel and delete them
for nick in [nick for nick in caller.nicks.get(nick_type="channel")
if nick.db_real.lower() == chkey]:
for nick in [nick for nick in caller.nicks.get(nick_type="channel")
if nick.db_real.lower() == chkey]:
nick.delete()
channel.disconnect_from(player)
caller.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key)
return
return
else:
# we are removing a channel nick
channame = caller.nicks.get(ostring, nick_type="channel")
channame = caller.nicks.get(ostring, nick_type="channel")
channel = find_channel(caller, channame, silent=True)
if not channel:
caller.msg("No channel with alias '%s' was found." % ostring)
@ -184,13 +184,13 @@ class CmdDelCom(MuxCommand):
caller.msg("Your alias '%s' for channel %s was cleared." % (ostring, channel.key))
else:
caller.msg("You had no such alias defined for this channel.")
class CmdAllCom(MuxCommand):
"""
allcom - operate on all channels
Usage:
allcom [on | off | who | destroy]
Usage:
allcom [on | off | who | destroy]
Allows the user to universally turn off or on all channels they are on,
as well as perform a 'who' for all channels they are on. Destroy deletes
@ -198,11 +198,11 @@ class CmdAllCom(MuxCommand):
Without argument, works like comlist.
"""
key = "allcom"
locks = "cmd: not pperm(channel_banned)"
help_category = "Comms"
def func(self):
"Runs the function"
@ -224,12 +224,12 @@ class CmdAllCom(MuxCommand):
for channel in channels:
caller.execute_cmd("delcom %s" % channel.key)
elif args == "destroy":
# destroy all channels you control
# destroy all channels you control
channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'control')]
for channel in channels:
caller.execute_cmd("@cdestroy %s" % channel.key)
elif args == "who":
# run a who, listing the subscribers on visible channels.
# run a who, listing the subscribers on visible channels.
string = "\n{CChannel subscriptions{n"
channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')]
if not channels:
@ -243,7 +243,7 @@ class CmdAllCom(MuxCommand):
string += " <None>"
caller.msg(string.strip())
else:
# wrong input
# wrong input
caller.msg("Usage: allcom on | off | who | clear")
class CmdChannels(MuxCommand):
@ -255,7 +255,7 @@ class CmdChannels(MuxCommand):
@clist
comlist
Lists all channels available to you, wether you listen to them or not.
Lists all channels available to you, wether you listen to them or not.
Use 'comlist" to only view your current channel subscriptions.
"""
key = "@channels"
@ -265,11 +265,11 @@ class CmdChannels(MuxCommand):
def func(self):
"Implement function"
caller = self.caller
# all channels we have available to listen to
channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')]
channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')]
if not channels:
caller.msg("No channels available.")
return
@ -278,7 +278,7 @@ class CmdChannels(MuxCommand):
if self.cmdstring != "comlist":
string = "\nChannels available:"
string = "\nChannels available:"
cols = [[" "], ["Channel"], ["Aliases"], ["Perms"], ["Description"]]
for chan in channels:
if chan in subs:
@ -289,10 +289,10 @@ class CmdChannels(MuxCommand):
cols[2].append(",".join(chan.aliases))
cols[3].append(str(chan.locks))
cols[4].append(chan.desc)
# put into table
# put into table
for ir, row in enumerate(utils.format_table(cols)):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
self.caller.msg(string)
@ -306,13 +306,13 @@ class CmdChannels(MuxCommand):
for chan in subs:
cols[0].append(" ")
cols[1].append(chan.key)
cols[2].append(",".join([nick.db_nick for nick in nicks
cols[2].append(",".join([nick.db_nick for nick in nicks
if nick.db_real.lower() == chan.key.lower()] + chan.aliases))
cols[3].append(chan.desc)
# put into table
for ir, row in enumerate(utils.format_table(cols)):
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
caller.msg(string)
@ -341,10 +341,10 @@ class CmdCdestroy(MuxCommand):
channel = find_channel(caller, self.args)
if not channel:
caller.msg("Could not find channel %s." % self.args)
return
return
if not channel.access(caller, 'control'):
caller.msg("You are not allowed to do that.")
return
return
message = "%s is being destroyed. Make sure to change your aliases." % channel
msgobj = create.create_message(caller, message, channel)
@ -352,7 +352,7 @@ class CmdCdestroy(MuxCommand):
channel.delete()
CHANNELHANDLER.update()
caller.msg("%s was destroyed." % channel)
class CmdCBoot(MuxCommand):
"""
@cboot
@ -370,24 +370,24 @@ class CmdCBoot(MuxCommand):
key = "@cboot"
locks = "cmd: not pperm(channel_banned)"
help_category = "Comms"
def func(self):
"implement the function"
if not self.args or not self.rhs:
string = "Usage: @cboot[/quiet] <channel> = <player> [:reason]"
self.caller.msg(string)
return
return
channel = find_channel(self.caller, self.lhs)
if not channel:
return
reason = ""
if ":" in self.rhs:
playername, reason = self.rhs.rsplit(":", 1)
playername, reason = self.rhs.rsplit(":", 1)
searchstring = playername.lstrip('*')
else:
searchstring = self.rhs.lstrip('*')
searchstring = self.rhs.lstrip('*')
player = self.caller.search(searchstring, player=True)
if not player:
return
@ -396,7 +396,7 @@ class CmdCBoot(MuxCommand):
if not channel.access(self.caller, "control"):
string = "You don't control this channel."
self.caller.msg(string)
return
return
if not PlayerChannelConnection.objects.has_connection(player, channel):
string = "Player %s is not connected to channel %s." % (player.key, channel.key)
self.caller.msg(string)
@ -405,10 +405,10 @@ class CmdCBoot(MuxCommand):
string = "%s boots %s from channel.%s" % (self.caller, player.key, reason)
channel.msg(string)
# find all player's nicks linked to this channel and delete them
for nick in [nick for nick in player.character.nicks.get(nick_type="channel")
if nick.db_real.lower() == channel.key]:
for nick in [nick for nick in player.character.nicks.get(nick_type="channel")
if nick.db_real.lower() == channel.key]:
nick.delete()
# disconnect player
# disconnect player
channel.disconnect_from(player)
class CmdCemit(MuxCommand):
@ -426,9 +426,9 @@ class CmdCemit(MuxCommand):
Allows the user to broadcast a message over a channel as long as
they control it. It does not show the user's name unless they
provide the /sendername switch.
"""
key = "@cemit"
aliases = ["@cmsg"]
locks = "cmd: not pperm(channel_banned)"
@ -440,18 +440,18 @@ class CmdCemit(MuxCommand):
if not self.args or not self.rhs:
string = "Usage: @cemit[/switches] <channel> = <message>"
self.caller.msg(string)
return
return
channel = find_channel(self.caller, self.lhs)
if not channel:
return
if not channel.access(self.caller, "control"):
string = "You don't control this channel."
self.caller.msg(string)
return
return
message = self.rhs
if "sendername" in self.switches:
message = "%s: %s" % (self.caller.key, message)
if not "noheader" in self.switches:
if not "noheader" in self.switches:
message = "[%s] %s" % (channel.key, message)
channel.msg(message)
if not "quiet" in self.switches:
@ -461,8 +461,8 @@ class CmdCemit(MuxCommand):
class CmdCWho(MuxCommand):
"""
@cwho
Usage:
Usage:
@cwho <channel>
List who is connected to a given channel you have access to.
@ -473,18 +473,18 @@ class CmdCWho(MuxCommand):
def func(self):
"implement function"
if not self.args:
string = "Usage: @cwho <channel>"
self.caller.msg(string)
return
return
channel = find_channel(self.caller, self.lhs)
if not channel:
return
return
if not channel.access(self.caller, "listen"):
string = "You can't access this channel."
self.caller.msg(string)
self.caller.msg(string)
string = "\n{CChannel subscriptions{n"
string += "\n{w%s:{n\n" % channel.key
conns = PlayerChannelConnection.objects.get_all_connections(channel)
@ -497,13 +497,13 @@ class CmdCWho(MuxCommand):
class CmdChannelCreate(MuxCommand):
"""
@ccreate
channelcreate
channelcreate
Usage:
@ccreate <new channel>[;alias;alias...] = description
Creates a new channel owned by you.
"""
key = "@ccreate"
aliases = "channelcreate"
locks = "cmd:not pperm(channel_banned)"
@ -517,7 +517,7 @@ class CmdChannelCreate(MuxCommand):
if not self.args:
caller.msg("Usage @ccreate <channelname>[;alias;alias..] = description")
return
description = ""
if self.rhs:
@ -526,25 +526,25 @@ class CmdChannelCreate(MuxCommand):
channame = lhs
aliases = None
if ';' in lhs:
channame, aliases = [part.strip().lower()
channame, aliases = [part.strip().lower()
for part in lhs.split(';', 1) if part.strip()]
aliases = [alias.strip().lower()
for alias in aliases.split(';') if alias.strip()]
channel = Channel.objects.channel_search(channame)
aliases = [alias.strip().lower()
for alias in aliases.split(';') if alias.strip()]
channel = Channel.objects.channel_search(channame)
if channel:
caller.msg("A channel with that name already exists.")
return
return
# Create and set the channel up
lockstring = "send:all();listen:all();control:id(%s)" % caller.id
new_chan = create.create_channel(channame, aliases, description, locks=lockstring)
new_chan.connect_to(caller)
caller.msg("Created channel %s and connected to it." % new_chan.key)
class CmdCset(MuxCommand):
"""
@cset - changes channel access restrictions
Usage:
@cset <channel> [= <lockstring>]
@ -559,26 +559,26 @@ class CmdCset(MuxCommand):
def func(self):
"run the function"
if not self.args:
string = "Usage: @cset channel [= lockstring]"
self.caller.msg(string)
return
return
channel = find_channel(self.caller, self.lhs)
if not channel:
return
return
if not self.rhs:
# no =, so just view the current locks
string = "Current locks on %s:" % channel.key
string = "%s\n %s" % (string, channel.locks)
self.caller.msg(string)
return
# we want to add/change a lock.
return
# we want to add/change a lock.
if not channel.access(self.caller, "control"):
string = "You don't control this channel."
self.caller.msg(string)
return
return
# Try to add the lock
channel.locks.add(self.rhs)
string = "Lock(s) applied. "
@ -595,7 +595,7 @@ class CmdCdesc(MuxCommand):
@cdesc <channel> = <description>
Changes the description of the channel as shown in
channel lists.
channel lists.
"""
key = "@cdesc"
@ -604,12 +604,12 @@ class CmdCdesc(MuxCommand):
def func(self):
"Implement command"
caller = self.caller
if not self.rhs:
caller.msg("Usage: @cdesc <channel> = <description>")
return
return
channel = find_channel(caller, self.lhs)
if not channel:
caller.msg("Channel '%s' not found." % self.lhs)
@ -635,7 +635,7 @@ class CmdPage(MuxCommand):
Switch:
last - shows who you last messaged
list - show your last <number> of tells/pages (default)
Send a message to target user (if online). If no
argument is given, you will get a list of your latest messages.
"""
@ -644,24 +644,24 @@ class CmdPage(MuxCommand):
aliases = ['tell']
locks = "cmd:not pperm(page_banned)"
help_category = "Comms"
def func(self):
"Implement function using the Msg methods"
caller = self.caller
player = caller
# get the messages we've sent
messages_we_sent = list(Msg.objects.get_messages_by_sender(player))
pages_we_sent = [msg for msg in messages_we_sent
messages_we_sent = list(Msg.objects.get_messages_by_sender(player))
pages_we_sent = [msg for msg in messages_we_sent
if msg.receivers]
# get last messages we've got
pages_we_got = list(Msg.objects.get_messages_by_receiver(player))
pages_we_got = list(Msg.objects.get_messages_by_receiver(player))
if 'last' in self.switches:
if pages_we_sent:
string = "You last paged {c%s{n." % (", ".join([obj.name
string = "You last paged {c%s{n." % (", ".join([obj.name
for obj in pages_we_sent[-1].receivers]))
caller.msg(string)
return
@ -680,15 +680,15 @@ class CmdPage(MuxCommand):
number = int(self.args)
except ValueError:
caller.msg("Usage: tell [<player> = msg]")
return
return
if len(pages) > number:
lastpages = pages[-number:]
else:
lastpages = pages
lastpages = "\n ".join(["{w%s{n {c%s{n to {c%s{n: %s" % (utils.datetime_format(page.date_sent),
page.sender.name,
lastpages = pages
lastpages = "\n ".join(["{w%s{n {c%s{n to {c%s{n: %s" % (utils.datetime_format(page.date_sent),
page.sender.name,
"{n,{c ".join([obj.name for obj in page.receivers]),
page.message)
for page in lastpages])
@ -704,16 +704,16 @@ class CmdPage(MuxCommand):
# We are sending. Build a list of targets
if not self.lhs:
# If there are no targets, then set the targets
# If there are no targets, then set the targets
# to the last person they paged.
if pages_we_sent:
receivers = pages_we_sent[-1].receivers
else:
caller.msg("Who do you want to page?")
return
return
else:
receivers = self.lhslist
receivers = self.lhslist
recobjs = []
for receiver in set(receivers):
if isinstance(receiver, basestring):
@ -724,22 +724,22 @@ class CmdPage(MuxCommand):
pobj = receiver.character
else:
caller.msg("Who do you want to page?")
return
return
recobjs.append(pobj)
if not recobjs:
caller.msg("No players matching your target were found.")
return
return
header = "{wPlayer{n {c%s{n {wpages:{n" % caller.key
message = self.rhs
# if message begins with a :, we assume it is a 'page-pose'
if message.startswith(":"):
if message.startswith(":"):
message = "%s %s" % (caller.key, message.strip(':').strip())
# create the persistent message object
msg = create.create_message(player, message,
receivers=recobjs)
msg = create.create_message(player, message,
receivers=recobjs)
# tell the players they got a message.
received = []
@ -747,8 +747,8 @@ class CmdPage(MuxCommand):
for pobj in recobjs:
if not pobj.access(caller, 'msg'):
rstrings.append("You are not allowed to page %s." % pobj)
continue
pobj.msg("%s %s" % (header, message))
continue
pobj.msg("%s %s" % (header, message))
if hasattr(pobj, 'has_player') and not pobj.has_player:
received.append("{C%s{n" % pobj.name)
rstrings.append("%s is offline. They will see your message if they list their pages later." % received[-1])
@ -768,19 +768,19 @@ class CmdIRC2Chan(MuxCommand):
Switches:
/disconnect - this will delete the bot and remove the irc connection to the channel.
/remove - "
/remove - "
/list - show all irc<->evennia mappings
Example:
@irc2chan myircchan = irc.dalnet.net 6667 myevennia-channel evennia-bot
This creates an IRC bot that connects to a given IRC network and channel. It will
relay everything said in the evennia channel to the IRC channel and vice versa. The
bot will automatically connect at server start, so this comman need only be given once.
The /disconnect switch will permanently delete the bot. To only temporarily deactivate it,
use the @services command instead.
This creates an IRC bot that connects to a given IRC network and channel. It will
relay everything said in the evennia channel to the IRC channel and vice versa. The
bot will automatically connect at server start, so this comman need only be given once.
The /disconnect switch will permanently delete the bot. To only temporarily deactivate it,
use the @services command instead.
"""
key = "@irc2chan"
locks = "cmd:serversetting(IRC_ENABLED) and pperm(Immortals)"
help_category = "Comms"
@ -811,21 +811,21 @@ class CmdIRC2Chan(MuxCommand):
self.caller.msg(string)
else:
self.caller.msg("No connections found.")
return
return
if not self.args or not self.rhs:
string = "Usage: @irc2chan[/switches] <evennia_channel> = <ircnetwork> <port> <#irchannel> <botname>"
self.caller.msg(string)
return
return
channel = self.lhs
self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues
try:
irc_network, irc_port, irc_channel, irc_botname = [part.strip() for part in self.rhs.split(None, 3)]
irc_channel = "#%s" % irc_channel
except Exception:
except Exception:
string = "IRC bot definition '%s' is not valid." % self.rhs
self.caller.msg(string)
return
return
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
chanmatch = find_channel(self.caller, channel, silent=True)
@ -837,7 +837,7 @@ class CmdIRC2Chan(MuxCommand):
self.caller.msg("IRC connection/bot could not be removed, does it exist?")
else:
self.caller.msg("IRC connection destroyed.")
return
return
channel = find_channel(self.caller, channel)
if not channel:
@ -845,7 +845,7 @@ class CmdIRC2Chan(MuxCommand):
ok = irc.create_connection(channel, irc_network, irc_port, irc_channel, irc_botname)
if not ok:
self.caller.msg("This IRC connection already exists.")
return
return
self.caller.msg("Connection created. Starting IRC bot.")
class CmdIMC2Chan(MuxCommand):
@ -857,12 +857,12 @@ class CmdIMC2Chan(MuxCommand):
Switches:
/disconnect - this clear the imc2 connection to the channel.
/remove - "
/remove - "
/list - show all imc2<->evennia mappings
Example:
@imc2chan myimcchan = ievennia
Connect an existing evennia channel to a channel on an IMC2
network. The network contact information is defined in settings and
should already be accessed at this point. Use @imcchanlist to see
@ -901,27 +901,27 @@ class CmdIMC2Chan(MuxCommand):
self.caller.msg(string)
else:
self.caller.msg("No connections found.")
return
return
if not self.args or not self.rhs:
string = "Usage: @imc2chan[/switches] <evennia_channel> = <imc2_channel>"
self.caller.msg(string)
return
return
channel = self.lhs
imc2_channel = self.rhs
if 'disconnect' in self.switches or 'remove' in self.switches or 'delete' in self.switches:
# we don't search for channels before this since we want to clear the link
# also if the channel no longer exists.
# also if the channel no longer exists.
ok = imc2.delete_connection(channel, imc2_channel)
if not ok:
self.caller.msg("IMC2 connection could not be removed, does it exist?")
else:
self.caller.msg("IMC2 connection destroyed.")
return
return
# actually get the channel object
# actually get the channel object
channel = find_channel(self.caller, channel)
if not channel:
return
@ -929,7 +929,7 @@ class CmdIMC2Chan(MuxCommand):
ok = imc2.create_connection(channel, imc2_channel)
if not ok:
self.caller.msg("The connection %s <-> %s already exists." % (channel.key, imc2_channel))
return
return
self.caller.msg("Created connection channel %s <-> IMC channel %s." % (channel.key, imc2_channel))
@ -940,18 +940,18 @@ class CmdIMCInfo(MuxCommand):
Usage:
@imcinfo[/switches]
@imcchanlist - list imc2 channels
@imclist - list connected muds
@imclist - list connected muds
@imcwhois <playername> - whois info about a remote player
Switches for @imcinfo:
channels - as @imcchanlist (default)
games or muds - as @imclist
games or muds - as @imclist
whois - as @imcwhois (requires an additional argument)
update - force an update of all lists
Shows lists of games or channels on the IMC2 network.
"""
key = "@imcinfo"
aliases = ["@imcchanlist", "@imclist", "@imcwhois"]
locks = "cmd: serversetting(IMC2_ENABLED) and pperm(Wizards)"
@ -966,18 +966,18 @@ class CmdIMCInfo(MuxCommand):
return
if "update" in self.switches:
# update the lists
# update the lists
import time
from src.comms.imc2lib import imc2_packets as pck
from src.comms.imc2 import IMC2_MUDLIST, IMC2_CHANLIST, IMC2_CLIENT
# update connected muds
# update connected muds
IMC2_CLIENT.send_packet(pck.IMC2PacketKeepAliveRequest())
# prune inactive muds
# prune inactive muds
for name, mudinfo in IMC2_MUDLIST.mud_list.items():
if time.time() - mudinfo.last_updated > 3599:
del IMC2_MUDLIST.mud_list[name]
# update channel list
IMC2_CLIENT.send_packet(pck.IMC2PacketIceRefresh())
# update channel list
IMC2_CLIENT.send_packet(pck.IMC2PacketIceRefresh())
self.caller.msg("IMC2 lists were re-synced.")
elif "games" in self.switches or "muds" in self.switches or self.cmdstring == "@imclist":
@ -999,24 +999,24 @@ class CmdIMCInfo(MuxCommand):
cols[3].append(mud.port)
ftable = utils.format_table(cols)
for ir, row in enumerate(ftable):
if ir == 0:
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
string += "\n %i Muds found." % nmuds
self.caller.msg(string)
self.caller.msg(string)
elif "whois" in self.switches or self.cmdstring == "@imcwhois":
# find out about a player
if not self.args:
if not self.args:
self.caller.msg("Usage: @imcwhois <playername>")
return
from src.comms.imc2 import IMC2_CLIENT
self.caller.msg("Sending IMC whois request. If you receive no response, no matches were found.")
self.caller.msg("Sending IMC whois request. If you receive no response, no matches were found.")
IMC2_CLIENT.msg_imc2(None, from_obj=self.caller, packet_type="imcwhois", data={"target":self.args})
elif not self.switches or "channels" in self.switches or self.cmdstring == "@imcchanlist":
# show channels
# show channels
from src.comms.imc2 import IMC2_CHANLIST, IMC2_CLIENT
channels = IMC2_CHANLIST.get_channel_list()
@ -1028,12 +1028,12 @@ class CmdIMCInfo(MuxCommand):
nchans += 1
cols[0].append(channel.name)
cols[1].append(channel.localname)
cols[2].append(channel.owner)
cols[2].append(channel.owner)
cols[3].append(channel.level)
cols[4].append(channel.policy)
ftable = utils.format_table(cols)
for ir, row in enumerate(ftable):
if ir == 0:
if ir == 0:
string += "\n{w" + "".join(row) + "{n"
else:
string += "\n" + "".join(row)
@ -1045,19 +1045,19 @@ class CmdIMCInfo(MuxCommand):
string = "Usage: imcinfo|imcchanlist|imclist"
self.caller.msg(string)
# unclear if this is working ...
# unclear if this is working ...
class CmdIMCTell(MuxCommand):
"""
imctell - send a page to a remote IMC player
Usage:
imctell User@MUD = <msg>
imcpage "
Usage:
imctell User@MUD = <msg>
imcpage "
Sends a page to a user on a remote MUD, connected
over IMC2.
over IMC2.
"""
key = "imctell"
aliases = ["imcpage", "imc2tell", "imc2page"]
locks = "cmd: serversetting(IMC2_ENABLED)"
@ -1072,11 +1072,11 @@ class CmdIMCTell(MuxCommand):
return
from src.comms.imc2 import IMC2_CLIENT
if not self.args or not '@' in self.lhs or not self.rhs:
string = "Usage: imctell User@Mud = <msg>"
self.caller.msg(string)
return
return
target, destination = self.lhs.split("@", 1)
message = self.rhs.strip()
data = {"target":target, "destination":destination}
@ -1087,7 +1087,7 @@ class CmdIMCTell(MuxCommand):
self.caller.msg("You paged {c%s@%s{n (over IMC): '%s'." % (target, destination, message))
# RSS connection
# RSS connection
class CmdRSS2Chan(MuxCommand):
"""
@rss2chan - link evennia channel to an RSS feed
@ -1097,20 +1097,20 @@ class CmdRSS2Chan(MuxCommand):
Switches:
/disconnect - this will stop the feed and remove the connection to the channel.
/remove - "
/remove - "
/list - show all rss->evennia mappings
Example:
@rss2chan rsschan = http://code.google.com/feeds/p/evennia/updates/basic
This creates an RSS reader that connects to a given RSS feed url. Updates will be
This creates an RSS reader that connects to a given RSS feed url. Updates will be
echoed as a title and news link to the given channel. The rate of updating is set
with the RSS_UPDATE_INTERVAL variable in settings (default is every 10 minutes).
with the RSS_UPDATE_INTERVAL variable in settings (default is every 10 minutes).
When disconnecting you need to supply both the channel and url again so as to identify
the connection uniquely.
the connection uniquely.
"""
key = "@rss2chan"
locks = "cmd:serversetting(RSS_ENABLED) and pperm(Immortals)"
help_category = "Comms"
@ -1125,7 +1125,7 @@ class CmdRSS2Chan(MuxCommand):
if 'list' in self.switches:
# show all connections
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='rss_')
connections = ExternalChannelConnection.objects.filter(db_external_key__startswith='rss_')
if connections:
cols = [["Evennia-channel"], ["RSS-url"]]
for conn in connections:
@ -1141,12 +1141,12 @@ class CmdRSS2Chan(MuxCommand):
self.caller.msg(string)
else:
self.caller.msg("No connections found.")
return
return
if not self.args or not self.rhs:
string = "Usage: @rss2chan[/switches] <evennia_channel> = <rss url>"
self.caller.msg(string)
return
return
channel = self.lhs
url = self.rhs
@ -1160,8 +1160,8 @@ class CmdRSS2Chan(MuxCommand):
self.caller.msg("RSS connection/reader could not be removed, does it exist?")
else:
self.caller.msg("RSS connection destroyed.")
return
return
channel = find_channel(self.caller, channel)
if not channel:
return
@ -1171,5 +1171,5 @@ class CmdRSS2Chan(MuxCommand):
ok = rss.create_connection(channel, url, interval)
if not ok:
self.caller.msg("This RSS connection already exists.")
return
return
self.caller.msg("Connection created. Starting RSS reader.")

View file

@ -23,17 +23,17 @@ class CmdHome(MuxCommand):
home
Usage:
home
home
Teleports the player to their home.
"""
key = "home"
locks = "cmd:perm(home) or perm(Builders)"
locks = "cmd:perm(home) or perm(Builders)"
def func(self):
"Implement the command"
caller = self.caller
caller = self.caller
home = caller.home
if not home:
caller.msg("You have no home set.")
@ -47,7 +47,7 @@ class CmdLook(MuxCommand):
Usage:
look
look <obj>
look <obj>
look *<player>
Observes your location or objects in your vicinity.
@ -59,27 +59,27 @@ class CmdLook(MuxCommand):
def func(self):
"""
Handle the looking.
Handle the looking.
"""
caller = self.caller
args = self.args
args = self.args
if args:
# Use search to handle duplicate/nonexistant results.
looking_at_obj = caller.search(args, use_nicks=True)
if not looking_at_obj:
return
return
else:
looking_at_obj = caller.location
if not looking_at_obj:
caller.msg("You have no location to look at!")
return
if not hasattr(looking_at_obj, 'return_appearance'):
# this is likely due to us having a player instead
looking_at_obj = looking_at_obj.character
looking_at_obj = looking_at_obj.character
if not looking_at_obj.access(caller, "view"):
caller.msg("Could not find '%s'." % args)
return
return
# get object's appearance
caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method.
@ -93,16 +93,16 @@ class CmdPassword(MuxCommand):
@password <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
key = "@password"
"""
key = "@password"
locks = "cmd:all()"
def func(self):
"hook function."
caller = self.caller
caller = self.caller
if hasattr(caller, "player"):
caller = caller.player
caller = caller.player
if not self.rhs:
caller.msg("Usage: @password <oldpass> = <newpass>")
@ -131,16 +131,16 @@ class CmdNick(MuxCommand):
nick[/switches] <nickname> = [<string>]
alias ''
Switches:
Switches:
object - alias an object
player - alias a player
player - alias a player
clearall - clear all your aliases
list - show all defined aliases
list - show all defined aliases
If no switch is given, a command alias is created, used
to replace strings before sending the command. Give an empty
right-hand side to clear the nick
Creates a personal nick for some in-game object or
string. When you enter that string, it will be replaced
with the alternate string. The switches dictate in what
@ -148,15 +148,15 @@ class CmdNick(MuxCommand):
is None, the alias (if it exists) will be cleared.
Obs - no objects are actually changed with this command,
if you want to change the inherent aliases of an object,
use the @alias command instead.
use the @alias command instead.
"""
key = "nick"
aliases = ["nickname", "nicks", "@nick", "alias"]
locks = "cmd:all()"
locks = "cmd:all()"
def func(self):
"Create the nickname"
caller = self.caller
switches = self.switches
@ -178,20 +178,20 @@ class CmdNick(MuxCommand):
if 'clearall' in switches:
nicks.delete()
caller.msg("Cleared all aliases.")
return
return
if not self.args or not self.lhs:
caller.msg("Usage: nick[/switches] nickname = [realname]")
return
return
nick = self.lhs
real = self.rhs
real = self.rhs
if real == nick:
caller.msg("No point in setting nick same as the string to replace...")
return
return
# check so we have a suitable nick type
if not any(True for switch in switches if switch in ("object", "player", "inputline")):
switches = ["inputline"]
switches = ["inputline"]
string = ""
for switch in switches:
oldnick = Nick.objects.filter(db_obj=caller.dbobj, db_nick__iexact=nick, db_type__iexact=switch)
@ -204,14 +204,14 @@ class CmdNick(MuxCommand):
else:
string += "\nNo nick '%s' found, so it could not be removed." % nick
else:
# creating new nick
# creating new nick
if oldnick:
string += "\nNick %s changed from '%s' to '%s'." % (nick, oldnick[0].db_real, real)
else:
string += "\nNick set: '%s' = '%s'." % (nick, real)
caller.nicks.add(nick, real, nick_type=switch)
caller.nicks.add(nick, real, nick_type=switch)
caller.msg(string)
class CmdInventory(MuxCommand):
"""
inventory
@ -219,9 +219,9 @@ class CmdInventory(MuxCommand):
Usage:
inventory
inv
Shows a player's inventory.
"""
"""
key = "inventory"
aliases = ["inv", "i"]
locks = "cmd:all()"
@ -235,7 +235,7 @@ class CmdInventory(MuxCommand):
# format item list into nice collumns
cols = [[],[]]
for item in items:
cols[0].append(item.name)
cols[0].append(item.name)
desc = item.db.desc
if not desc:
desc = ""
@ -247,19 +247,19 @@ class CmdInventory(MuxCommand):
string += "\n " + "{C%s{n - %s" % (row[0], row[1])
self.caller.msg(string)
class CmdGet(MuxCommand):
class CmdGet(MuxCommand):
"""
get
Usage:
get <obj>
Picks up an object from your location and puts it in
your inventory.
"""
key = "get"
aliases = "grab"
locks = "cmd:all()"
locks = "cmd:all()"
def func(self):
"implements the command."
@ -287,13 +287,13 @@ class CmdGet(MuxCommand):
obj.move_to(caller, quiet=True)
caller.msg("You pick up %s." % obj.name)
caller.location.msg_contents("%s picks up %s." %
(caller.name,
obj.name),
caller.location.msg_contents("%s picks up %s." %
(caller.name,
obj.name),
exclude=caller)
# calling hook method
obj.at_get(caller)
class CmdDrop(MuxCommand):
"""
@ -301,14 +301,14 @@ class CmdDrop(MuxCommand):
Usage:
drop <obj>
Lets you drop an object from your inventory into the
Lets you drop an object from your inventory into the
location you are currently in.
"""
key = "drop"
locks = "cmd:all()"
def func(self):
"Implement command"
@ -318,17 +318,17 @@ class CmdDrop(MuxCommand):
return
results = caller.search(self.args, ignore_errors=True)
# we process the results ourselves since we want to sift out only
# those in our inventory.
# we process the results ourselves since we want to sift out only
# those in our inventory.
results = [obj for obj in results if obj in caller.contents]
# now we send it into the handler.
obj = AT_SEARCH_RESULT(caller, self.args, results, False)
if not obj:
return
return
obj.move_to(caller.location, quiet=True)
caller.msg("You drop %s." % (obj.name,))
caller.location.msg_contents("%s drops %s." %
caller.location.msg_contents("%s drops %s." %
(caller.name, obj.name),
exclude=caller)
# Call the object script's at_drop() method.
@ -340,33 +340,33 @@ class CmdQuit(MuxCommand):
quit
Usage:
@quit
@quit
Gracefully disconnect from the game.
"""
key = "@quit"
locks = "cmd:all()"
locks = "cmd:all()"
def func(self):
"hook function"
"hook function"
for session in self.caller.sessions:
session.msg("{RQuitting{n. Hope to see you soon again.")
session.session_disconnect()
class CmdWho(MuxCommand):
"""
who
Usage:
who
doing
who
doing
Shows who is currently online. Doing is an alias that limits info
also for those with all permissions.
"""
key = "who"
aliases = "doing"
aliases = "doing"
locks = "cmd:all()"
def func(self):
@ -386,7 +386,7 @@ class CmdWho(MuxCommand):
table = [["Player Name"], ["On for"], ["Idle"], ["Room"], ["Cmds"], ["Host"]]
else:
table = [["Player Name"], ["On for"], ["Idle"]]
for session in session_list:
if not session.logged_in:
continue
@ -396,12 +396,12 @@ class CmdWho(MuxCommand):
plr_pobject = session.get_character()
if not plr_pobject:
plr_pobject = session.get_player()
show_session_data = False
show_session_data = False
table = [["Player Name"], ["On for"], ["Idle"]]
if show_session_data:
table[0].append(plr_pobject.name[:25])
table[1].append(utils.time_format(delta_conn, 0))
table[2].append(utils.time_format(delta_cmd, 1))
table[2].append(utils.time_format(delta_cmd, 1))
table[3].append(plr_pobject.location.id)
table[4].append(session.cmd_total)
table[5].append(session.address[0])
@ -421,7 +421,7 @@ class CmdWho(MuxCommand):
else:
string += "\n" + "".join(row)
nplayers = (SESSIONS.player_count())
if nplayers == 1:
if nplayers == 1:
string += '\nOne player logged in.'
else:
string += '\n%d players logged in.' % nplayers
@ -434,14 +434,14 @@ class CmdSay(MuxCommand):
Usage:
say <message>
Talk to those in your current location.
Talk to those in your current location.
"""
key = "say"
aliases = ['"', "'"]
locks = "cmd:all()"
def func(self):
"Run the say command"
@ -458,13 +458,13 @@ class CmdSay(MuxCommand):
# Feedback for the object doing the talking.
caller.msg('You say, "%s{n"' % speech)
# Build the string to emit to neighbors.
emit_string = '{c%s{n says, "%s{n"' % (caller.name,
emit_string = '{c%s{n says, "%s{n"' % (caller.name,
speech)
caller.location.msg_contents(emit_string,
caller.location.msg_contents(emit_string,
exclude=caller)
class CmdPose(MuxCommand):
"""
@ -477,13 +477,13 @@ class CmdPose(MuxCommand):
Example:
pose is standing by the wall, smiling.
-> others will see:
Tom is standing by the wall, smiling.
Tom is standing by the wall, smiling.
Describe an script being taken. The pose text will
automatically begin with your name.
automatically begin with your name.
"""
key = "pose"
aliases = [":", "emote"]
aliases = [":", "emote"]
locks = "cmd:all()"
def parse(self):
@ -491,7 +491,7 @@ class CmdPose(MuxCommand):
Custom parse the cases where the emote
starts with some special letter, such
as 's, at which we don't want to separate
the caller's name and the emote with a
the caller's name and the emote with a
space.
"""
args = self.args
@ -500,33 +500,33 @@ class CmdPose(MuxCommand):
self.args = args
def func(self):
"Hook function"
"Hook function"
if not self.args:
msg = "Do what?"
self.caller.msg(msg)
else:
msg = "%s%s" % (self.caller.name, self.args)
self.caller.location.msg_contents(msg)
class CmdEncoding(MuxCommand):
"""
encoding - set a custom text encoding
Usage:
Usage:
@encoding/switches [<encoding>]
Switches:
clear - clear your custom encoding
This sets the text encoding for communicating with Evennia. This is mostly an issue only if
This sets the text encoding for communicating with Evennia. This is mostly an issue only if
you want to use non-ASCII characters (i.e. letters/symbols not found in English). If you see
that your characters look strange (or you get encoding errors), you should use this command
to set the server encoding to be the same used in your client program.
to set the server encoding to be the same used in your client program.
Common encodings are utf-8 (default), latin-1, ISO-8859-1 etc.
If you don't submit an encoding, the current encoding will be displayed instead.
If you don't submit an encoding, the current encoding will be displayed instead.
"""
key = "@encoding"
@ -539,7 +539,7 @@ class CmdEncoding(MuxCommand):
"""
caller = self.caller
if hasattr(caller, 'player'):
caller = caller.player
caller = caller.player
if 'clear' in self.switches:
# remove customization
@ -551,22 +551,22 @@ class CmdEncoding(MuxCommand):
del caller.db.encoding
elif not self.args:
# just list the encodings supported
pencoding = caller.db.encoding
pencoding = caller.db.encoding
string = ""
if pencoding:
string += "Default encoding: {g%s{n (change with {w@encoding <encoding>{n)" % pencoding
if pencoding:
string += "Default encoding: {g%s{n (change with {w@encoding <encoding>{n)" % pencoding
encodings = settings.ENCODINGS
if encodings:
string += "\nServer's alternative encodings (tested in this order):\n {g%s{n" % ", ".join(encodings)
string += "\nServer's alternative encodings (tested in this order):\n {g%s{n" % ", ".join(encodings)
if not string:
string = "No encodings found."
else:
# change encoding
else:
# change encoding
old_encoding = caller.db.encoding
encoding = self.args
caller.db.encoding = encoding
string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding)
caller.msg(string.strip())
caller.msg(string.strip())
class CmdAccess(MuxCommand):
"""
@ -575,7 +575,7 @@ class CmdAccess(MuxCommand):
Usage:
access
This command shows you the permission hierarchy and
This command shows you the permission hierarchy and
which permission groups you are a member of.
"""
key = "access"
@ -587,8 +587,8 @@ class CmdAccess(MuxCommand):
caller = self.caller
hierarchy_full = settings.PERMISSION_HIERARCHY
string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full)
hierarchy = [p.lower() for p in hierarchy_full]
string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full)
#hierarchy = [p.lower() for p in hierarchy_full]
if self.caller.player.is_superuser:
cperms = "<Superuser>"
@ -603,7 +603,7 @@ class CmdAccess(MuxCommand):
string += "\nPlayer {c%s{n: %s" % (caller.player.key, pperms)
caller.msg(string)
# OOC commands
# OOC commands
class CmdOOCLook(CmdLook):
"""
@ -614,8 +614,8 @@ class CmdOOCLook(CmdLook):
This is an OOC version of the look command. Since a
Player doesn't have an in-game existence, there is no
concept of location or "self". If we are controlling
a character, pass control over to normal look.
concept of location or "self". If we are controlling
a character, pass control over to normal look.
"""
@ -631,7 +631,7 @@ class CmdOOCLook(CmdLook):
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
# An object of some type is calling. Convert to player.
#print self.caller, self.caller.__class__
self.character = self.caller
self.character = self.caller
if hasattr(self.caller, "player"):
self.caller = self.caller.player
@ -642,20 +642,20 @@ class CmdOOCLook(CmdLook):
else:
self.caller = self.character # we have to put this back for normal look to work.
super(CmdOOCLook, self).func()
class CmdIC(MuxCommand):
"""
Switch control to an object
Usage:
@ic <character>
Go in-character (IC) as a given Character.
Go in-character (IC) as a given Character.
This will attempt to "become" a different object assuming you have
the right to do so. You cannot become an object that is already
controlled by another player. In principle <character> can be
any in-game object as long as you have access right to puppet it.
any in-game object as long as you have access right to puppet it.
"""
key = "@ic"
@ -671,7 +671,7 @@ class CmdIC(MuxCommand):
if utils.inherits_from(caller, "src.objects.objects.Object"):
caller = caller.player
new_character = None
new_character = None
if not self.args:
new_character = caller.db.last_puppet
if not new_character:
@ -682,19 +682,19 @@ class CmdIC(MuxCommand):
new_character = caller.search(self.args, global_search=True)
if not new_character:
# the search method handles error messages etc.
return
return
if new_character.player:
if new_character.player == caller:
caller.msg("{RYou already are {c%s{n." % new_character.name)
else:
caller.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
caller.msg("{c%s{r is already acted by another player.{n" % new_character.name)
return
if not new_character.access(caller, "puppet"):
caller.msg("{rYou may not become %s.{n" % new_character.name)
return
old_char = None
old_char = None
if caller.character:
# save the old character. We only assign this to last_puppet if swap is successful.
# save the old character. We only assign this to last_puppet if swap is successful.
old_char = caller.character
if caller.swap_character(new_character):
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
@ -704,7 +704,7 @@ class CmdIC(MuxCommand):
loc = new_character.db.prelogout_location
if not loc: # still no location; use home
loc = new_character.home
new_character.location = loc
new_character.location = loc
if new_character.location:
new_character.location.msg_contents("%s has entered the game." % new_character.key, exclude=[new_character])
new_character.location.at_object_receive(new_character, new_character.location)
@ -715,10 +715,10 @@ class CmdIC(MuxCommand):
class CmdOOC(MuxCommand):
"""
@ooc - go ooc
Usage:
@ooc
Go out-of-character (OOC).
This will leave your current character and put you in a incorporeal OOC state.
@ -730,8 +730,8 @@ class CmdOOC(MuxCommand):
help_category = "General"
def func(self):
"Implement function"
"Implement function"
caller = self.caller
if utils.inherits_from(caller, "src.objects.objects.Object"):
@ -740,18 +740,18 @@ class CmdOOC(MuxCommand):
if not caller.character:
string = "You are already OOC."
caller.msg(string)
return
return
caller.db.last_puppet = caller.character
# save location as if we were disconnecting from the game entirely.
if caller.character.location:
caller.character.location.msg_contents("%s has left the game." % caller.character.key, exclude=[caller.character])
caller.character.db.prelogout_location = caller.character.location
caller.character.location = None
# disconnect
caller.character.location = None
# disconnect
caller.character.player = None
caller.character = None
caller.character = None
caller.msg("\n{GYou go OOC.{n\n")
caller.execute_cmd("look")

View file

@ -5,11 +5,11 @@ command-help is all auto-loaded and searched from the current command
set. The normal, database-tied help system is used for collaborative
creation of other help topics such as RP help or game-world aides.
"""
from src.utils.utils import fill, dedent
from src.commands.command import Command
from src.help.models import HelpEntry
from src.utils import create
from src.utils import create
from src.commands.default.muxcommand import MuxCommand
# limit symbol import for API
@ -18,43 +18,43 @@ __all__ = ("CmdHelp", "CmdSetHelp")
LIST_ARGS = ("list", "all")
SEP = "{C" + "-"*78 + "{n"
def format_help_entry(title, help_text, aliases=None, suggested=None):
"""
This visually formats the help entry.
"""
"""
string = SEP + "\n"
if title:
string += "{CHelp topic for {w%s{n" % (title.capitalize())
if title:
string += "{CHelp topic for {w%s{n" % (title.capitalize())
if aliases:
string += " {C(aliases: {w%s{n{C){n" % (", ".join(aliases))
if help_text:
string += "\n%s" % dedent(help_text.rstrip())
if suggested:
string += "\n\n{CSuggested:{n "
string += "{w%s{n" % fill(", ".join(suggested))
string += "{w%s{n" % fill(", ".join(suggested))
string.strip()
string += "\n" + SEP
return string
string += "\n" + SEP
return string
def format_help_list(hdict_cmds, hdict_db):
"""
Output a category-ordered list. The input are the
pre-loaded help files for commands and database-helpfiles
Output a category-ordered list. The input are the
pre-loaded help files for commands and database-helpfiles
resectively.
"""
"""
string = ""
if hdict_cmds and hdict_cmds.values():
string += "\n" + SEP + "\n {CCommand help entries{n\n" + SEP
for category in sorted(hdict_cmds.keys()):
string += "\n {w%s{n:\n" % (str(category).capitalize())
string += "\n {w%s{n:\n" % (str(category).capitalize())
string += "{G" + fill(", ".join(sorted(hdict_cmds[category]))) + "{n"
if hdict_db and hdict_db.values():
string += "\n\n" + SEP + "\n\r {COther help entries{n\n" + SEP
string += "\n\n" + SEP + "\n\r {COther help entries{n\n" + SEP
for category in sorted(hdict_db.keys()):
string += "\n\r {w%s{n:\n" % (str(category).capitalize())
string += "{G" + fill(", ".join(sorted([str(topic) for topic in hdict_db[category]]))) + "{n"
return string
string += "\n\r {w%s{n:\n" % (str(category).capitalize())
string += "{G" + fill(", ".join(sorted([str(topic) for topic in hdict_db[category]]))) + "{n"
return string
class CmdHelp(Command):
"""
@ -66,15 +66,15 @@ class CmdHelp(Command):
help all
This will search for help on commands and other
topics related to the game.
topics related to the game.
"""
key = "help"
locks = "cmd:all()"
# this is a special cmdhandler flag that makes the cmdhandler also pack
# the current cmdset with the call to self.func().
return_cmdset = True
# the current cmdset with the call to self.func().
return_cmdset = True
def parse(self):
"""
input is a string containing the command or topic to match.
@ -85,7 +85,7 @@ class CmdHelp(Command):
def func(self):
"""
Run the dynamic help entry creator.
"""
"""
query, cmdset = self.args, self.cmdset
caller = self.caller
@ -95,37 +95,37 @@ class CmdHelp(Command):
# removing doublets in cmdset, caused by cmdhandler
# having to allow doublet commands to manage exits etc.
cmdset.make_unique(caller)
# Listing all help entries
if query in LIST_ARGS:
# we want to list all available help entries, grouped by category.
hdict_cmd = {}
for cmd in (cmd for cmd in cmdset if cmd.auto_help and not cmd.is_exit
for cmd in (cmd for cmd in cmdset if cmd.auto_help and not cmd.is_exit
and not cmd.key.startswith('__') and cmd.access(caller)):
try:
hdict_cmd[cmd.help_category].append(cmd.key)
except KeyError:
hdict_cmd[cmd.help_category] = [cmd.key]
hdict_cmd[cmd.help_category] = [cmd.key]
hdict_db = {}
for topic in (topic for topic in HelpEntry.objects.get_all_topics()
if topic.access(caller, 'view', default=True)):
try:
hdict_db[topic.help_category].append(topic.key)
hdict_db[topic.help_category].append(topic.key)
except KeyError:
hdict_db[topic.help_category] = [topic.key]
help_entry = format_help_list(hdict_cmd, hdict_db)
caller.msg(help_entry)
return
return
# Look for a particular help entry
# Cmd auto-help dynamic entries
# Cmd auto-help dynamic entries
cmdmatches = [cmd for cmd in cmdset if query in cmd and cmd.auto_help and cmd.access(caller)]
if len(cmdmatches) > 1:
# multiple matches. Try to limit it down to exact match
cmdmatches = [cmd for cmd in cmdmatches if cmd == query] or cmdmatches
# Help-database static entries
dbmatches = [topic for topic in
HelpEntry.objects.find_topicmatch(query, exact=False)
@ -135,7 +135,7 @@ class CmdHelp(Command):
dbmatches = [topic for topic in HelpEntry.objects.find_topicmatch(query, exact=True)
if topic.access(caller, 'view', default=True)] or dbmatches
# Handle result
# Handle result
if (not cmdmatches) and (not dbmatches):
# no normal match. Check if this is a category match instead
categ_cmdmatches = [cmd.key for cmd in cmdset if query == cmd.help_category and cmd.access(caller)]
@ -143,13 +143,13 @@ class CmdHelp(Command):
if topic.access(caller, 'view', default=True)]
cmddict = None
dbdict = None
if categ_cmdmatches:
cmddict = {query:categ_cmdmatches}
if categ_dbmatches:
dbdict = {query:categ_dbmatches}
if categ_dbmatches:
dbdict = {query:categ_dbmatches}
if cmddict or dbdict:
help_entry = format_help_list(cmddict, dbdict)
help_entry = format_help_list(cmddict, dbdict)
else:
help_entry = "No help entry found for '%s'" % self.original_args
@ -187,33 +187,33 @@ class CmdSetHelp(MuxCommand):
add - add or replace a new topic with text.
append - add text to the end of topic with a newline between.
merge - As append, but don't add a newline between the old
text and the appended text.
text and the appended text.
delete - remove help topic.
force - (used with add) create help topic also if the topic
already exists.
already exists.
Examples:
@sethelp/add throw = This throws something at ...
@sethelp/append pickpocketing,Thievery,is_thief, is_staff) = This steals ...
@sethelp/append pickpocketing, ,is_thief, is_staff) = This steals ...
"""
key = "@help"
aliases = "@sethelp"
locks = "cmd:perm(PlayerHelpers)"
help_category = "Building"
def func(self):
"Implement the function"
caller = self.caller
caller = self.caller
switches = self.switches
lhslist = self.lhslist
rhs = self.rhs
if not self.args:
caller.msg("Usage: @sethelp/[add|del|append|merge] <topic>[,category[,locks,..] = <text>]")
return
return
topicstr = ""
category = ""
@ -226,12 +226,12 @@ class CmdSetHelp(MuxCommand):
pass
if not topicstr:
caller.msg("You have to define a topic!")
return
return
string = ""
#print topicstr, category, lockstring
if switches and switches[0] in ('append', 'app','merge'):
# add text to the end of a help topic
# add text to the end of a help topic
# find the topic to append to
old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr)
if not old_entry:
@ -242,14 +242,14 @@ class CmdSetHelp(MuxCommand):
if switches[0] == 'merge':
old_entry.entrytext = "%s %s" % (entrytext, self.rhs)
string = "Added the new text right after the old one (merge)."
else:
else:
old_entry.entrytext = "%s\n\n%s" % (entrytext, self.rhs)
string = "Added the new text as a new paragraph after the old one (append)"
old_entry.save()
elif switches and switches[0] in ('delete','del'):
#delete a help entry
old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr)
old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr)
if not old_entry:
string = "Could not find topic '%s'." % topicstr
else:
@ -257,14 +257,14 @@ class CmdSetHelp(MuxCommand):
string = "Deleted the help entry '%s'." % topicstr
else:
# add a new help entry.
# add a new help entry.
force_create = ('for' in switches) or ('force' in switches)
old_entry = None
old_entry = None
try:
old_entry = HelpEntry.objects.get(key=topicstr)
except Exception:
pass
if old_entry:
if old_entry:
if force_create:
old_entry.key = topicstr
old_entry.entrytext = self.rhs
@ -277,13 +277,13 @@ class CmdSetHelp(MuxCommand):
string = "Topic '%s' already exists. Use /force to overwrite it." % topicstr
else:
# no old entry. Create a new one.
new_entry = create.create_help_entry(topicstr,
new_entry = create.create_help_entry(topicstr,
rhs, category, lockstring)
if new_entry:
string = "Topic '%s' was successfully created." % topicstr
else:
string = "Error when creating topic '%s'! Maybe it already exists?" % topicstr
string = "Error when creating topic '%s'! Maybe it already exists?" % topicstr
# give feedback
caller.msg(string)

View file

@ -4,18 +4,18 @@ System commands
These are the default commands called by the system commandhandler
when various exceptions occur. If one of these commands are not
implemented and part of the current cmdset, the engine falls back
to a default solution instead.
to a default solution instead.
Some system commands are shown in this module
as a REFERENCE only (they are not all added to Evennia's
default cmdset since they don't currently do anything differently from the
as a REFERENCE only (they are not all added to Evennia's
default cmdset since they don't currently do anything differently from the
default backup systems hard-wired in the engine).
Overloading these commands in a cmdset can be used to create
interesting effects. An example is using the NoMatch system command
to implement a line-editor where you don't have to start each
line with a command (if there is no match to a known command,
the line is just added to the editor buffer).
the line is just added to the editor buffer).
"""
from src.comms.models import Channel
@ -27,7 +27,7 @@ from src.commands.cmdhandler import CMD_NOINPUT
from src.commands.cmdhandler import CMD_NOMATCH
from src.commands.cmdhandler import CMD_MULTIMATCH
from src.commands.cmdhandler import CMD_CHANNEL
from src.commands.default.muxcommand import MuxCommand
# Command called when there is no input at line
@ -59,7 +59,7 @@ class SystemNoMatch(MuxCommand):
def func(self):
"""
This is given the failed raw string as input.
"""
"""
self.caller.msg("Huh?")
#
@ -69,13 +69,13 @@ class SystemMultimatch(MuxCommand):
"""
Multiple command matches.
The cmdhandler adds a special attribute 'matches' to this
system command.
The cmdhandler adds a special attribute 'matches' to this
system command.
matches = [(candidate, cmd) , (candidate, cmd), ...],
where candidate is an instance of src.commands.cmdparser.CommandCandidate
and cmd is an an instantiated Command object matching the candidate.
and cmd is an an instantiated Command object matching the candidate.
"""
key = CMD_MULTIMATCH
locks = "cmd:all()"
@ -84,21 +84,21 @@ class SystemMultimatch(MuxCommand):
"""
Format multiple command matches to a useful error.
This is copied directly from the default method in
src.commands.cmdhandler.
This is copied directly from the default method in
src.commands.cmdhandler.
"""
string = "There where multiple matches:"
for num, match in enumerate(matches):
for num, match in enumerate(matches):
# each match is a tuple (candidate, cmd)
candidate, cmd = match
candidate, cmd = match
is_channel = hasattr(cmd, "is_channel") and cmd.is_channel
if is_channel:
is_channel = " (channel)"
else:
is_channel = ""
is_exit = hasattr(cmd, "is_exit") and cmd.is_exit
is_exit = hasattr(cmd, "is_exit") and cmd.is_exit
if is_exit and cmd.destination:
is_exit = " (exit to %s)" % cmd.destination
else:
@ -115,7 +115,7 @@ class SystemMultimatch(MuxCommand):
id2 = ""
string += "\n %s%s%s%s%s" % (id1, candidate.cmdname, id2, is_channel, is_exit)
return string
def func(self):
"""
argument to cmd is a comma-separated string of
@ -126,37 +126,37 @@ class SystemMultimatch(MuxCommand):
# Command called when the command given at the command line
# was identified as a channel name, like there existing a
# channel named 'ooc' and the user wrote
# > ooc Hello!
# channel named 'ooc' and the user wrote
# > ooc Hello!
class SystemSendToChannel(MuxCommand):
"""
This is a special command that the cmdhandler calls
when it detects that the command given matches
an existing Channel object key (or alias).
an existing Channel object key (or alias).
"""
key = CMD_CHANNEL
locks = "cmd:all()"
def parse(self):
channelname, msg = self.args.split(':', 1)
channelname, msg = self.args.split(':', 1)
self.args = channelname.strip(), msg.strip()
def func(self):
"""
Create a new message and send it to channel, using
the already formatted input.
"""
caller = self.caller
"""
caller = self.caller
channelkey, msg = self.args
if not msg:
caller.msg("Say what?")
return
return
channel = Channel.objects.get_channel(channelkey)
if not channel:
caller.msg("Channel '%s' not found." % channelkey)
return
return
if not channel.has_connection(caller):
string = "You are not connected to channel '%s'."
caller.msg(string % channelkey)
@ -165,6 +165,6 @@ class SystemSendToChannel(MuxCommand):
string = "You are not permitted to send to channel '%s'."
caller.msg(string % channelkey)
return
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
msgobj = create.create_message(caller, msg, channels=[channel])
channel.msg(msgobj)

View file

@ -8,14 +8,12 @@ import traceback
import os, datetime, time
import django, twisted
from django.contrib.auth.models import User
from django.conf import settings
from src.server.sessionhandler import SESSIONS
from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB
from src.players.models import PlayerDB
from src.server.models import ServerConfig
from src.utils import create, logger, utils, gametime
from src.utils import logger, utils, gametime
from src.commands.default.muxcommand import MuxCommand
# limit symbol import for API
@ -40,7 +38,7 @@ class CmdReload(MuxCommand):
def func(self):
"""
Reload the system.
Reload the system.
"""
SESSIONS.announce_all(" Server restarting ...")
SESSIONS.server.shutdown(mode='reload')
@ -64,7 +62,7 @@ class CmdReset(MuxCommand):
def func(self):
"""
Reload the system.
Reload the system.
"""
SESSIONS.announce_all(" Server restarting ...")
SESSIONS.server.shutdown(mode='reset')
@ -101,7 +99,7 @@ class CmdShutdown(MuxCommand):
class CmdPy(MuxCommand):
"""
Execute a snippet of python code
Execute a snippet of python code
Usage:
@py <cmd>
@ -110,10 +108,10 @@ class CmdPy(MuxCommand):
available for convenience in order to offer access to the system
(you can import more at execution time).
Available variables in @py environment:
Available variables in @py environment:
self, me : caller
here : caller.location
ev : the evennia API
ev : the evennia API
inherits_from(obj, parent) : check object inheritance
{rNote: In the wrong hands this command is a severe security risk.
@ -160,7 +158,7 @@ class CmdPy(MuxCommand):
caller.msg(ret)
# helper function. Kept outside so it can be imported and run
# by other commands.
# by other commands.
def format_script_list(scripts):
"Takes a list of scripts and formats the output."
@ -216,7 +214,7 @@ class CmdScripts(MuxCommand):
Usage:
@scripts[/switches] [<obj or scriptid>]
Switches:
stop - stops an existing script
kill - kills a script - without running its cleanup hooks
@ -232,7 +230,7 @@ class CmdScripts(MuxCommand):
aliases = "@listscripts"
locks = "cmd:perm(listscripts) or perm(Wizards)"
help_category = "System"
def func(self):
"implement method"
@ -247,7 +245,7 @@ class CmdScripts(MuxCommand):
if not scripts:
# try to find an object instead.
objects = ObjectDB.objects.object_search(args, caller=caller, global_search=True)
if objects:
if objects:
scripts = []
for obj in objects:
# get all scripts on the object(s)
@ -267,7 +265,7 @@ class CmdScripts(MuxCommand):
string = "No scripts/objects matching '%s'. " % args
string += "Be more specific."
elif len(scripts) == 1:
# we have a unique match!
# we have a unique match!
if 'kill' in self.switches:
string = "Killing script '%s'" % scripts[0].key
scripts[0].stop(kill=True)
@ -300,8 +298,8 @@ class CmdObjects(MuxCommand):
Usage:
@objects [<nr>]
Gives statictics on objects in database as well as
a list of <nr> latest objects in database. If not
Gives statictics on objects in database as well as
a list of <nr> latest objects in database. If not
given, <nr> defaults to 10.
"""
key = "@objects"
@ -379,7 +377,7 @@ class CmdService(MuxCommand):
list - shows all available services (default)
start - activates a service
stop - stops a service
Service management system. Allows for the listing,
starting, and stopping of services. If no switches
are given, services will be listed.
@ -487,8 +485,8 @@ class CmdTime(MuxCommand):
@time
Usage:
@time
@time
Server local time.
"""
key = "@time"
@ -498,12 +496,12 @@ class CmdTime(MuxCommand):
def func(self):
"Show times."
table = [["Current server uptime:",
"Total server running time:",
"Total in-game time (realtime x %g):" % (gametime.TIMEFACTOR),
"Server time stamp:"
],
],
[utils.time_format(time.time() - SESSIONS.server.start_time, 3),
utils.time_format(gametime.runtime(format=False), 2),
utils.time_format(gametime.gametime(format=False), 2),
@ -523,13 +521,13 @@ class CmdTime(MuxCommand):
self.caller.msg(string)
class CmdServerLoad(MuxCommand):
"""
"""
server load statistics
Usage:
@serverload
Show server load statistics in a table.
Show server load statistics in a table.
"""
key = "@serverload"
locks = "cmd:perm(list) or perm(Immortals)"
@ -607,9 +605,9 @@ class CmdServerLoad(MuxCommand):
# class CmdPs(MuxCommand):
# """
# list processes
# Usage
# @ps
# @ps
# Shows the process/event table.
# """

View file

@ -182,13 +182,14 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT
new_character = create.create_player(playername, email, password,
permissions=permissions,
character_typeclass=typeclass,
character_location=default_home,
character_home=default_home)
if not new_character:
session.msg("There was an error creating the default Character/Player. This error was logged. Contact an admin.")
try:
new_character = create.create_player(playername, email, password,
permissions=permissions,
character_typeclass=typeclass,
character_location=default_home,
character_home=default_home)
except Exception, e:
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
return
new_player = new_character.player

View file

@ -323,7 +323,7 @@ class Object(TypeClass):
"""
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
return self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
def access(self, accessing_obj, access_type='read', default=False):
"""

View file

@ -58,9 +58,6 @@ def create_objects():
create_character=True,
character_typeclass=character_typeclass)
if not god_character:
raise Exception(_("#1 could not be created. Check the Player/Character typeclass for bugs."))
god_character.id = 1
god_character.db.desc = _('This is User #1.')
god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all();puppet:false()")
@ -164,7 +161,11 @@ def create_admin_media_links():
on system.
"""
import django, os
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
if django.get_version() < 1.4:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
else:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'static', 'admin')
apath = os.path.join(settings.ADMIN_MEDIA_ROOT)
if os.path.isdir(apath):
print _(" ADMIN_MEDIA_ROOT already exists. Ignored.")
@ -177,8 +178,8 @@ def create_admin_media_links():
try:
os.symlink(dpath, apath)
print _(" Admin-media symlinked to ADMIN_MEDIA_ROOT.")
except OSError:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT. If you see issues, link manually.")
except OSError, e:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT:\n %s\n -> \n %s\n (%s)\n If you see issues, link manually." % (dpath, apath, e))
else:
print _(" Admin-media files should be copied manually to ADMIN_MEDIA_ROOT.")
@ -194,7 +195,7 @@ def at_initial_setup():
return
try:
mod = __import__(modname, fromlist=[None])
except ImportError, ValueError:
except (ImportError, ValueError):
return
print _(" Running at_initial_setup() hook.")
if mod.__dict__.get("at_initial_setup", None):

View file

@ -12,6 +12,7 @@ etc.
"""
import re
from src.utils.utils import make_iter
from src.utils import logger
# variables
MSDP = chr(69)
@ -24,7 +25,7 @@ MSDP_ARRAY_CLOSE = chr(6)
regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple
regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested)
regex_varval = re.compile(r"%s(.*?)%s(.*?)[%s]" % (MSDP_VAR, MSDP_VAL, ENDING)) # return 2-tuple
regex_varval = re.compile(r"%s(.*?)%s(.*?)" % (MSDP_VAR, MSDP_VAL)) # return 2-tuple
class Msdp(object):
"""
@ -111,8 +112,8 @@ class Msdp(object):
tables[table[0]] = dict(regex_varval(table[1]))
for array in regex_array.findall(data):
arrays[array[0]] = dict(regex_varval(array[1]))
variables = dict(regex._varval(regex_array.sub("", regex_table.sub("", data))))
variables = dict(regex_varval(regex_array.sub("", regex_table.sub("", data))))
print variables
# MSDP Commands
@ -125,7 +126,7 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client
"""
if arg == 'COMMANDS':
return self.func_to_msdp(arg, MSDP_COMMANDS.keys())
return self.func_to_msdp(arg, self.MSDP_COMMANDS.keys())
elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS",
"CONFIGURABLE_VARIABLES",
@ -133,11 +134,11 @@ class Msdp(object):
elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
elif arg == 'REPORTABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys())
return self.func_to_msdp(arg, self.MSDP_REPORTABLE.keys())
elif arg == 'REPORTED_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTED.keys())
return self.func_to_msdp(arg, self.MSDP_REPORTED.keys())
elif arg == 'SENDABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_SEND.keys())
return self.func_to_msdp(arg, self.MSDP_SEND.keys())
else:
return self.func_to_msdp("LIST", arg)
@ -147,7 +148,7 @@ class Msdp(object):
reportable variable to the client.
"""
try:
MSDP_REPORTABLE[arg](report=True)
self.MSDP_REPORTABLE[arg](report=True)
except Exception:
logger.log_trace()
@ -156,16 +157,16 @@ class Msdp(object):
Unreport a previously reported variable
"""
try:
MSDP_REPORTABLE[arg](eport=False)
self.MSDP_REPORTABLE[arg](eport=False)
except Exception:
logger.log_trace()
self.logger.log_trace()
def msdp_cmd_reset(self, arg):
"""
The reset command resets a variable to its initial state.
"""
try:
MSDP_REPORTABLE[arg](reset=True)
self.MSDP_REPORTABLE[arg](reset=True)
except Exception:
logger.log_trace()
@ -177,21 +178,22 @@ class Msdp(object):
arg - this is a list of variables the client wants.
"""
ret = []
for var in makeiter(arg):
for var in make_iter(arg):
try:
ret.append(MSDP_REPORTABLE[arg](send=True))
ret.append(self.MSDP_REPORTABLE[arg](send=True))
except Exception:
logger.log_trace()
return ret
MSDP_COMMANDS = {
"LIST": self.msdp_list,
"LIST": "msdp_list",
"REPORT":"mspd_report",
"RESET":"mspd_reset",
"SEND":"mspd_send",
"UNREPORT":"mspd_unreport"
}
# MSDP_MAP is a standard suggestions for making it easy to create generic guis.
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
# is up to these commands to return data on proper form.

View file

@ -244,6 +244,7 @@ class ServerSession(Session):
else:
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
if outdata:
# we have a result, send it back
self.oob_data_out(outdata)

View file

@ -951,6 +951,7 @@ class TypedObject(SharedMemoryModel):
# If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling
# method to use e.g. self.is_typeclass() to detect that the result is not the one asked for.
_GA(self, "_display_errmsg")(errstring)
_SA(self, "typeclass_lasterrmsg", errstring)
return _GA(self, "_get_default_typeclass")(cache=False, silent=False, save=False)
#@typeclass.deleter
@ -961,6 +962,11 @@ class TypedObject(SharedMemoryModel):
# typeclass property
typeclass = property(__typeclass_get, fdel=__typeclass_del)
# the last error string will be stored here for accessing methods to access.
# It is set by _display_errmsg, which will print to log if error happens
# during server startup.
typeclass_last_errmsg = ""
def _path_import(self, path):
"""
Import a class from a python path of the
@ -973,17 +979,19 @@ class TypedObject(SharedMemoryModel):
return None
try:
modpath, class_name = path.rsplit('.', 1)
module = __import__(modpath, fromlist=[class_name])
module = __import__(modpath, fromlist=["none"])
return module.__dict__[class_name]
except ImportError:
trc = sys.exc_traceback
if not trc.tb_next:
# we separate between not finding the module, and finding a buggy one.
errstring += "(Tried path '%s')." % path
errstring = "Typeclass not found trying path '%s'." % path
else:
# a bug in the module is reported normally.
trc = traceback.format_exc()
errstring += "\n%sError importing '%s'." % (trc, path)
errstring = "\n%sError importing '%s'." % (trc, path)
except (ValueError, TypeError):
errstring = "Malformed typeclass path '%s'." % path
except KeyError:
errstring = "No class '%s' was found in module '%s'."
errstring = errstring % (class_name, modpath)
@ -997,26 +1005,32 @@ class TypedObject(SharedMemoryModel):
"""
Helper function to display error.
"""
infochan = None
cmessage = message
try:
from src.comms.models import Channel
infochan = settings.CHANNEL_MUDINFO
infochan = Channel.objects.get_channel(infochan[0])
if infochan:
cname = infochan.key
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line])
cmessage = cmessage.strip()
infochan.msg(cmessage)
else:
# no mudinfo channel is found. Log instead.
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
logger.log_errmsg(cmessage)
except Exception:
if ServerConfig.objects.conf("server_starting_mode"):
print cmessage
else:
logger.log_trace(cmessage)
if ServerConfig.objects.conf("server_starting_mode"):
print message.strip()
else:
_SA(self, "typeclass_last_errmsg", message.strip())
return
#infochan = None
#cmessage = message
#try:
# from src.comms.models import Channel
# infochan = settings.CHANNEL_MUDINFO
# infochan = Channel.objects.get_channel(infochan[0])
# if infochan:
# cname = infochan.key
# cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line])
# cmessage = cmessage.strip()
# infochan.msg(cmessage)
# else:
# # no mudinfo channel is found. Log instead.
# cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
# logger.log_errmsg(cmessage)
#except Exception:
# if ServerConfig.objects.conf("server_starting_mode"):
# print cmessage
# else:
# logger.log_trace(cmessage)
def _get_default_typeclass(self, cache=False, silent=False, save=False):
"""
@ -1064,7 +1078,9 @@ class TypedObject(SharedMemoryModel):
"""
Returns true if this object has this type
OR has a typeclass which is an subclass of
the given typeclass.
the given typeclass. This operates on the actually
loaded typeclass (this is important since a failing
typeclass may instead have its default currently loaded)
typeclass - can be a class object or the
python path to such an object to match against.
@ -1079,7 +1095,7 @@ class TypedObject(SharedMemoryModel):
pass
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in _GA(self, "_typeclass_paths")]
if exact:
current_path = _GA(self, "_cached_db_typeclass_path")
current_path = _GA(self.typeclass, "path") #"_GA(self, "_cached_db_typeclass_path")
return typeclass and any((current_path == typec for typec in typeclasses))
else:
# check parent chain
@ -1158,10 +1174,9 @@ class TypedObject(SharedMemoryModel):
self.nattr(nattr, delete=True)
else:
#print "deleting attrs ..."
self.get_all_attributes()
for attr in self.get_all_attributes():
attr.delete()
for nattr in self.ndb.all():
for nattr in self.ndb.all:
del nattr
# run hooks for this new typeclass
@ -1328,8 +1343,9 @@ class TypedObject(SharedMemoryModel):
_GA(self, 'obj').set_attribute(attrname, value)
def __delattr__(self, attrname):
_GA(self, 'obj').del_attribute(attrname)
def all(self):
def get_all(self):
return _GA(self, 'obj').get_all_attributes()
all = property(get_all)
self._db_holder = DbHolder(self)
return self._db_holder
#@db.setter
@ -1386,9 +1402,10 @@ class TypedObject(SharedMemoryModel):
except AttributeError:
class NdbHolder(object):
"Holder for storing non-persistent attributes."
def all(self):
def get_all(self):
return [val for val in self.__dict__.keys()
if not val.startswith['_']]
if not val.startswith('_')]
all = property(get_all)
def __getattribute__(self, key):
# return None if no matching attribute was found.
try:

View file

@ -13,12 +13,12 @@ shorter "create.object()".
The respective object managers hold more methods for manipulating and
searching objects already existing in the database.
Models covered:
Models covered:
Objects
Scripts
Help
Message
Channel
Channel
Players
"""
@ -26,8 +26,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.db import IntegrityError
from src.utils.idmapper.models import SharedMemoryModel
from src.utils import logger, utils, idmapper
from src.utils.utils import is_iter, has_parent, inherits_from
from src.utils import utils, logger
# limit symbol import from API
__all__ = ("create_object", "create_script", "create_help_entry", "create_message", "create_channel", "create_player")
@ -35,23 +34,28 @@ __all__ = ("create_object", "create_script", "create_help_entry", "create_messag
GA = object.__getattribute__
#
# Game Object creation
# Game Object creation
#
def create_object(typeclass, key=None, location=None,
home=None, player=None, permissions=None, locks=None,
aliases=None, destination=None):
home=None, player=None, permissions=None, locks=None,
aliases=None, destination=None, report_to=None):
"""
Create a new in-game object. Any game object is a combination
of a database object that stores data persistently to
the database, and a typeclass, which on-the-fly 'decorates'
the database object into whataver different type of object
it is supposed to be in the game.
it is supposed to be in the game.
See src.objects.managers for methods to manipulate existing objects
in the database. src.objects.objects holds the base typeclasses
and src.objects.models hold the database model.
and src.objects.models hold the database model.
report_to is an optional object for reporting errors to in string form.
If report_to is not set, errors will be raised as en Exception
containing the error message. If set, this method will return
None upon errors.
"""
# deferred import to avoid loops
from src.objects.objects import Object
@ -64,32 +68,36 @@ def create_object(typeclass, key=None, location=None,
typeclass = typeclass.typeclass.path
elif isinstance(typeclass, Object) or utils.inherits_from(typeclass, Object):
# this is already an object typeclass, extract its path
typeclass = typeclass.path
typeclass = typeclass.path
# create new database object
# create new database object
new_db_object = ObjectDB()
# assign the typeclass
# assign the typeclass
typeclass = utils.to_unicode(typeclass)
new_db_object.typeclass_path = typeclass
# the name/key is often set later in the typeclass. This
# is set here as a failsafe.
# is set here as a failsafe.
if key:
new_db_object.key = key
new_db_object.key = key
else:
new_db_object.key = "#%i" % new_db_object.id
# this will either load the typeclass or the default one
new_object = new_db_object.typeclass
if not GA(new_db_object, "is_typeclass")(typeclass, exact=True):
if not GA(new_object, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default
SharedMemoryModel.delete(new_db_object)
return None
if report_to:
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_object.key, typeclass,
GA(new_db_object, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_object, "typeclass_last_errmsg"))
# from now on we can use the typeclass object
# from now on we can use the typeclass object
# as if it was the database object.
if player:
@ -97,14 +105,14 @@ def create_object(typeclass, key=None, location=None,
new_object.player = player
player.obj = new_object
new_object.destination = destination
new_object.destination = destination
# call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom
# things on its database object.
# things on its database object.
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
new_object.at_object_creation()
# custom-given perms/locks overwrite hooks
if permissions:
new_object.permissions = permissions
@ -119,15 +127,15 @@ def create_object(typeclass, key=None, location=None,
else:
new_object.home = settings.CHARACTER_DEFAULT_HOME
if location:
new_object.move_to(location, quiet=True)
else:
# rooms would have location=None.
new_object.location = None
new_object.location = None
# post-hook setup (mainly used by Exits)
new_object.basetype_posthook_setup()
new_object.basetype_posthook_setup()
new_object.save()
return new_object
@ -136,12 +144,12 @@ def create_object(typeclass, key=None, location=None,
object = create_object
#
# Script creation
# Script creation
#
def create_script(typeclass, key=None, obj=None, locks=None,
interval=None, start_delay=None, repeats=None,
persistent=None, autostart=True):
def create_script(typeclass, key=None, obj=None, locks=None,
interval=None, start_delay=None, repeats=None,
persistent=None, autostart=True, report_to=None):
"""
Create a new script. All scripts are a combination
of a database object that communicates with the
@ -149,42 +157,47 @@ def create_script(typeclass, key=None, obj=None, locks=None,
database object into being different types of scripts.
It's behaviour is similar to the game objects except
scripts has a time component and are more limited in
scope.
scope.
Argument 'typeclass' can be either an actual
typeclass object or a python path to such an object.
Only set key here if you want a unique name for this
particular script (set it in config to give
same key to all scripts of the same type). Set obj
to tie this script to a particular object.
to tie this script to a particular object.
See src.scripts.manager for methods to manipulate existing
scripts in the database.
report_to is an obtional object to receive error messages.
If report_to is not set, an Exception with the
error will be raised. If set, this method will
return None upon errors.
"""
# deferred import to avoid loops.
from src.scripts.scripts import Script
#print "in create_script", typeclass
from src.scripts.models import ScriptDB
#print "in create_script", typeclass
from src.scripts.models import ScriptDB
if not typeclass:
typeclass = settings.BASE_SCRIPT_TYPECLASS
elif isinstance(typeclass, ScriptDB):
# this is already an scriptdb instance, extract its typeclass
typeclass = new_db_object.typeclass.path
typeclass = typeclass.typeclass.path
elif isinstance(typeclass, Script) or utils.inherits_from(typeclass, Script):
# this is already an object typeclass, extract its path
typeclass = typeclass.path
typeclass = typeclass.path
# create new database script
new_db_script = ScriptDB()
new_db_script = ScriptDB()
# assign the typeclass
# assign the typeclass
typeclass = utils.to_unicode(typeclass)
new_db_script.typeclass_path = typeclass
# the name/key is often set later in the typeclass. This
# is set here as a failsafe.
# is set here as a failsafe.
if key:
new_db_script.key = key
else:
@ -195,15 +208,19 @@ def create_script(typeclass, key=None, obj=None, locks=None,
if not GA(new_db_script, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default
print "failure:", new_db_script, typeclass
SharedMemoryModel.delete(new_db_script)
return None
if report_to:
GA(report_to, "msg")("Error creating %s (%s): %s" % (new_db_script.key, typeclass,
GA(new_db_script, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_script, "typeclass_last_errmsg"))
if obj:
try:
new_script.obj = obj
except ValueError:
new_script.obj = obj.dbobj
new_script.obj = obj.dbobj
# call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom
@ -212,10 +229,10 @@ def create_script(typeclass, key=None, obj=None, locks=None,
# custom-given variables override the hook
if key:
new_script.key = key
new_script.key = key
if locks:
new_script.locks.add(locks)
if interval != None:
if interval != None:
new_script.interval = interval
if start_delay != None:
new_script.start_delay = start_delay
@ -223,13 +240,13 @@ def create_script(typeclass, key=None, obj=None, locks=None,
new_script.repeats = repeats
if persistent != None:
new_script.persistent = persistent
# a new created script should usually be started.
if autostart:
new_script.start()
new_db_script.save()
return new_script
return new_script
#alias
script = create_script
@ -243,7 +260,7 @@ def create_help_entry(key, entrytext, category="General", locks=None):
help entries are dynamic and directly taken from the __doc__ entries
of the command. The database-stored help entries are intended for more
general help on the game, more extensive info, in-game setting information
and so on.
and so on.
"""
from src.help.models import HelpEntry
@ -255,19 +272,19 @@ def create_help_entry(key, entrytext, category="General", locks=None):
if locks:
new_help.locks.add(locks)
new_help.save()
return new_help
return new_help
except IntegrityError:
string = "Could not add help entry: key '%s' already exists." % key
logger.log_errmsg(string)
return None
except Exception:
logger.log_trace()
return None
return None
# alias
help_entry = create_help_entry
#
# Comm system methods
# Comm system methods
#
def create_message(senderobj, message, channels=None,
@ -279,7 +296,7 @@ def create_message(senderobj, message, channels=None,
senderobj - the player sending the message. This must be the actual object.
message - text with the message. Eventual headers, titles etc
should all be included in this text string. Formatting
will be retained.
will be retained.
channels - a channel or a list of channels to send to. The channels
may be actual channel objects or their unique key strings.
receivers - a player to send to, or a list of them. May be Player objects
@ -289,7 +306,7 @@ def create_message(senderobj, message, channels=None,
The Comm system is created very open-ended, so it's fully possible
to let a message both go to several channels and to several receivers
at the same time, it's up to the command definitions to limit this as
desired.
desired.
"""
from src.comms.models import Msg
from src.players.models import PlayerDB
@ -304,10 +321,10 @@ def create_message(senderobj, message, channels=None,
elif hasattr(obj, 'db_player'):
return obj.db_player
else:
return None
return None
if not message:
# we don't allow empty messages.
# we don't allow empty messages.
return
new_message = Msg()
@ -315,19 +332,19 @@ def create_message(senderobj, message, channels=None,
new_message.message = message
new_message.save()
if channels:
if not is_iter(channels):
if not utils.is_iter(channels):
channels = [channels]
new_message.channels = [channel for channel in
[to_object(channel, objtype='channel')
for channel in channels] if channel]
for channel in channels] if channel]
if receivers:
#print "Found receiver:", receivers
if not is_iter(receivers):
if not utils.is_iter(receivers):
receivers = [receivers]
#print "to_player: %s" % to_player(receivers[0])
new_message.receivers = [to_player(receiver) for receiver in
[to_object(receiver) for receiver in receivers]
if receiver]
if receiver]
if locks:
new_message.locks.add(locks)
new_message.save()
@ -343,20 +360,20 @@ def create_channel(key, aliases=None, desc=None,
specifying the receivers explicitly. Instead players may
'connect' to the channel and follow the flow of messages. By
default the channel allows access to all old messages, but
this can be turned off with the keep_log switch.
this can be turned off with the keep_log switch.
key - this must be unique.
key - this must be unique.
aliases - list of alternative (likely shorter) keynames.
locks - lock string definitions
"""
from src.comms.models import Channel
from src.comms import channelhandler
from src.comms.models import Channel
from src.comms import channelhandler
try:
new_channel = Channel()
new_channel.key = key
new_channel.key = key
if aliases:
if not is_iter(aliases):
if not utils.is_iter(aliases):
aliases = [aliases]
new_channel.aliases = ",".join([alias for alias in aliases])
new_channel.desc = desc
@ -369,33 +386,33 @@ def create_channel(key, aliases=None, desc=None,
new_channel.locks.add(locks)
new_channel.save()
channelhandler.CHANNELHANDLER.add_channel(new_channel)
return new_channel
return new_channel
channel = create_channel
channel = create_channel
#
# Player creation methods
# Player creation methods
#
def create_player(name, email, password,
user=None,
typeclass=None,
is_superuser=False,
is_superuser=False,
locks=None, permissions=None,
create_character=True, character_typeclass=None,
character_location=None, character_home=None,
player_dbobj=None):
player_dbobj=None, report_to=None):
"""
This creates a new player, handling the creation of the User
object and its associated Player object.
object and its associated Player object.
If player_dbobj is given, this player object is used instead of
If player_dbobj is given, this player object is used instead of
creating a new one. This is called by the admin interface since it
needs to create the player object in order to relate it automatically
to the user.
to the user.
If create_character is
True, a game player object with the same name as the User/Player will
also be created. Its typeclass and base properties can also be given.
@ -403,15 +420,15 @@ def create_player(name, email, password,
Returns the new game character, or the Player obj if no
character is created. For more info about the typeclass argument,
see create_objects() above.
Note: if user is supplied, it will NOT be modified (args name, email,
passw and is_superuser will be ignored). Change those properties
directly on the User instead.
Note: if user is supplied, it will NOT be modified (args name, email,
passw and is_superuser will be ignored). Change those properties
directly on the User instead.
If no permissions are given (None), the default permission group
as defined in settings.PERMISSION_PLAYER_DEFAULT will be
as defined in settings.PERMISSION_PLAYER_DEFAULT will be
assigned. If permissions are given, no automatic assignment will
occur.
occur.
Concerning is_superuser:
A superuser should have access to everything
@ -420,18 +437,18 @@ def create_player(name, email, password,
django's own creation, not this one).
Usually only the server admin should need to be superuser, all
other access levels can be handled with more fine-grained
permissions or groups.
permissions or groups.
Since superuser overrules all permissions, we don't
set any in this case.
"""
# The system should already have checked so the name/email
# isn't already registered, and that the password is ok before
# getting here.
# getting here.
from src.players.models import PlayerDB
from src.players.player import Player
if not email:
email = "dummy@dummy.com"
if user:
@ -441,7 +458,7 @@ def create_player(name, email, password,
if is_superuser:
new_user = User.objects.create_superuser(name, email, password)
else:
new_user = User.objects.create_user(name, email, password)
new_user = User.objects.create_user(name, email, password)
try:
if not typeclass:
typeclass = settings.BASE_PLAYER_TYPECLASS
@ -450,7 +467,7 @@ def create_player(name, email, password,
typeclass = typeclass.typeclass.path
elif isinstance(typeclass, Player) or utils.inherits_from(typeclass, Player):
# this is already an object typeclass, extract its path
typeclass = typeclass.path
typeclass = typeclass.path
if player_dbobj:
new_db_player = player_dbobj
@ -458,7 +475,7 @@ def create_player(name, email, password,
new_db_player = PlayerDB(db_key=name, user=new_user)
new_db_player.save()
# assign the typeclass
# assign the typeclass
typeclass = utils.to_unicode(typeclass)
new_db_player.typeclass_path = typeclass
@ -468,13 +485,18 @@ def create_player(name, email, password,
if not GA(new_db_player, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default
SharedMemoryModel.delete(new_db_player)
return None
if report_to:
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_player.key, typeclass,
GA(new_db_player, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_player, "typeclass_last_errmsg"))
new_player.basetype_setup() # setup the basic locks and cmdset
# call hook method (may override default permissions)
new_player.at_player_creation()
# custom given arguments potentially overrides the hook
# custom given arguments potentially overrides the hook
if permissions:
new_player.permissions = permissions
elif not new_player.permissions:
@ -483,19 +505,19 @@ def create_player(name, email, password,
if locks:
new_player.locks.add(locks)
# create *in-game* 'player' object
# create *in-game* 'player' object
if create_character:
if not character_typeclass:
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
# creating the object automatically links the player
# and object together by player.obj <-> obj.player
new_character = create_object(character_typeclass, key=name,
location=None, home=character_location,
location=None, home=character_location,
permissions=permissions,
player=new_player)
player=new_player, report_to=report_to)
return new_character
return new_player
except Exception,e:
except Exception:
# a failure in creating the character
if not user:
# in there was a failure we clean up everything we can
@ -507,12 +529,12 @@ def create_player(name, email, password,
try:
new_player.delete()
except Exception:
pass
pass
try:
del new_character
except Exception:
pass
raise
pass
raise
# alias
player = create_player